• ベストアンサー

ポインタについて(初心者です)

おはようございます。「独習C」を使って独学している者です。わかったような、わからないような、そんな感じでポインタの所まできたのですが、文字列定数とポインタのところで分からないところがあります。 #include <stdio.h> int main(void) { char *p; p = "ひとつ ふたつ みっつ"; printf(p); return 0; } #include <stdio.h> int main(void) { char *p; printf("文字列を入力して下さい:"); gets(p); return 0; } 上のような二つのプログラムが出てきたのですが、下の方はメモリが設定されていないので正しくないと書かれていました。しかし上のプログラムでもメモリは設定されていないように思うのですが違うのでしょうか?メモリを設定するというのは、 char ch; p = &ch; ということだと思うのですが、上のプログラムではこのような文が無くても正常に動きます。この二つのプログラムは何か違いがあるのでしょうか?そもそも根本的に考え方が間違っているのでしょうか?なにぶん初心者な者で見当違いの質問だったら申し訳有りません。よろしくお願いします。

  • aleck
  • お礼率85% (40/47)

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

  • ベストアンサー
  • rentahero
  • ベストアンサー率53% (182/342)
回答No.3

#1、#2の回答で十分かとは思いましたが、より正確な説明を書いておきます。 borland C++ compilerでアセンブラを出力した結果を見てみましょう。 最初のプログラム(printfのほう) (バイナリ初期化コードは省略) 21:_TEXT segment dword public use32 'CODE' 22:_main proc near 23:?live1@0:    ;    ; int main(void)    ; 24: push ebp 25: mov ebp,esp    ;    ; {    ; char *p;    ; p = "ひとつ ふたつ みっつ";    ; 26:@1: 27: mov eax,offset s@    ;    ; printf(p);    ; 28:?live1@32: ; EAX = p 29: push eax 30: call _printf 31: pop ecx    ;    ; return 0;    ; 32:?live1@48: ; 33: xor eax,eax    ;    ; }    ; 34:@3: 35:@2: 36: pop ebp 37: ret 38:_main endp 39:_TEXT ends 40:_DATA segment dword public use32 'DATA' 41:s@ label byte 42: db 130,208,130,198,130,194,129    ; s@+7: 43: db "@",130,211,130,189,130,194,129    ; s@+15: 44: db "@",130,221,130,193,130,194,0 45: align 4 46:_DATA ends 47:_TEXT segment dword public use32 'CODE' 48:_TEXT ends 49: public _main 50: extrn _printf:near 51: end ↑この行番号は私がつけたものです。 28行~30行で、printfが呼ばれています。その手前で、引数として渡すため、eaxがスタックにpushされています。 では、eaxにはなにが入っているのかとみてみると、 27行で、mov eax, offset s@とあります。 offset s@というのは、s@というラベルに指定されているメモリのアドレスのことです。 さて、それではs@はどこにあるのかというと、41行にありました。この後ろにある三行のdb命令文がデータです。 数えてみると、23バイトあります。確かに文字列のようです(実際には文字コードを調べると間違いなく文字列です)。 さて、これでわかるようにコンパイル時に確かにメモリが確保されています。 二つ目のプログラム(getsのほう) (バイナリ初期化コードは省略) 21:_TEXT segment dword public use32 'CODE' 22:_main proc near 23:?live1@0:    ;    ; int main(void)    ; 24: push ebp 25: mov ebp,esp    ;    ; {    ; char *p;    ; gets(p);    ; 26:?live1@16: ; EAX = p 27:@1: 28: push eax 29: call _gets 30: pop ecx    ;    ; return 0;    ; 31:?live1@32: ; 32: xor eax,eax    ;    ; }    ; 33:@3: 34:@2: 35: pop ebp 36: ret 37:_main endp 38:_TEXT ends 39: public _main 40: extrn _gets:near 41: end *説明に不要なので、printf文は削除してコンパイルしました。 一つ目のプログラムと同様に27行から30行でgetsが呼ばれています。同様にeaxが引数としてスタックにpushされています。 しかし、このeaxは、プログラムのはじめから、まったくアクセスされていません。したがって、なにが入っているかわかりません。 これが「メモリが設定されていない」ということなのです。

aleck
質問者

お礼

わかりやすい回答をありがとうございます。 やはりこの二つは似ているようで違うみたいですね。 今までメモリを設定するというのは、 char ch; p = &ch; でしか実現できないものと思っていました。一つ目ではその処理もしてしまっているということなんですね。勉強になりました。ありがとうございます。

その他の回答 (4)

回答No.5

例が悪い。本に出ていた例ならこの本は捨てて別の本を買った方が良い。。。。 ポインタ変数は、メモリ空間のある位置を指し示す。 char* p ; と宣言した時点では、ポインタ変数は初期化されておらず、不定。pの位置にアクセスは可能だが普通正しく動作しない。動作する事はあるがそれはたまたま。 ポインタ変数は、メモリ空間のある位置(住所?)を保持すると言ったが、メモリ空間はOSに管理されているため、勝手な事はできない。つまり、「使ってもいいよ!」と言われたメモリ空間しかアクセスしてはいけないのだ。現実世界でも他人の家に勝手に上がり込んだら警察に捕まるだろ。 char str[1000] ; と書けば、1000 byteの「使ってもいい」メモリ空間を確保できる。 で、 char *p ; p = &str[0] ; /* or p = str */ とすれば、char str[1000]で使っても良い事になったメモリ空間の場所をpに入れておく事ができる。 で、 p = "ひとつ ふたつ みっつ"; は、ダブルクォートで囲まれた文字列が格納できるサイズのメモリ空間を確保してくれてかつこの文字列で初期化したメモリ空間の位置がpに入る。 単に勝手に使っても良いメモリ空間が欲しかったら char* p = (char*)malloc(sizeof(char)*1000) ; とすると、サイズ分のメモリ空間を確保してその先頭の位置をpにセットする事ができる。使わなくなったら free(p) ; p = NULL ; とでもしておこう。 宿題。 メモリ空間を図示できるようにトレーニングしよう。 なれれば、頭の中にメモリ空間をイメージできるようになる。そうなれば、ポインタなんて当たり前すぎる概念にみえてくるよ。 キーワード:メモリ空間、ポインタ変数、確保、宣言、初期化・・・。 ポインタ、ポインタと言うとなんだか特殊な気分だが、ポインタは変数なんだから単なる数値(メモリ空間の位置を数値で表したもの)が入っているに過ぎない。メモリ空間は先頭から数えた数値(番地)で一次元的に位置を特定できる。 メモリ空間を使わせてもらうには、確保する。 いらなくなったら返す。 確保は、 1. char a[1000]; 2. malloc, calloc などの関数 3. char a[] = "abcdefg" ; 4. char* a = "abcdefg" ; でやる。 3. 4. 普通、値をセットするのに使ったりしない。 1., 2.を正しくりかいできればことたりる。

aleck
質問者

お礼

回答ありがとうございます。 やはり本が悪いですか…色々なサイトを見ても「独習C」はあまり評判は良くないですね… でもtikisukeman2さんの回答で少し分かったような気がします。これからも少しずつ理解を深めていこうと思います。ありがとうございました。

回答No.4

例が悪い。本に出ていた例ならこの本は捨てて別の本を買った方が良い。。。。 ポインタ変数は、メモリ空間のある位置を指し示す。 char* p ; と宣言した時点では、ポインタ変数は初期化されておらず、不定。pの位置にアクセスは可能だが普通正しく動作しない。動作する事はあるがそれはたまたま。 ポインタ変数は、メモリ空間のある位置(住所?)を保持すると言ったが、メモリ空間はOSに管理されているため、勝手な事はできない。つまり、「使ってもいいよ!」と言われたメモリ空間しかアクセスしてはいけないのだ。現実世界でも他人の家に勝手に上がり込んだら警察に捕まるだろ。 char str[1000] ; と書けば、1000 byteの「使ってもいい」メモリ空間を確保できる。 で、 char *p ; p = &str[0] ; /* or p = str */ とすれば、char str[1000]で使っても良い事になったメモリ空間の場所をpに入れておく事ができる。 で、 p = "ひとつ ふたつ みっつ"; は、ダブルクォートで囲まれた文字列が格納できるサイズのメモリ空間を確保してくれてかつこの文字列で初期化したメモリ空間の位置がpに入る。 単に勝手に使っても良いメモリ空間が欲しかったら char* p = (char*)malloc(sizeof(char)*1000) ; とすると、サイズ分のメモリ空間を確保してその先頭の位置をpにセットする事ができる。使わなくなったら free(p) ; p = NULL ; とでもしておこう。 宿題。 メモリ空間を図示できるようにトレーニングしよう。 なれれば、頭の中にメモリ空間をイメージできるようになる。そうなれば、ポインタなんて当たり前すぎる概念にみえてくるよ。 キーワード:メモリ空間、ポインタ変数、確保、宣言、初期化・・・。 ポインタ、ポインタと言うとなんだか特殊な気分だが、ポインタは変数なんだから単なる数値(メモリ空間の位置を数値で表したもの)が入っているに過ぎない。メモリ空間は先頭から数えた数値(番地)で一次元的に位置を特定できる。 メモリ空間を使わせてもらうには、確保する。 いらなくなったら返す。 確保は、 1. char a[1000]; 2. malloc, calloc などの関数 3. char a[] = "abcdefg" ; 4. char* a = "abcdefg" ; でやる。 3. 4. 普通、値をセットするのに使ったりしない。 1., 2.を正しくりかいできればことたりる。

  • ymmasayan
  • ベストアンサー率30% (2593/8599)
回答No.2

> p = "ひとつ ふたつ みっつ"; これを見てCコンパイラは2つのことをします。 1.定数エリアに11文字相当分の場所をとって「ひとつ ふたつ みっつ」を入れます。   ・・・コンパイル時に実行。 2.「ひとつ ふたつ みっつ」の先頭アドレスをpに入れる命令コードを作成。   ・・・実行時に実行。 もう一方ではこの手順が踏まれていません。

aleck
質問者

お礼

回答ありがとうございます。 p = "ひとつ ふたつ みっつ" この1行にそんな意味があるとは思いもしませんでした。ありがとうございました。

回答No.1

> char *p; > p = "ひとつ ふたつ みっつ"; "ひとつ ふたつ みっつ"という文字列がメモリ上のどこかに収められていて、pにはその先頭が設定されます。 > char *p; > gets(p); ポインタpはどこだかわかんない不定な場所を指しています。getsは入力された文字列を'どこだかわかんない不定な場所'に格納します……ヤバいです。

aleck
質問者

お礼

回答ありがとうございます。 やはり上記の二つは違うことをやっているようですね。ありがとうございました。

関連するQ&A

  • ポインタについて

    今初めてポインタというものを勉強しております。 よろしくお願いします。 ◎1---------------------------------- #include<stdio.h> int main(void) { int mydt=1234; int *pt; pt=&mydt; printf("*pt=%d\n",*pt); printf("&mydt=%p\n",&mydt); return 0; } --------------------------------------- ◎1のようにmydtのアドレスをポインタptに代入すれば、このプログラムは正常に動きました。 ◎2----------------------------------- #include<stdio.h> int main(void) { int mydt=1234; int *pt=&mydt; printf("*pt=%d\n",*pt); printf("&mydt=%p\n",&mydt); return 0; } ---------------------------------------- ◎2で「int *pt=&mydt;」があまりどういう意味かはわかりませんが、これも正常に動きました。 ◎3------------------------------------ #include<stdio.h> int main(void) { int mydt=1234; int *pt; *pt=&mydt printf("*pt=%d\n",*pt); printf("&mydt=%p\n",&mydt); return 0; } -------------------------------------- ◎3のように◎2と違って「*pt=&mydt」の代入を後から行うと、「'=' : 'int *__w64 ' から 'int' に変換できません。」といったようなエラーが起きてしまいます。 ◎1と◎2の違い、後何故◎3はダメなのかがわかりません。 教えていただけると嬉しいです。 後補足として、配列とポインタについてですが、 ◎4------------------------------ char ss[10]="ABCDE"; char *ssp=ss; --------------------------------- ◎5---------------------------- char ss[10]="ABCDE"; char *ssp; ssp=ss; -------------------------------- ◎4と◎5も同じような事だとは思いますが違いを教えていただけると嬉しいです。 よろしくお願いします。

  • ポインタ

    #include<stdio.h> int main() { char *p; p="ポインタ"; printf(p); return 0; } なぜ p="ポインタ"; とできるのですか? 普通は  int *i, j; i=&j; *i=100; こんな感じでやるのでは?

  • ポインタ定義は必要なんですか?

    #include <stdio.h> int main( void ) { int x; int *p; p = &x; printf("Address = %p\t\n", p ); return 0; } を #include <stdio.h> int main( void ) { int x; int p;←ここを上とはかえた p = &x; printf("Address = %p\t\n", p ); return 0; } にしたところコンパイル時に p = &x に関して 移植性のないポインタ変換と表記されますが 結果は同じだったのでポインタ定義というのは結局必要なんですか? 移植性のないポインタ変換とはなんなんですか? よければ詳しく教えてください

  • char型ポインタ

    よくプログラムで charポインタだけ指定して、 #include<stdio.h> int main(){ char* p; p = "abcdef"; printf("%s",p); return 0; } のようにしているのをみかけますが、 メモリーを確保していなくても問題ないのでしょうか? char* p; p = (char*)malloc(7); strcpy(p,"abcdef"); としたのと同じでしょうか?

  • ポインタいついて教えてください

    ポインタがわかりません。 教えてください。 下の二つは、共に「100」を表記すると思いますが、 どこがどのように違うのですか。 また、f1()という関数をつくって、ここで scanfを使って、5つぐらい値を代入させて、 他の関数でこの値を使おうと思っています。 この場合下のどちらを使うのが、よろしいのでしょうか。 よろしくお願いします #include <stdio.h> int main(void) { int *p, q; q = 100; /* q に100を代入 */ p = &q; /* p にq のアドレスを割り当てる */ printf("%d", *p); return 0; } #include <stdio.h> int main(void) { int *p, q; p = &q; /* q のアドレスを得る */ *p = 100; /* ポインタを使ってq に値を代入する */ printf("%d", q); return 0; }

  • ポインタ スペースを数えるプログラム

    入力した文章のスペースを数えるプログラムを作ってみました. ポインタをまだしっかり理解していないのですが,ポインタを使用 する場合,以下のようなプログラムで正しいですか? (オーバーフローなどについては対処していません.) #include<stdio.h> int main(void) { char str[80],*p; int i,count=0; gets(str); p=str; for(i=0;i<80;i++) { if(p[i]==' ') { count++; } } printf("%d",count); return 0; }

  • ポインタ配列について

    /************配列 change1.c ***********/ #include <stdio.h> void change(char *c) { c[0]='a'; c[1]='b'; c[2]='c'; } int main(int agrc, char **agrv) { change(*++agrv); printf("%s",*agrv); return(0); } /**********ポインタ change2.c ***********/ #include <stdio.h> void change(char *c) { c="abc"; } int main(int agrc, char **agrv) { change(*++agrv); printf("%s",*agrv); return(0); } ********************* この2つのプログラムchange1.c,change2.cにおいて、次のようにしたとき結果がこうなります \change1.exe 123 abc \change2.exe 123 123 change2.c において、ポインタでの文字の格納 c="abc"; は何故実行されないのですか?

  • C言語のポインタ

    あまり意識せずにポインタを使っているせいか,次のプログラムではまってしまいました. #include<stdio.h> #include<stdlib.h> int main(void) {  int *p, q;  p = (int *)malloc(sizeof(int));  q = (int *)malloc(sizeof(int));  *p = 2;  printf("%d\n", *p);  return 0; } コンパイルエラーで実行ファイルが出力されません. このプログラムで変数qはなぜポインタじゃないのでしょうか? 次にtypedefでptr_intという型を定義したプログラムは, 上のようなエラーが出力されず,期待とおりの結果になりました. #include<stdio.h> #include<stdlib.h> typedef int* ptr_int; int main(void) {  ptr_int p, q;  p = (int *)malloc(sizeof(int));  q = (int *)malloc(sizeof(int));  *p = 2;  *q = 3;  printf("%d\n", *p);  printf("%d\n", *q); return 0; } typedefすることでなぜエラーを回避することができるのでしょうか? よろしくおねがいします.

  • void型ポインタについて

    -------------------------------- #include<stdio.h> void uni_disp(void *p,int typ); int main() { int idt=123456; double ddt=56.789; char ss[]="abcdef"; uni_disp(&idt,'I'); uni_disp(&ddt,'D'); uni_disp(ss,'S'); uni_disp("STRING",'S'); return 0; } void uni_disp(void *p,int typ) { if(typ=='I'){ printf("%d\n",*(int *)p); } else if(typ=='D'){ printf("%f\n",*(double *)p); } else if(typ=='S'){ printf("%s\n",(char *)p); } } ----------------------------------- 以上のプログラム等で、void型ポインタをint型ポインタ、double型ポインタとみなす場合の、「*(int *)p」や「*(double *)p」の表記がどういう仕組みになっているか分かりません。 「*(int)p」などはエラーが出るのですが、やはり表記の意味を理解していないため何故か分かりません。 「*(int *)p」などの表記を分解して教えていただけると嬉しいです。

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

    ◎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にはアドレスを代入するという認識かあるのですが、文字列定数を代入しても問題ないのか?という疑問があります。 教えていただけたら嬉しいです。

専門家に質問してみよう