• ベストアンサー

文字列、ポインタを使用したプログラムで分からないところがあります

1 #include <stdio.h> 2 3 void setstr(char *str){ 4   str = "abc"; 5   return; 6 } 7 8 int main(){ 9 10   char *str; 11 12   setstr(str); 13 14   str[1] = 'E'; 15 16   printf("str = %s\n", str); 17 18   return 0; 19 } 20 上記のプログラムの動きがいまいち理解できません。 (メモリの状態など) 16行目でprintfすると、結果は「str = 、E・」となります。 ---まず、4行目でabcに対してメモリが確保されて、その先頭アドレスが strに設定されます。 しかし、setstr関数を抜けた時点で、先ほど確保されたメモリは開放されて しまう。(? ここは想像です。確証がありません) main関数に戻ってきて、14行目で変更しているメモリは、abcがかつてあった 場所の"b"の部分。(str自体は何も変更されていないから) 16行目でprintfしているのだけど、なぜこの結果になるのかが分かりません。。 分かる方いましたら教えて下さい。上の文章では何を言っているのか分かりづらいとは 思いますが。。 説明には適宜行番号を使って頂いて構いません。 よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • osamuy
  • ベストアンサー率42% (1231/2878)
回答No.4

デバッガgdbを使って、実際にどのアドレスを指すかを実験(後述)してみると、main()で定義している変数str($2で指示されるアドレス)と、setstr()の仮引数str($4で指示されるアドレス)は別物であることがわかります。 これは、cパラメータ受け渡し方がcall by valueである事によるものです。 なので、setstr()で文字列のアドレスを設定したと思っていても、main()のstrには影響してません(setstr()から戻った直後の$7の値をみると明らか)。 こんな風にデバッガで追っかけたり、アセンブラソース出力(gcc -Sとかcl.exe /FAとかで生成)を眺めてみると、感じがつかめると思います。 (gdb) list 1 void setstr( char *str ){ 2 str = "abc"; 3 return; 4 } 5 int main(){ 6 char *str; 7 setstr( str ); 8 str[1] = 'E'; 9 return 0; 10 } (gdb) b 7 Breakpoint 1 at 0x401ada: file a.c, line 7. (gdb) run Starting program: a.out Breakpoint 1, main () at a.c:7 7 setstr( str ); (gdb) p str $1 = 0x0 (gdb) p &str $2 = (char **) 0x81ff88 (gdb) step setstr (str=0x0) at a.c:2 2 str = "abc"; (gdb) p str $3 = 0x0 (gdb) p &str $4 = (char **) 0x81ff70 (gdb) n 4 } (gdb) p str $5 = 0x405048 "abc" (gdb) p &str $6 = (char **) 0x81ff70 (gdb) n main () at a.c:8 8 str[1] = 'E'; (gdb) p str $7 = 0x0 (gdb) p &str $8 = (char **) 0x81ff88 (gdb) p &str[1] $9 = 0x1 <Address 0x1 out of bounds> (gdb) n Program received signal SIGSEGV, Segmentation fault. 0x00401aec in main () at a.c:8 8 str[1] = 'E'; (gdb) quit %

upanepa
質問者

お礼

ご回答ありがとうございます。 回答者さんの環境では、セグメンテーションフォールトが おきるのですね。私の環境(BCC)では、おきませんでした。 これは置いておいて・・・ 指摘された内容を見て、値渡しがダメなので参照渡しにして みたら期待した動作になりました。 void setstr(char *str){ ↓ void setstr(char *&str){ よく考えてみたら、4行目で、str自身を書き換えてますね。。 動作するようにはなりましたが、setstr内で登場した"abc"の部分の メモリの扱いはどのようなものなるのかが謎です。 mainに制御が戻った後で、自然にこの部分が上書きされて しまわないのかとか。。mallocで領域を確保したわけじゃないしなあ、とか。。 そもそもこのプログラムの書き方はおかしいのかな。。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (12)

  • andy_kun
  • ベストアンサー率23% (64/274)
回答No.2

プログラムの意図はわかりますが、ポインタをリテラルのアドレスで初期化することは出来ません。 結局は、ANo1さんの言う通り14行目でメモリの内容を破壊しています。

全文を見る
すると、全ての回答が全文表示されます。
  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.1

ひとまず setstr() の実装は忘れて、main の中だけ考えましょう。 char 型のポインタ変数 str を宣言していますが、str の指す先は存在していません。 setstr() は char型のポインタ変数を受け取りますが、それがどのような実装であろうと、 存在しないものを変更することはできません。 そして、str[1] = 'E'; で「存在しないはずのどこか」を破壊しています。 printf() ではその破壊された様子が見えているのです。

upanepa
質問者

お礼

ご回答ありがとうございます。 strを宣言した時点では、NULLで初期化もしていないので、 何か不定値が入っているんですね、きっと、、 そしてその不定値をアドレスとして見て、そこらへん(不定値+1)の メモリを書き換えてしまっているということですね。とてもシステムに とって危険な香りがします。。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • mainから渡した文字列を関数内で書き換え

    非常に基礎的な質問で申し訳ないのですが mainから渡した文字列を関数内で書き換えることができません。 int型の整数やchar型一文字はできるのですが。。。 例えば以下のようなソースでmainのABCをDEFに書き換えたいとき どのようにすればいいのでしょうか。 (関数の戻り値で変更という方法以外で) 以下のソースでは値は書き換わりませんでした。 void func(char *str2) { str2 = "DEF"; } int main() { char str1[20] = "ABC" printf("%s", str1); //ABC func(str1); printf("%s", str1); //DEFになるようにしたい }

  • ポインタで分からないことがあります。

    つい最近C言語の勉強を始め、現在ポインタの勉強をしています。 過去の質問を検索したり、サイトを見てみましたが、一人の力では解決できませんでしたので質問させていただきます。 ポインタのプログラムで、下記のプログラムについて分からないことがありました。 ――――――――――――――――――――――――――――――――― #include <stdio.h> int main (void) { char *str = "abc"; printf ("%s %d %d\n", str, &str, &(*str)); str = "日本語"; printf ("%s %d %d\n", str, &str, &(*str)); return 0; } ――――――――――――――――――――――――――――――――― このプログラムで、「char *str = "abc";」の部分でstrには abcのアドレスが入っていると思っていたのですが、 1度目の「printf ("%s %d %d\n", str, &str, &(*str));」で、 結果が「abc 1245064 4235560」となっているのを見ると 私の見解は間違っている気がします。 「char *str = "abc";」の部分では一体なにが行われているのでしょうか? また、このプログラムをコンパイルして実行した結果が、 abc 1245064 4235560 日本語 1245064 4235574 となったのですが、なぜstrのアドレスは同じなのに、 &(*str)のアドレスは異なるのでしょうか? 質問をまとめますと、以下の2つです。 1.「char *str = "abc";」の部分では一体なにが行われているのでしょうか? 2.「abc」と「日本語」のstrのアドレスは同じなのに、&(*str)のアドレスは異なるのでしょうか? 初心者ですので言葉の足らない部分があるかもしれませんが、ご教授のほどよろしくおねがいします。

  • abcが、入力された文字列内にあるかどうかを表示するプログラム

    文字列strの中にabcが含まれていれば、1を返し、含まれていなければ0を返すプログラムが分かりません。 C言語の問題で下記のものが分かりません。どなたか知恵を貸してください。 ユーザが文字を入力し、CTRL+Zが押されるまで、半角英数字の入力(最大でも1000文字)を受け付ける。文字列「abc」が、入力された文字列内にあるかどうかを表示するプログラムを作成する。ユーザが入力した文字列が3文字未満はabcがありませんと表示させる。 そのプログラム内で以下の関数を完成させる。 int str_srch_abc(char str []) 文字列strの中にabcが含まれていれば、1を返し、含まれていなければ0を返す関数とする。 (例えばabcは連続でabcの時だけ1を返し、asbscなどはoを返します。) ちなみに自分なりにやってみたのですが、ここまでしかできませんでした。 #include<stdio.h> int main() { int str_srch_abc(char str []); char str[1000]; int ch=0, j=0; printf("半角英数字を入力してください"); scanf("%s",str); while((ch=getchar())!=EOF){ str[j]=ch; j++; } str[j]='\0'; printf("%s",str); return(0); }

  • 文字列の扱い方

    初歩的な質問ですみません… str文字列からcという文字を見つけたら添字を返すという関数を作ったのですが、 iにこの関数を代入して、if文の制御式にiを使って比較するまでは正常なのですが、 真文にiを使うと何故か偽文(という言い方でいいのでしょうか…この場合("そんな値はありません。"というところです)が実行されてしまいます。 よろしければご教授お願い致します。 #include <stdio.h> int str_char(const char str[],int c) { int len = strlen(str); int i; for (i = 0;i < len;i++) { if (str[i] == c) return i; } return -1; } int main() { char str[64] = "Fucking Brutal Death Metal"; int ch,i; printf("どの文字を調べますか?"); scanf("%c",&ch); i = str_char(str,ch); if (i >= 0) printf("その文字は%d番目にあります。",str_char(str,ch) + 1); //何故かiだと動かない else printf("そんな値はありません。"); return 0; }

  • プログラミング ポインタを使った文字列比較

    プログラミング ポインタを使った文字列比較 2つの文字列str1, str2を入力し,それらが等しければ0,等しくなければ1を返す関数str_compareを作り、返り値によって以下のように表示するプログラムを作れ。ただし,関数strcmpを使ってはならない。 文字列の入出力はmain関数で行い,関数str_compareの仮引数にはポインタ変数を宣言し,ポインタと間接演算子*を用いた処理を行うこと。 % ./a.out input str1 = Worldcup input str2 = Worldcup same strings % ./a.out input str1 = World input str2 = cup different strings この問題に私は次のようにプログラミングしました。 #include <stdio.h> #define MAX 100 int str_compare(char *, char *); main() { char str1[MAX], str2[MAX]; printf("input str1 = %s", str1); scanf("%s", str1); printf("input str2 = %s", str2); scanf("%s", str2); str_compare(str1, str2); if (str_compare(str1, str2) == 0) printf("same strings\n"); else if (str_compare(str1, str2) == 1) printf("different strings\n"); } int str_compare(char *s1, char *s2) { int i; for (i = 0; s1[i] != '\0'; i++) { if (s1[i] != s2[i]) { break; } } if (s1[i] == s2[i]) { return 0; } else { return 1; } } これで実行したところ、「input str1 =」の右のスペースが文字化け?してしまいます。(半角カタカナや記号が出る)ただ、その後に文字列を入力すると、正しく機能します。 これは何が悪いなのでしょうか、どなたか教えてください。

  • 文字列中に含まれる文字の個数をカウントするプログラムについて…

    文字列、1文字が与えられたとき、これをポインタで入力し文字列中に含まれる文字の個数を計算するプログラムを作成せよ。 と、いう課題がだされたんですけど、ユーザが任意の文字列と1文字を入力できるようにすることができません…。 多分main関数の部分をちょっといじくればよいと思うのですが…。 どなたかアドバイスをお願いします。 #include <stdio.h> int count(const char *str, const char ch) { int cnt=0; while (*str!='\0') { if (*str==ch) cnt++; str++; } return cnt; } int main() { const char *str="hello,world!"; const ch='o'; int cnt; cnt=count(str, ch); printf("%s中に%cは%d個です\n", str, ch, cnt); return 0; }

  • 文字列とポインタの問題です。

    #include<stdio.h> int f(char *s); int main(void){ char*str="nasida Institute of Technology"; int i; i=f(str); printf("%s:%d\n",str,i); return 0; } int f(char *s) { int j=0; while(*s!='\0'){ if(*s=='t'){ j++; } s++; } return j; } このプログラムの答えが3になるんですが、if文のとこの動作がよく分からないので、よろしくお願いします。

  • 配列やポインタに文字列を設定することについて

    ◎1------------------------- #include<stdio.h> int main(void) { char ss[80]; scanf("%s",ss); printf("%s\n",ss); return 0; } ---------------------------- ◎2--------------------------- #include<stdio.h> int main(void) { char *ss="abcde"; printf("%s\n",ss); return 0; } ------------------------- ◎3---------------------- #include<stdio.h> int main(void) { char *ss; ss="abcde"; printf("%s\n",ss); return 0; } ------------------------- 以上3つプログラムで疑問をいだいたのですが、 まず◎1で、これは例えば、 cahr ss[80]="abc"; のように配列ssに文字列"abc"そのものを入れているのか、 char *ss="xyz"; のようにまず"xyz"という文字列をメモリ上のどこかに設定し、その先頭番地をssに代入しているのか、どちらの考えでいいのかわかりません。 次に、◎2、3ではどちらも正常に実行できたのですが、特に◎3で「ss="abcde";」と記述していますが、ssにはアドレスを代入するという認識かあるのですが、文字列定数を代入しても問題ないのか?という疑問があります。 教えていただけたら嬉しいです。

  • ポインタによる文字列の出力

    こんにちは、お世話になっています。 この参考書のプログラムの例題のソースを実行しようとしたのですが、 Visual Studioを使っているのですが、警告がなぜか出てしまいます。 プログラム力もそんなにないので何が原因か解りません。 ご教示お願いします。 #include<iostream> using namespace std; int main() { char* str = "Hello"; ←ここでエラー cout << str << '\n'; return 0; } 重大度レベル コード 説明 プロジェクト ファイル 行 抑制状態 エラー (アクティブ) E0144 型 "const char *" の値を使用して型 "char *" のエンティティを初期化することはできません

  • 文字列ポインタとgets関数の関係について。

    以下のプログラムはコンパイルは出来ますが、 実行するとクラッシュしてしまいます。 gets関数は char *gets( char *str ); と定義されているので文字列の先頭アドレスを返すはずですが 何故このプログラムはエラーが出るのでしょうか・・。 #include <stdio.h> int main ( void ){  char *p, *s;  p = gets(s);  printf("%s", p);  return 0; }