• ベストアンサー

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

つい最近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)のアドレスは異なるのでしょうか? 初心者ですので言葉の足らない部分があるかもしれませんが、ご教授のほどよろしくおねがいします。

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

  • ベストアンサー
  • nerosuke
  • ベストアンサー率33% (39/115)
回答No.5

どうもnerosukeです。 "日本語"は漢字で2バイトですので、解りやすい1バイトの"abc"で説明します。概念は2バイトでも同じですのであしからず。 >*str は「日本語 = アドレス」という扱いになってしまうのではないかと思うのですが 正確には "日本語"の先頭アドレスです。  "abc"で説明しますので、 char *str = "abc"; は"abc"の先頭アドレスを現します。 str == 4235560 ですよね。 で 4235560のアドレスの中身はなんでしょうか? 答えは 'a'です。 試しに出力。 printf ("%d\n", *str); //97はアスキーコードでaを表す。 printf ("%c\n", *str); //そのままaが表示 4235560 = a 4235561 = b 4235562 = c と値がメモリにあってstrには"abc"の先頭が入っているのですから、 str == 4235560 です。 *str はそのアドレスの中身の値ですから、'a'です。 &(*str)はそのアドレスの中身の値のアドレスですから、4235560です。 即ち、 str == &(*str)となるわけです。 わかりましたか? ちなみに配列はポインタそのものですので、 この辺は配列も意識しましょう。 str == str *str == str[0] &(*str) == &str[0] 配列としてこのように書いても同じ結果です。 わかりますか?

sachi-999
質問者

お礼

「char *str = "abc";」を書いた後に、「str = ○△□」を代入すると、 先に書いた「char *str = "abc";」の宣言によって、 str には ○△□ の文字ではなく ○ のアドレスが入ることになる ということですね? str に "日本語" を代入しても文字ではなくそのアドレスが入るということでようやくスッキリしました!!これで str == str *str == str[0] &(*str) == &str[0] も分かりました!! 私の何回もの質問に丁寧に答えていただき本当にありがとうございました。 良回答20点10点両方差し上げたいくらいです!! 本当にありがとうございました。またCの勉強を頑張っていきます。 機会がありましたら、また質問させていただきます。 そのときはよろしくお願いします。では。。。

その他の回答 (4)

  • nerosuke
  • ベストアンサー率33% (39/115)
回答No.4

>反対にstr のアドレスが同じなのはなぜなのでしょうか? それはchar *strはchar型のポインタ変数ですよね? &strはその変数のアドレスだからです。 "abc"も"日本語"も同じstrという変数に値を代入しています。 当然同じ変数なのでアドレスはかわりません strのアドレス == 1245064 "abc"という文字列が格納されたアドレス(先頭のアドレス) == 4235560 "日本語"という文字列が格納されたアドレス(先頭のアドレス) == 4235574 まあ 単純にこんな感じです。 変数はstrに値を入れ替えているだけなのでstrのアドレスは変わりません。 例えば str =1; str = 2; 違う値が入っても変数のアドレスは変わらないでしょ?

sachi-999
質問者

お礼

やっと理解できました!!ありがとうございます!! アドレスが「同じ」「異なる」については本当にスッキリしました!! あと、問題なのは、"日本語"の代入時です。。。 * はアドレスが指すメモリに保存されている値を表示するものですよね? str には 日本語 が代入されていてるので、 *str は「日本語 = アドレス」という扱いになってしまうのではないかと思うのですが、これでどうして &(*str) は表示できるのでしょうか? いったい * によって何を参照しているのでしょうか? 何度も質問してしまってすみません。。。

  • nerosuke
  • ベストアンサー率33% (39/115)
回答No.3

これはメモリの図を書けば一発で解るのですが、この解答欄でそれも むずかしいの「文字列 ポインタ」のキーワードで検索してみると わかりやすい図が出てくるかもしれませんね。 >1.「char *str = "abc";」の部分では一体なにが行われているのでしょうか? これはstr(charのポインタ変数)にabcの文字列が格納されたアドレスを入れています。 abc 1245064 4235560 この4235560というアドレスにabcの文字列がaから順に入っています。 なので、例えば char *abc_Adr = 4235560; //そのままアドレスの値を入れる(実際はこの値は可変) printf ("%s \n", abc_Adr); この値をそのまま入れてもabcが入った先頭が代入されいるので、 abcと出力されます。 すなわち、4235560=a,4235561=b,4235562=c とメモリ上に置かれているのです。 *str="abc" は 先頭の4235560を代入しているのです。 >2.「abc」と「日本語」のstrのアドレスは同じなのに、&(*str)のアドレスは異なるのでしょうか? 上に書いたことが理解できたら、もうお分かりかと思いますが、 "abc"同様 "日本語"というデータだメモリに置かれた先頭のアドレスを入れていますので、"abc"の先頭とはことなるのです。

sachi-999
質問者

お礼

>char *abc_Adr = 4235560; //そのままアドレスの値を入れる(実際はこの値は可変) printf ("%s \n", abc_Adr); この値をそのまま入れてもabcが入った先頭が代入されいるので、 abcと出力されます。 printf ("%s \n", abc_Adr); ならば結果は「abc」、 printf ("%d \n", abc_Adr); ならば結果は「4235560」になるということですよね? 少し理解できました!! >"abc"同様 "日本語"というデータだメモリに置かれた先頭のアドレスを入れていますので、"abc"の先頭とはことなるのです。 これは理解できました!初歩的なことを聞いて申し訳ありませんが、反対にstr のアドレスが同じなのはなぜなのでしょうか?

  • semona
  • ベストアンサー率20% (1/5)
回答No.2

>1.「char *str = "abc";」の部分では一体なにが行われているのでしょうか? strの値に"abc"のアドレスを設定しています。 ここでいうstrとはアドレスを値として保持する変数であるため "abc"のアドレス格納と言う事になります >2.「abc」と「日本語」のstrのアドレスは同じなのに、&(*str)のアドレスは異なるのでしょうか? "abc"と"日本語"というのはそれ単独で定数と言われる変数なので コンパイル時にプログラムの中の相対アドレスが設定されます。 (*str)とは変数strがさす値(ポインタですね)ですから 1回目は"abc"のアドレス、2回目は"日本語"のアドレス が格納されています。 &(*str)とはポインタが差している変数のアドレスですから "abc"と"日本語"では異なる事になります。 ちょっと回答が分かりづらいかもしれないので追加します。 |---------|--------------|----------| |変数名 |変数のアドレス| 値 | |---------|--------------|----------| |str |0x00000001 | ??? | |"abc" |0x00000002 | abc | |"日本語" |0x00000003 | 日本語 | |---------|--------------|----------| str="abc"はstrの値に「0x00000002」を設定する事です。 (*str)は「abc」ですが&(*str)は「0x00000002」ですね。 分かりましたかね? 何か説明が下手で申し訳ないです

sachi-999
質問者

お礼

char *str = "abc"; では、str はアドレスが入っていて、 str = "日本語"; では、str は"日本語"という文字が入っている という考えでイイのでしょうか? しかし、これだと、日本語を代入したあとの &(*str) は"日本語"をもとに変数を求めるて、その変数のアドレスをというというよくわからない状態になってしまうので頭がパニックになってしまいます。 どなたか、この部分を詳しく説明していただけますでしょうか? よろしくおねがいします。

回答No.1

1.「char *str = "abc";」の部分では一体なにが行われているのでしょうか? > abcのアドレスが入っていると思っていたのですが、 これで合っています。 2.「abc」と「日本語」のstrのアドレスは同じなのに、&(*str)のアドレスは異なるのでしょうか? > str = "日本語"; この部分で書き換えたからです。 printf()で表示している3種、  str  &str  &(*str) このうち str と &(*str) は同じ意味です。 *str とは簡単に言えば「strのアドレスの先頭の文字」のことであり、 それに  & が付くので「strのアドレスの先頭の文字のアドレス」となります。 ( printf ("[%s %d] [%d] [%s %d]\n", str, str, &str, &(*str), &(*str)); を表示してみてください。) &str は「str変数のアドレス」の事で、これは変動しません。 そして > str = "日本語"; この処理を行なった際に、str の中身が「"abc"の先頭アドレス」から 「"日本語"の先頭アドレス」に書き換わりました。 よって str と &(*str) が変更しました。 中身が変更されても str のアドレスは変わりませんので、 &str はそのままです。

sachi-999
質問者

お礼

回答ありがとうございます。 >> abcのアドレスが入っていると思っていたのですが、 >これで合っています。 自分が間違っていると思ったので安心しました。 >str と &(*str) は同じ意味です。 これがよく分からないのですが、 * はアドレスが指すメモリに保存されている値を表示するものではないのですか? str には 日本語 が代入されていてるので、 日本語 = アドレス?という状態になってしまうのではないかと・・・。 *によって何を参照しているのでしょうか? 物分りが悪くてすみません。。。

関連するQ&A

  • ポインタ

    質問なのですが、このソースのchar *str_copy(char *d, const char *s)関数内のchar *p=d;はなんで、*pにdを入れるか分かりません。それと、このdは、*dなのですか?どうして、while (*d++ = *s++) みたいに*dをつけないんですか?教えてください。宜しくお願いします。 #include <stdio.h> char *str_copy(char *d, const char *s) { char *p=d; while (*d++ = *s++) ; return (p); } int main(void) { char tmp[100]; char st1[100], st2[100],st3[100]; printf("文字列を入力してください:"); scanf("%s",tmp); str_copy(st1,str_copy(st2,tmp)); printf("文字列st1は%sです。\n", st1); printf("文字列st2は%sです。\n", st2); printf("文字列st3は%sです。\n", str_copy(st3,tmp)); return (0); }

  • ポインタ?

    #include <stdio.h> main() { char s[] = "I love cat and dog."; char c = 'a'; char *p = s; int n = 0; printf("\"%s\"の中から\'%c\'をさがします。\n", s, c); while(*p != '\0') { if(*p == c) { printf("%d番目で発見しました。\n", p - s + 1); ++n; } ++p; } if(n == 0) { printf("1つも見つかりませんでした。\n"); } else printf("全部で%d個見つかりました。\n", n); return 0; } わからないので質問したいのですが、これは「Cの絵本」という、 本に出ているサンプルプログラムです。 わからないところは、 12行目の、printf("%d番目で発見しました。\n", p - s + 1); ところです。その、「p - s」のところが特にわかりません。 ポインタって言うのは、アドレスを格納する変数ですよね? その、pからsを引いても0になるんじゃないかと思って、理解が できません。どうして、このp-sで、cの位置が発見できるのかが 理解できません。最後の+1は配列が0から始まるんで+1にすれば いいのはわかるんですが、p-sでどんなことが起きているかが 理解できなくて。ポインタをちゃんと理解できていないから、 こういった疑問が出てくるんですかね? ほかの参考書も本屋さんに行って見てみようと思っているんですが、 どなたか教えていただけませんか? よろしくお願いします。

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

    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しているのだけど、なぜこの結果になるのかが分かりません。。 分かる方いましたら教えて下さい。上の文章では何を言っているのか分かりづらいとは 思いますが。。 説明には適宜行番号を使って頂いて構いません。 よろしくお願いします。

  • ポインタの問題です

    学校の課題である文字列をポインタを使って逆順に表示するというプログラムを作ったのですが、「問題で引数で与えられた文字列を逆順とする関数を作成せよ.ただし文字数が最大256文字であると仮定して良い.」ということなんですが、課題が求めている答えと自分が作ったプログラムがあっているか確認お願いします。 #include <stdio.h> void reverseWord(char *str); void main(void) { char *str = "sapporo"; char *p, *q; printf("元: %s\n", str); p = q = str; while (*q != '\0') q++; printf("逆順:"); while (q >= p) putchar(*q--); printf("\n"); } よろしくお願いします。

  • ポインタについて

    #include<stdio.h> int main(void) { char str[10]; char *ptr = str; printf("文字列を入力してください。\n"); scanf("%s",ptr); printf("文字列は%sです。",str); return 0; } 上記のプログラムのscanf("%s",ptr);の ptrに&をつけるとなぜ先頭の4文字は入力しても 表示されなくなってしまうのでしょうか? よろしくお願いします。

  • ポインタに ~0を入れること

    見かけたCのプログラムで、 ポインタに~0を代入するものを見ました。 そのプログラムをそのまま載せるのはわかりにくいので、 代わりに以下のプログラムを作って実行しました。 #include <stdio.h> int main(void) { char *pa[3]; int i; pa[0]=0; pa[1]=~0; pa[2]="Hello"; printf("sizeof(char*)=%d\n", sizeof(char*)); for(i=0; i<=2; i++) { if(pa[i]==NULL) printf("pa[%d] はNULLです。\n", i); if(pa[i]==(char*)0xFFFFFFFF) printf("pa[%d]は全ビット1です。\n", i); if(pa[i]==~0) printf("pa[%d]は~0です。\n", i); } return 0; } 結果 sizeof(char*)=4 pa[0] はNULLです。 pa[1]は全ビット1です。 pa[1]は~0です。 このプログラムはコンパイル時にエラーも警告も出ず、 動作も意図したとおりです。 pa[1]に入っている ~0 は、int型の定数なのでしょうか。 それならば、 pa[1]=~0; という代入や if(pa[i]==~0) という比較は 左辺はchar*型で右辺はconst int型であって型が異なりますが、 問題ないのでしょうか。 ~0は0の否定なので、全ビットは1なのでしょうけど、 int型(の定数)だと思います。 ~0というのは何か特別な値なのでしょうか。 ポインタに~0を入れるというのは、意味があるのでしょうか。 (例えば、「ポインタに0を入れるということは、ヌルポインタであって、ポインタとして無効なんですよ」のようなこと。)

  • ポインタ配列

    ポインタ配列によるひとつのプログラムを組もうと思っています。 で、以下のようなプログラムを作ってみました。 1:#include<stdio.h> 2:#define NUM 5 3:main(void){ 4: char *str[NUM]; 5: int i; 6: for(i=0;i<NUM;i++){ 7: printf("string --->"); 8: scanf("%s",str[i]); 9: } 10: for(i=0;i<NUM;i++){ 11: printf("str[%d] --> %c\n",i,str[i]); 12: } 13:} これなのですが、8行目のscanf文でコンパイルエラーではなく、実行エラーが出ます。どのようにすれば動くようになるのでしょうか? 入力する文字は、9文字以下を想定しています。

  • C言語のポインタにの動作について

       以下のコードで、DelHalfSpc() において引数 str は str++ でカウントされ、ループを抜けた時点では文字列の末尾を指しているはずですが、なぜ呼び出し側の main()の char str[] には正しい値が入るのでしょうか? char *DelHalfSpc(char *str) {   //char *dmy = str; → char * という型の変数 dmy の宣言   char *dmy, *Res;   Res = dmy = str; // Res は常に先頭アドレスを指す   while (*str != '\0') {     if ( *str != ' ' ) {       *dmy = *str;       dmy++;     }     str++;   }   //ループを抜けたこの時点で str は文字列の末尾を指しているはず   *dmy = '\0';   return Res;   //ここを return str とするとヌル文字列を返す } int main(void) {   char str[] = " 1 23 4 5";   printf("空白削除前:\"%s\"\n", str);   printf("空白削除後:\"%s\"\n", DelHalfSpc(str));   printf("str も変化:\"%s\"\n", str); //なぜ正しい結果が表示されるのか?   return 0; } 【結果】 ----------------------- 空白削除前:" 1 23 4 5" 空白削除後:"12345" str も変化:"12345"

  • 2次元配列とポインタ配列の違い

    2次元配列とポインタ配列の違いを比較するプログラムを作成したつもりなのですが、下のプログラムの printf("we[%d]=%u\n",i,&we[i][0]); という文がどうやら違うらしいのですが、全く分かりません。 なので、なんとなくでも構わないので分かる方がいらっしゃったらお願いします。 #include<stdio.h> int main(void) { int i; char *we[]={"Monday","Tuesday","Wednesday"}; char ek[][10]={"Monday","Tuesday","Wednesday"}; printf("2次元配列で格納したアドレス\n"); for(i=0;i<3;i++){ printf("ek[%d]=%u\n",i,&ek[i]); } printf("ポインタ配列のアドレス\n"); for(i=0;i<3;i++){ printf("we[%d]=%u\n",i,&we[i]); } printf("ポインタ配列で格納したアドレス\n"); for(i=0;i<3;i++){ printf("we[%d]=%u\n",i,&we[i][0]); } printf("ポインタ配列で格納した文字列\n"); for(i=0;i<3;i++){ printf("we[%d]=\%s\n",i,we[i]); } printf("2次元配列で格納した文字列\n"); for(i=0;i<3;i++){ printf("ek[%d]=\%s\n",i,ek[i]); } return(0); }

  • ポインタ変数のサイズ

     いろいろC言語のことを知りたくて、次のソースを作って動かしてみました。 ★ソース(□はタブ) ◆◆◆◆◆ #include <stdio.h> #include <stdlib.h> int main(void) { □char *s1; □int *s2; □ □s1 = (char *)malloc(1000); □s2 = (int *)malloc(1000); □ □printf("sizeof s1 = %d\n", sizeof s1); □printf("sizeof s2 = %d\n", sizeof s2); □ □printf("sizeof *s1 = %d\n", sizeof *s1); □printf("sizeof *s2 = %d\n", sizeof *s2); □ □printf("s1 = %d\n", s1); □printf("s2 = %d\n", s2); □ □printf("*s1 = %d\n", *s1); □printf("*s2 = %d\n", *s2); □ □return EXIT_SUCCESS; } ◆◆◆◆◆ ★実行結果1 ◆◆◆◆◆ sizeof s1 = 4 sizeof s2 = 4 sizeof *s1 = 1 sizeof *s2 = 4 s1 = 1323000 s2 = 1324008 *s1 = -60 *s2 = 1310916 ◆◆◆◆◆ ★実行結果2 ◆◆◆◆◆ sizeof s1 = 4 sizeof s2 = 4 sizeof *s1 = 1 sizeof *s2 = 4 s1 = 11087864 s2 = 11088872 *s1 = -60 *s2 = 11075780 ◆◆◆◆◆ ★実行結果3 ◆◆◆◆◆ sizeof s1 = 4 sizeof s2 = 4 sizeof *s1 = 1 sizeof *s2 = 4 s1 = 1519608 s2 = 1520616 *s1 = -60 *s2 = 1507524 ◆◆◆◆◆  OSはWindows Vista、コンパイラはMS Visual Studio 2010 コマンドプロンプトです。次の疑問についてご教授頂きたく、お願い致します。 (1)“sizeof s1”、“sizeof s2”の値が共に4となるのはなぜか。  char型へのポインタ、int型へのポインタとして宣言したs1、s2のサイズが同じ4になる理由が、どうしても分かりません。 (動かす前の予想は、でたらめな値になるかと思っていましたが) (2)“s1”、“s2”、“*s2”の値が毎回でたらめな値になったのに対して、“*s1”の値が毎回“-60”になったのはなぜか。  ポインタでつまずいており、いろいろ実験して体で理解したいと思っています。どうぞよしくお願い致します。