- ベストアンサー
ポインタについて(初心者です)
おはようございます。「独習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; ということだと思うのですが、上のプログラムではこのような文が無くても正常に動きます。この二つのプログラムは何か違いがあるのでしょうか?そもそも根本的に考え方が間違っているのでしょうか?なにぶん初心者な者で見当違いの質問だったら申し訳有りません。よろしくお願いします。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
#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は、プログラムのはじめから、まったくアクセスされていません。したがって、なにが入っているかわかりません。 これが「メモリが設定されていない」ということなのです。
その他の回答 (4)
- tikisukeman2
- ベストアンサー率32% (32/99)
例が悪い。本に出ていた例ならこの本は捨てて別の本を買った方が良い。。。。 ポインタ変数は、メモリ空間のある位置を指し示す。 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.を正しくりかいできればことたりる。
お礼
回答ありがとうございます。 やはり本が悪いですか…色々なサイトを見ても「独習C」はあまり評判は良くないですね… でもtikisukeman2さんの回答で少し分かったような気がします。これからも少しずつ理解を深めていこうと思います。ありがとうございました。
- tikisukeman2
- ベストアンサー率32% (32/99)
例が悪い。本に出ていた例ならこの本は捨てて別の本を買った方が良い。。。。 ポインタ変数は、メモリ空間のある位置を指し示す。 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)
> p = "ひとつ ふたつ みっつ"; これを見てCコンパイラは2つのことをします。 1.定数エリアに11文字相当分の場所をとって「ひとつ ふたつ みっつ」を入れます。 ・・・コンパイル時に実行。 2.「ひとつ ふたつ みっつ」の先頭アドレスをpに入れる命令コードを作成。 ・・・実行時に実行。 もう一方ではこの手順が踏まれていません。
お礼
回答ありがとうございます。 p = "ひとつ ふたつ みっつ" この1行にそんな意味があるとは思いもしませんでした。ありがとうございました。
- επιστημη(@episteme)
- ベストアンサー率46% (546/1184)
> char *p; > p = "ひとつ ふたつ みっつ"; "ひとつ ふたつ みっつ"という文字列がメモリ上のどこかに収められていて、pにはその先頭が設定されます。 > char *p; > gets(p); ポインタpはどこだかわかんない不定な場所を指しています。getsは入力された文字列を'どこだかわかんない不定な場所'に格納します……ヤバいです。
お礼
回答ありがとうございます。 やはり上記の二つは違うことをやっているようですね。ありがとうございました。
お礼
わかりやすい回答をありがとうございます。 やはりこの二つは似ているようで違うみたいですね。 今までメモリを設定するというのは、 char ch; p = &ch; でしか実現できないものと思っていました。一つ目ではその処理もしてしまっているということなんですね。勉強になりました。ありがとうございます。