- ベストアンサー
アドレスとポインタがどうしても理解できない
C言語を独学しているのですが、どの参考書読んでも、アドレスとポインタの理解ができません。アドレスとポインタを使わなくても別に開発できるのではないかと思います。どなたか、アドレスとポインタを初心者でも分かるように分かりやすく教えて頂けないでしょうか?
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
私は具体的なアドレス、アドレスに格納された値の例を上げてデバッガなどを使用して説明しています。 #1さんの例で行きますと、 01:int add_sub(int a,int b,int *ans2) 02:{ 03: *ans2 = a - b; 04: return a + b; 05:} 06: 07:main() 08:{ 09: int a,b,add_ans,sub_ans; 10: a = 1; 11: b = 2; 12: add_ans = add_sub(a,b,&sub_ans); 13:} を実行すると、 1) 10行目の頭の所で、a,b,add_ans,sub_ansの4つの変数宣言が終わり、 a=-858993460(=0xcccccccc、初期値) b=-858993460 add_ans=-858993460 sub_ans=-858993460 の値が格納されています。 また、各変数が確保された(スタック上の)メモリアドレスは、 &a=0x0012ff7c &b=0x0012ff78 &add_ans=0x0012ff74 &sub_ans=0x0012ff70 となっており、int型(4バイト)が12ff70番地から宣言したのと逆順に確保された事が分かります。 2) 12行目の頭の所で、a,bの内容を見ると、値が代入された事が確認できます。 また、add_sub関数に渡される引数は、 add_sub(1, 2, 0x0012ff70) となっている事が確認できます。 3) 関数を呼び出して、3行目の頭の所で、 a=1 b=2 と値が渡されますが、 &a=0x0012ff18 &b=0x0012ff1c と、main()とadd_sub()で同じ変数名のa,bでも別々の場所に値が確保されている事に注意してください。 このおかげで、add_sub()の中でa,bの値を変更しても、main()のa,bには影響を与えない事が分かります。 そして、 ans2=0x0012ff70 と、main関数のsub_ansの変数が格納されていた"アドレス"が入っていることが確認できます。 従って、 *ans2=-858993460 と言う風に、add_sub()の外側の呼び出し元のmain()での値の確認、変更を可能にしています。 4) 4行目の頭の所で、a-bの計算結果を*ans2に代入が終わりました。 先ほどの、-858993460が新たに計算された-1で置き換えられた事が確認できます。 5) main()に戻って、13行目の頭の所では、add_ans,sub_ansそれぞれに値が格納されている事が確認できます。 更に、実際のメモリのダンプ内容も併せると理解が進みやすいです。 以上、Win2000、VC++6.0の環境での話です。 -- > アドレスとポインタを使わなくても別に開発できるのではないかと思います。 テレビや洗濯機が無くても生活できるのと…違うか。
その他の回答 (4)
- hitomura
- ベストアンサー率48% (325/664)
>アドレスとポインタを使わなくても別に開発できるのではないかと思います。 という疑問に対して、回答します。 極論をいえば、確かにそのとおりです。 ただし、システムの資源および時間が無尽蔵に使用でき、後のメンテナンスを考えなくていいならば、という条件がつきます。 ポインタを使わなくても、グローバル変数を使用することによって複数の値のやり取りはできます。 また、文字列処理もその長さがどのくらいになるか分からなければ何MB何GBといった十分に大きな領域を用意すれば良いだけの話です――理論的には。 しかし実際には、グローバル変数の使用はメンテナンスする際どこがその値を修正しどこで利用されているのか非常に分かりづらくなりますし、十分に大きな領域を用意するといってもそんなに巨大な領域を用意することはできません。 複数の値の参照・修正地点を明確にしたり、必要な時に必要な領域を確保するときにポインタが有効なのです。 あと、巨大な構造体を引数にする関数の場合、構造体のコピーを避けるためにポインタを使用します。 ポインタがある理由としてはこんなところです。
- yatokesa
- ベストアンサー率40% (201/496)
ポインタと言っても、ちょっと特殊なだけの普通の変数です。intやlongなどと大差有りません。単なる4バイト(アドレスが格納できる幅)の領域です。普通に足し算や引き算ができる変数です。#本当は普通じゃないですが... で、* を変数の頭につけると、その変数に格納された数値を アドレス と見なしてその領域の内容が参照できるんです。 アドレスは、物理的なメモリに1バイトずつ番号を振ったものです。メモリが32KBしかなければ 0 ~ 32767番までの番号がアドレスです。 変数はその32KBのメモリのどこかの領域を使います。例えばlong abc変数を使うとき、abcという変数は 100番地から4バイトを使って longの数値を格納します。 ポインタ変数も同様で、long* xyz変数を使うとき、xyzという変数は 104番地から4バイトを使ってlong*の数値を格納します。 xyz に abcのアドレスを格納してみましょう。 abc = 999; xyz = &abc; そうすると xyzという変数は 100という値になります。printf ("%d", xyz);は 100を表示します。xyz に * をつけると100番地に書かれた内容になります。 printf ("%d", *xyz); は 999を表示します。 実際に変数が使うアドレスが決定するのは実行時ですし、アドレス幅はOSやコンパイラ等々によって変わりますが、要はこんな感じです。 #参考書と同じ説明になったちゃったかな?
- sha-girl
- ベストアンサー率52% (430/816)
>アドレスとポインタを使わなくても別に開発できるのではないかと思います。 可能ですがそれならC言語以外の言語を使った方が良いです。 C言語を使うメリットはアドレスを直に扱える点です。その為高速でもあるのです。 開発効率だけでみるならJavaのほうがで良いです。 まず頭の中でメモリの存在を意識してください。 最近のパソコンには256メガバイトほどのメモリがつまれていますが それぞれの位置に番地(アドレス)が割り振られています。 0------------------------------------->256MB ↑ ↑ 5000番地 20000番地 さて5000番地を拡大してみると |5000|5001|5002|5003|5004|5005|5006| |___h|___e|___l|___l|___o|___w|__\0| ↑ ここを表示したい char *p; p = (char*)5000; printf("%c",*p); とするとhと表示されるわけですね。 printf("%s",p); だと helloと表示されます。 ※5000という数値は架空のものです。
- chie65536
- ベストアンサー率41% (2512/6032)
例題として「答えを1つ返す関数」「答えを2つ返す関数」「答えを4つ返す関数」を考えてみて下さい。 例えば、 ・2つの数を足し算した答えを返す ・2つの数を足し算、引き算した答えを返す ・2つの数を足し算、引き算、掛け算、割り算した答えを返す 返す答えが1つならば int a,b,ans; a = 1; b = 2; ans = add(a,b); で済みますが、答えが2つの場合は? 関数の戻り値で答えの1つは拾えますが、2つ目の答えが拾えません。 そこで「1つは関数の戻り値で、もう1つは『この場所(アドレス)に入れてね』と、2つ目の答えを入れて欲しいポインタを渡す」と言う方法を使います。 実際には、以下のようになります。 int a,b,add_ans,sub_ans; a = 1; b = 2; add_ans = add_sub(a,b,&sub_ans); int add_sub(int a,int b,int *ans2) { *ans2 = a - b; return a + b; } あと、文字列を操作して返す関数を作る場合も、同じような考え方をします。 なぜなら「文字列を操作して結果を返す」とは「文字が複数集まった固まりを操作して結果を返す」と言う事なので、上記の複数の答えを返す関数と同じように作る訳です。 文字列の操作(例えば、2つの文字列を連結する)などの処理が必要になった場合、アドレスとポインタは避けて通れないので、是非とも理解しておきたい部分ですね。