質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

13958閲覧

ポインタを使って文字列挿入したい。

ikuo-biyori

総合スコア56

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2016/11/19 13:13

編集2016/11/19 17:36

いつもお世話になっています。c言語初心者で勉強中です。課題で、文字列のn文字目の後ろに、標準入力から入力した単語を挿入時に文字列の長さが1023文字を超えても動作しつづけるプログラムを作成したいんです。いろいろ考えたんですが、流れがうまくつかめず、苦戦しています。nのアドレスを知って、配列の中身を1つずらそうとおもったんですが、p=&nでは変数の型が違くてアドレスを得られませんでした。どうやればnのアドレスを得られるのか教えて下さい。また流れがいまいちわからないので、流れもよろしければ教えてください。
実行したいこと
./a.out
[Wakamatsu]
0 Atui
[Atui][Wakamatsu]
11 young
[Atui][Waka[young]matsu]
23 pine
[Atui][Waka[young]matsu[pine]]
以下がコードです。```C言語
コード

#include <stdio.h> #include <string.h> #include <stdlib.h> #define BUFSIZE 1024 int main(){ char init[]="[Wakamatsu]"; /* 初期データ用文字列 */ char *str,*buf,*mou; int n; char input[BUFSIZE]; /* 入力用文字列バッファ */ /* その他の変数を宣言 */ int kazu,mozi; char *p; /* strの初期化を記述する*/ str=init; /* strlen,malloc,strcpyを使いstrにinitの内容のコピーを保持させる */ kazu=strlen(init);/*文字列をコピーするからinit[i]みたいに文字として表記しない。*/printf("kazu=%d\n",kazu); str=(char*)malloc(kazu*sizeof(char)); strcpy(str,init);/*文字列をコピーするからinit[i]みたいに文字として表記しない。*/ while (1) { printf( "%s\n", str ); if( scanf("%d %s", &n, input) != 2 ) break; /* * 処理の内容を記述する */ mozi=strlen(input); buf=(char*)malloc(kazu+2+mozi*sizeof(char)); mou=(char*)malloc(mozi+2*sizeof(char)); // mou[0]='['; // mou[mozi+2]=']'; strncpy(mou,input,mozi+2); mou[0]='['; mou[mozi+2]=']'; printf( "%s\n", mou); }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raccy

2016/11/19 14:55

ソースコードがソースコードとして書かれていないため正確に表現できていません。編集画面でソースコード全体を選択し、メニューの「<code>」ボタンを押してください
guest

回答3

0

ベストアンサー

Cの文字列は指定の場所に指定の文字列を挿入なんてことはできません。できることは

  • 文字列に文字列をコピーする。strcpy strncpy
  • 文字列の後ろに文字列を追加する(結合する)。strcat strncat
  • 文字列を文字列に置き換える。memcpy memmove
  • 文字列を特定の文字で埋める。memset
  • 文字列の特定の場所を特定の文字に置き換える。左辺に[]演算子を用いた=演算子

などです。これらの道具を駆使し、mallocやreallocでメモリ領域を確保しながら行う必要があります。方法は二つです。
※ 以下の説明で「文字列の長さ」とはstrlenで取得する'\0'を含めない文字の数です。配列の長さではないことに注意してください。

###毎回新たな領域を確保する。

  1. 挿入位置と挿入文字列を取得する。scanf
  2. 現在文字列の長さと挿入文字列の長さから新文字列の長さを求める。strlen
    挿入文字列に含まれない"[""]"分も入れること。
  3. 2.で求めた長さに基づき、メモリを確保する。malloc or calloc
    終端の'\0'分が必要なため、確保するメモリは新文字列の長さ+1にすること。
  4. 新文字列に現在文字列の挿入位置の前までの長さ分コピーする。strncpy
  5. strncpyは最後に'\0'を挿入しない場合があるため、挿入位置の所を'\0'にする。[]=
  6. "["、挿入文字列、"]"現在文字列の挿入位置の後ろを順番に新文字列に追加する。strcat
    現在文字列の挿入位置の後ろは現在文字列からnだけポインタを進めた先である。
    つまり、現在文字列 + nで表現できる。
  7. 現在文字列をメモリ解放する。free
    ここでメモリ解放を行わないとメモリリークする
  8. 現在文字列を新文字列に置き換える。=
    どういうことかというと、単純に現在文字列 = 新文字列として、ポインタの値を代入すると言うことです。

str〜系はヌル終端文字列であることが前提です。しかし、その結果は関数によってヌル終端文字列であったり、なかったりします。常に最後に'\0'になるように意識してください。freeし忘れさえなければ、単純だと思います。

###領域を拡張して、ずらして入れる。

  1. 挿入位置と挿入文字列を取得する。scanf
  2. 現在文字列の長さと挿入文字列の長さから必要になる現在文字列の長さを求める。strlen
    挿入文字列に含まれない"[""]"分も入れること。
  3. 2.で求めた長さに基づき、現在文字列のメモリを拡張する。realloc
    終端の'\0'分が必要なため、確保するメモリは新文字列の長さ+1にすること。
  4. 現在文字列の挿入位置の前から終わりまで('\0'含む)挿入後になる場所へずらしてコピーする。memmove
    コピー元とコピー先が重なる場合があるため、memcpyではなくmemmoveを使用する必要がある。
    挿入後の位置は挿入文字列だけではなく、"[""]"分も入れること。
    イメージ的にはこんな感じにする。(実際の'-'部分は何かしら値があるが、後から上書きする)
    "abcdefghi\0-----" ==(n=2 で "XYZ")==> "ab-----cdefghi\0"
    '\0'も含めて移動しておくと、後から'\0'で閉める必要が無い。
  5. 現在文字列の挿入位置に'['を入れる。[]=
  6. 現在文字列の挿入位置から一つ進んだ先に挿入文字列をコピーする。memcpy
    strcpyでは終端の\0が挿入され、移動した元々の文字列が上書きされるため、ここでは使えない。
  7. 現在文字列の挿入文字列が終わった次に']'を入れる。[]=

freeし忘れもなく、安全そうに見えますが、mem〜系は文字列であることを前提にしないため、しっかり自分で長さを計算しながら使う必要があります。最後に、文字列が'\0'で終わっているかどうかも注意するようにしてください。

###その他の注意事項

  • 文字列の領域を確保する場合は、常に末端の'\0'を意識してください。
    確保されたcharの配列の領域のサイズは、strlenで取得できるそこに入る文字列の長さより、必ず1以上大きくなければなりません。同じサイズでは入らず、場合によってはエラーや深刻なバグを引き起こします。
  • "%s"で長さを指定しないscanfはバッファオーバーランを防げないため、バグや脆弱性の原因になります。
    "%1023s"にするなど、必ず長さを指定してください
    [迷信] scanf ではバッファオーバーランを防げない | 株式会社きじねこ
    scanf自体が危険なのではなく、使い方が危険ということです。
    長さを指定する場合も、'\0'分が必要になりますので、必ず確保した領域の長さ-1にしてください。
  • C99以降(1999年に出たC言語の仕様です)、C言語では//という一行コメントが使えます。
    /* */を無理に使う必要はありません。
  • C99以降、変数の宣言は関数やブロックの先頭以外でも可能です。
    無理に関数の先頭で全ての変数を宣言する必要はありません。
  • 適切なインデントはコードを読みやすくし、理解を深めます。
    ブロックは適時インデントを行うなど、コーディングスタイルを採用してください。
    手動であわせるのが大変であれば、お使いのIDEやclang-format等のツールで整形することをお勧めします。
  • 本来、mallocreallocを使う場合は、メモリ確保に失敗した場合の処理も必要です。
  • malloccallocでメモリ領域を確保した場合は、メモリの解放('free')をどこでするかを意識してください。
    今回のようにすぐにmainが終わってしまう場合でも、終わる前にfreeでメモリ解放を行うことをお勧めします。
    これは「mallocfreeはワンセットで考える癖を付けるため」と「もし、mainを別の単独関数にして、複数回呼ばれるようにしても、ほぼそのまま対応できるようにするため」です。

投稿2016/11/20 00:55

raccy

総合スコア21733

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ikuo-biyori

2016/11/23 23:19

丁寧な解説ありがとうございます。すごく参考になりました。
guest

0

reallocを使って作成しましたサンプルです。

#include <stdio.h> #include <string.h> #include <stdlib.h> #define BUFSIZE 1024 void shift_c(char *p, int n, int moji){ int i,t; t=strlen(p); for(i=t+1; i>=n; i--){ *(p+i+moji)=*(p+i); } } int main(){ char *str,*str2,*buf,*mou; str = (char *) malloc(BUFSIZE); if( str == NULL ) { printf( "メモリ確保エラー\n" ); return 3; } strcpy(str,"[Wakamatsu]"); /* 初期データ用文字列 */ int n; char input[BUFSIZE]="["; /* 入力用文字列バッファ */ char *pl="]"; /* その他の変数を宣言 */ int kazu,mozi; int bsize = BUFSIZE; /* strの初期化を記述する*/ /* strlen,malloc,strcpyを使いstrにinitの内容のコピーを保持させる */ kazu=strlen(str);/*文字列をコピーするからinit[i]みたいに文字として表記しない。*/ printf("kazu=%d\n",kazu); // strcpy(str,init);/*文字列をコピーするからinit[i]みたいに文字として表記しない。*/ printf( "%s\n", str); while (1) { if( scanf("%d %s", &n, input+1) != 2 ) break; if( kazu < n ){ printf( "入力値エラー\n" ); return 3; } /* 処理の内容を記述する */ strcat(input,pl); mozi=strlen(input); if (bsize < kazu + mozi){ str2 = ( char * )realloc( str, bsize+BUFSIZE ); if( str2 == NULL ) { printf( "メモリ確保エラー\n" ); return 3; } if(str2 != str){ memcpy(str2,str,bsize); free(str); str=str2; } bsize+=BUFSIZE; } shift_c(str, n, mozi); memcpy(str+n,input,mozi); printf( "%s\n", str); kazu+=mozi; printf("kazu=%d\n",kazu); } }

投稿2016/11/19 17:55

編集2016/11/20 01:02
A.Ichi

総合スコア4070

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

どこで’p=&n’ってやってます?それと'char *p'と'int n'ではアドレスを渡すにしても型が違うので。
それと気が付いたのが’mou[mozi+2]=']'; ’はいいけど、最後に'\0'にしないと文字列の最後が分かりませんよ。

投稿2016/11/19 13:30

MasahikoHirata

総合スコア3747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ikuo-biyori

2016/11/19 13:37

エラーになったので消してました。ヌル文字を入れなきゃいけないんですね、分かりました。すみません後、このプログラムの流れであっているか教えてください。
MasahikoHirata

2016/11/19 13:47

’if(scanf("%d %s", &n, input) != 2 ) 'ってscanfから何を返り値で得たいの?が知りたい部分と'n'は結局どこで使うの?入力として何を求めていますか?
MasahikoHirata

2016/11/19 15:11

もやもやしてしまうので、修正点など。strlenでは文字の長さだけで'\0'は含まないので'malloc'では、それを考慮して1byte多めに確保。 ’str=(char*)malloc((kazu+1)*sizeof(char)); ' '!=2'の意味が分からないが、取りあえず’2’を入力したら'break'させるとして 'while(1) { printf("%s\n",str); scanf("%d %s",&n,input); if( n == 2 ) break; mozi=strlen(input); // buf=(char*)malloc(kazu+2+mozi*sizeof(char)); //未使用 mou=(char*)malloc((mozi+2+kazu+1)*sizeof(char)); // '['と']'+'\0'分を含めて。 mou[0] = '['; strcpy(( mou+1),str); strcpy((mou+1+kazu),input); mou[kazu+mozi+2] = ']'; mou[kazu+mozi+3] = '\0'; printf("%s\n",mou); } で良い?
ikuo-biyori

2016/11/19 17:39

if( scanf("%d %s", &n, input) != 2 ) break; は、数字のnと文字列を入力しなくなったらブレイクするという意味です。
ikuo-biyori

2016/11/19 17:45

nというのは、文字を挿入する場所をきめるもので、そこにinputの文字列を'['と']'を付け加えた形で表示するというものです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問