• ベストアンサー

アドレスとポインタがどうしても理解できない

C言語を独学しているのですが、どの参考書読んでも、アドレスとポインタの理解ができません。アドレスとポインタを使わなくても別に開発できるのではないかと思います。どなたか、アドレスとポインタを初心者でも分かるように分かりやすく教えて頂けないでしょうか?

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

  • ベストアンサー
  • neKo_deux
  • ベストアンサー率44% (5541/12319)
回答No.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)
回答No.4

>アドレスとポインタを使わなくても別に開発できるのではないかと思います。 という疑問に対して、回答します。 極論をいえば、確かにそのとおりです。 ただし、システムの資源および時間が無尽蔵に使用でき、後のメンテナンスを考えなくていいならば、という条件がつきます。 ポインタを使わなくても、グローバル変数を使用することによって複数の値のやり取りはできます。 また、文字列処理もその長さがどのくらいになるか分からなければ何MB何GBといった十分に大きな領域を用意すれば良いだけの話です――理論的には。 しかし実際には、グローバル変数の使用はメンテナンスする際どこがその値を修正しどこで利用されているのか非常に分かりづらくなりますし、十分に大きな領域を用意するといってもそんなに巨大な領域を用意することはできません。 複数の値の参照・修正地点を明確にしたり、必要な時に必要な領域を確保するときにポインタが有効なのです。 あと、巨大な構造体を引数にする関数の場合、構造体のコピーを避けるためにポインタを使用します。 ポインタがある理由としてはこんなところです。

  • yatokesa
  • ベストアンサー率40% (201/496)
回答No.3

ポインタと言っても、ちょっと特殊なだけの普通の変数です。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やコンパイラ等々によって変わりますが、要はこんな感じです。 #参考書と同じ説明になったちゃったかな?

参考URL:
http://maborosi.kobe-du.ac.jp/sugiura/soft/tora/
  • sha-girl
  • ベストアンサー率52% (430/816)
回答No.2

>アドレスとポインタを使わなくても別に開発できるのではないかと思います。 可能ですがそれなら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)
回答No.1

例題として「答えを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つの文字列を連結する)などの処理が必要になった場合、アドレスとポインタは避けて通れないので、是非とも理解しておきたい部分ですね。

関連するQ&A

  • ポインタについてアドバイスお願いします。

    C言語の初心者です。 参考書でも書かれている事があるのですが、経験豊富な方でもポインタについて知ってるようで知ってないということがあります。って読んだことがあります。 実際、ポインタの必要性とはどのような時に必要なのでしょうか?? アドレス指定と言う風に、はじめはこの様な理解から入ると習いましたが、私には配列で十分間に合うのでは??って思っちゃうのです。 構造体の出力、アドレスの入れ替え、こんなときには便利なのかもしれませんが、C言語をやる上で絶対に必要・・、いったいなぜ??って考えちゃいます。 初心者なのに生意気な事言ってしまってもうしわけございません。 以前、私もそんな感じで悩んだ事あるって方がいらっしゃいましたら、ポインタとはこんな感じの時に初めて必要だと思うのだ!とご教授お願いいたします。 この様な質問に対しても、笑って答えてくださるような プロフェッショナルの方や、一般の凄い方のアドバイス、お待ちいたしております。 宜しくお願いいたします。

  • 関数ポインタの利点

    こんにちは。 C言語初心者ですが今勉強中です。 その中でポインタについては理解できたのですが、 関数ポインタの利点、使うべき所などが理解できません。 ポインタの基本は理解しています。 値渡し、アドレス渡しも理解しています。 関数ポインタを使うと何がいい、またはどんなとき使わなければならないのか 教本を読んでいてもさっぱりわかりません。 サンプルプログラムを打っても何のために使ってるのかわからないです。 どなたか教えていただけませんでしょうか? よろしくお願いします。

  • C言語のポインタとスタックポインタ

    プログラム始めて1ヶ月の初心者です。 C言語のポインタとスタックポインタというのは同じなのでしょうか。 スタックポインタの考えは大体理解出来たのですが C言語のポインタとなるとコードを見てもサッパリ分かりません。 ネットで調べても出てこなかったのでどなたか教えて下さい。 よろしくお願いします。

  • C言語のポインタに直接アドレスを割り振りしたい

    C言語のポインタに直接アドレスを割り振りしたいのですが、どうしたら良いのでしょうか?

  • 関数へのポインタ

    超初心者です。 C言語を使ってsin波を生成して音を鳴らそうとしているのですが・・ネットで調べてもよくワカリマセン・・ 超初心者な私でも理解できるようなサイトを教えて下さい. また、関数へのポインタも勉強しているのですが, char *(*func)(void); といったchar 型へのポインタを返す関数へのポインタというのがあったとして,関数へのポインタは理解できたのですがさらにchar 型へのポインタとなると一体これが何を指しているのかさっぱりで・・・・ ご協力お願いします.

  • ポインタ

    C言語初心者の者です。 構造体のいくつかのメンバを取り出して 複数の構造体にそのメンバの値をコピーする方法 をポインタのアドレス操作を使ってやりたいのですが さっぱりわかりません。手順を教えていただきたいです。 よろしくお願いいたします。

  • ハンドル、アドレス、ポインターについて。

    質問失礼します。 VB2005においてプログラミングをしようしているのですが、作っている途中で用語に躓いてしまいました。 ハンドル、アドレス、ポインタというのは具体的に何を表すのでしょうか? 自分が使うプログラムのコードを確認していて、GCHandleという構造体を見つけました。 これを理解するにはハンドル、アドレス、ポインタの理解が必要だと聞いたのですが、さっぱりわかりません。 幸いGCHandleについては、「勝手に処理されないようにするために必要なんだな」くらいのことは理解しましたが、ハンドル、アドレス、ポインタの概念を理解できていないせいか、その程度の理解で終わってしまいます。 よろしくお願いいたします。

  • C言語のポインタを理解するには

    今、自分はC言語のポインタを勉強しているのですがメモリの動きなどが想像できません・・・(なんでポインタを使うと速くなるのかなど) 聞いたり調べたところアセンブラをやると理解できるとのことでアセンブラをやろうと思ってるのですが他になにかやるべきことはあるでしょうか? 後コマンドプロンプトとPICは勉強になるでしょうか? 昔のパソコンを使ってるとパソコンがよくわかるみたいなことを聞いたのですが昔のパソコンなんて持ってないのでCUIのコマンドプロンプトを勉強すればなんかわかるかなと思って。 PICは自分で作れば勉強にもなるしそこからなんか得られるかなと思って聞いてみました。 まだパソコンをもって長くないのでいろいろと調べたのですがどうしてもよく理解できないところがあり質問におかしいところがあるかもしれないですがお返事よろしくお願いします。

  • ポインタ変数とポインタのポインタ

    ポインタ変数の宣言 char *a[]; をしたとき僕の中では a[0],a[1]...という、ある文字列A,B,C...の最初のアドレスを指すポインタが、配列になっているものを宣言していると理解していました。 しかしこの次に、ポインタのポインタが出てきました。僕はこれを、 ある変数を指し示すアドレスのアドレスである、と理解しました。 この2つは1つめはいくつかのアドレスを指し示すもの、2つ目は1つのアドレスを指し示すものであるとして、僕の中で異なったものであると理解していましたが、参考書「C標準コースウェア」によると プログラムにおいて、関数でポインタ配列を受け取るときchar *p[]はchar **pとしてもよい と書かれており、またその実例として、 (9-5) #include <stdio.h> void disp (char *p[],int n){ int i; for (i= 1;i<n;i++){ printf("%s\n",p[i]); } } int main(void){ char *girl[] = {"Arica","Candy","Lisa"}; disp (girl,sizeof(girl)/sizeof(girl[0])); return 0; } というプログラムが書かれていました。 ここで一気に訳が分からなくなりました。 char *girl[] = {"Arica","Candy","Lisa"}; と宣言されているため、 girl[0]はAricaという文字列の最初のアドレスを指すポインタ、 *girl[0]はAricaという文字列を直接指し示していると解釈しています。 girlは{"Arica","Candy","Lisa"}という文字列の配列の最初のアドレスを指し示していると考えました。 sizeof(girl)を使った時に不思議なのですが、 girlはどのように配列の終わりを理解しているのでしょうか? (配列の要素数を渡していない点が不思議です。) また、 disp側が受け取ったのは*girl[]であり、いくつかのポインタの配列ですが、渡したものはgirlという要素数がないポインタ1つだけです。 そして最初の疑問が出てくるわけですが、*p[]を**pと書きかえてみると、 文字列のアドレスを示すgirlという名の1つのポインタを渡すと、pという名のポインタのポインタで受け取るというのも、よくわからなくなっています。 おそらくポインタ配列に対する理解がどこかでずれているようですが、自分でどこがわからないのかわからなくなっています。 どうかご教授ください。

  • ポインタで特定のアドレスにアクセスできるのでしょうか

    armadillo9(マスター)とFPGA(スレーブ)で、SPI通信をしようと思っています。 調べたのですが、PICなどのマイコンの通信方法がでてきて、Armadillo9上でC言語で通信する方法は見つかりませんでした。 おそらく、SPI1 Data Register(0x808A+0x08)というアドレスでデータを送受信するのではと考えたのですが、Cのポインタで特定のアドレスにアクセスすることは可能なのでしょうか? 冗長な質問ですがお願いいたします。

専門家に質問してみよう