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

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

ただいまの
回答率

88.77%

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

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 7,936

ikuo-biyori

score 56

いつもお世話になっています。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);  






}

     

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • raccy

    2016/11/19 23:55

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

    キャンセル

回答 3

checkベストアンサー

+2

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/24 08:19

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

    キャンセル

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/20 00: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);
    }
    で良い?

    キャンセル

  • 2016/11/20 02:39

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

    キャンセル

  • 2016/11/20 02:45

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

    キャンセル

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);
   }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る