重要なお知らせ

「教えて! goo」は2025年9月17日(水)をもちまして、サービスを終了いたします。詳細はこちら>

電子書籍の厳選無料作品が豊富!

2次元配列を作り、そのアドレスなどを表示させましたが
どうもおかしいです。
3行5列の配列で、3本のポインタ配列がありますが、
その配列のアドレスがかぶっているのです。
まずはコンパイルしてみてください。
#include<stdio.h>
main()
{
int i;
int k;
int y=1;
int **p;

p=(int **)malloc(sizeof(int *)*3);

for(i=0;i<=2;i++)
{
*(p+i)=(int *)malloc(sizeof(int)*5);
}

for(i=0;i<=2;i++){
for(k=0;k<=4;k++){

p[i][k]=(i+k)*y;
}
y=y*10;
}

for(i=0;i<=2;i++){
for(k=0;k<=4;k++){

printf("%4d",*(*(p+i)+k));

}
printf("\n");
}
printf("\n");


for(i=0;i<=2;i++)
{
printf("%p\n",p+i);
}

printf("\n");

for(i=0;i<=2;i++)
{
printf("%p\n",&p+i);
}

printf("\n");

for(i=0;i<=2;i++)
{
printf("%p\n",*(p+i));
}

printf("\n");

for(i=0;i<=4;i++)
{
printf("%p\n",*(p)+i);
}

printf("\n");

for(i=0;i<=4;i++)
{
printf("%p\n",&*(p)+i);
}

printf("\n");

for(i=0;i<=4;i++)
{
printf("%p\n",&**(p)+i);
}

printf("\n");

for(i=0;i<=4;i++)
{
printf("%p\n",*(p+1)+i);
}

printf("\n");

for(i=0;i<=4;i++)
{
printf("%p\n",&*(p+1)+i);
}

printf("\n");

for(i=0;i<=4;i++)
{
printf("%p\n",&**(p+1)+i);
}
printf("\n");

for(i=0;i<=4;i++)
{
printf("%p\n",*(p+2)+i);
}

printf("\n");

for(i=0;i<=4;i++)
{
printf("%p\n",&*(p+2)+i);
}

printf("\n");

for(i=0;i<=4;i++)
{
printf("%p\n",&**(p+2)+i);
}

printf("\n");

for(i=0;i<=2;i++){
for(k=0;k<=4;k++){

printf("%p\n",&*(*(p+i)+k));

}}
free(p);

return 0;
}


どこがかぶっているかと申しますと、
for(i=0;i<=4;i++)
{
printf("%p\n",&*(p)+i);
}

とfor(i=0;i<=4;i++)
{
printf("%p\n",&*(p+1)+i);
}

for(i=0;i<=4;i++)
{
printf("%p\n",&*(p+2)+i);
}
です。
これが同じアドレスになるはずは論理的にありえないことです。
VC++2008無料バージョンです。
よろしくお願いいたします。

A 回答 (16件中1~10件)

> printf("%p\n",&*(p)+i);



*(p)で、pが指している場所の値を取りだしています。
そして、&で、そのアドレスを取り出しています。そこは、pそのものです。
つまり、&*(p)+i は、p+i と同じです。
同様に、

> printf("%p\n",&*(p+1)+i);

&*(p+1)+i は、p+1+i と同じです。
また、

> printf("%p\n",&*(p+2)+i);

&*(p+2)+i は、p+2+i と同じです。
結局、おかしいとおっしゃっている3つのループでは、それぞれ
p~p+4
p+1~p+5
p+2~p+6
を出力しています。
重複している部分があります。そこが同じ値になるのは当然です。
    • good
    • 0
この回答へのお礼

ありがとうございます。
そこでp[1][3]に注目してみました。
それがこれです。
#include<stdio.h>
main()
{
int i;
int k;
int y=1;
int **p;

p=(int **)malloc(sizeof(int *)*3);

for(i=0;i<=2;i++)
{
*(p+i)=(int *)malloc(sizeof(int)*5);
}

for(i=0;i<=2;i++){
for(k=0;k<=4;k++){

p[i][k]=(i+k)*y;
}
y=y*10;
}

for(i=0;i<=2;i++){
for(k=0;k<=4;k++){

printf("%4d",*(*(p+i)+k));

}
printf("\n");
}

printf("%d\n",p[1][3]);

printf("%p\n",&p[1][3]);

printf("%d\n",*(*(p+1)+3));

printf("%p\n",*(p+1)+3);

printf("%p\n",&*(p+1)+3);

printf("%p\n",&*(p+2)+2);/*アドレスがかぶってしまっている*/

return 0;
}

下から1個目と2個目が理解できません。
p[1][3]に入っている値は40、
p[1][3]のアドレスは00370764、
*(p+1)+3が指す先は40、
*(p+1)+3が入っている値は00370764
ここまでは理解できます。
じゃあ
*(p+1)+3のアドレスはと思い、調べようと思ったのです。
*(p+1)+3のアドレスを調べようと思い、&をつけたら
なんと勝手に1+3=4を計算してしまっています。
ためしに*(p+2)+2のアドレスも調べてみたら2+2=4なので
同じアドレスになっています(最終行)
どうやったら
p[1][3]のアドレスが入っている変数のアドレス
を知ることができるのでしょうか?

お礼日時:2008/09/29 05:50

言語使用の解説からちょっと行ってみましょう。



質問者様は左辺値と右辺値をご存知ないようですね。(もしご存知ならこれを見て復習してください。)
Cの値には左辺値と右辺値があり、右辺値は1だとか2だとかいった数値などです。
左辺値とは右辺値の入れ物を指すものです。
a = 10;
とあれば、aが左辺値、10が右辺値です。
また、左辺値は右辺値が要求される場所にあった場合、自分の持っている右辺値を返します。
a = b;
bは左辺値ですが、bは自分の持っている右辺値を代入演算子に渡します。

「&」はアドレス演算子といいます。
この演算子は単項演算子で、右結合です。
オペランドとして左辺値を取り、そのポインタを返します。

ポインタとはそれだけではただの右辺値であり、左辺値ではありません。そこで「*」が登場します。
「*」は間接演算子といい、ポインタ型の左辺値を右辺値にします。

よって以下のとおりとなります。(コメントは面倒なんでC99方式)

char *p; //ポインタ
char c = 'a'; //char型

p = &c; //cのアドレスをpに保存 (cは左辺値でアドレス演算子により右辺値のchar*型の値を返す。pは左辺値なので、pの右辺値が書き換えられる。
*p = 'b'; //pはchar*型だが、間接演算子によりポインタの指す場所(c)の左辺値を返す。結局この式はc = 'b';と同じになる。
putchar(*p); //pはchar*型だが、間接演算子により該当する場所の左辺値(cと等しい)を返す。しかし、関数の引数は右辺値を取るので*pの持つ値を返す。結局この式はputchar('b')ということになる。

という感じです。右辺値と左辺値については参考URLをご覧ください。

要するに左辺値は場所、右辺値は数値であり、場所からしかアドレス演算子でアドレス(ポインタ)が取れないのです。
ポインタ自体は場所を示す数値であり、場所じゃないので、間接演算子で場所を取ります。
そして、配列は先頭から連続する場所として作られるので、先頭アドレスにインデックスを加算したアドレスを間接演算子に渡すと、配列の指定されたインデックスの場所を返してくれるわけです。

そして、場所からは数値が取れますが、数値からは数値がいる場所は取れません。たとえば、左辺値を箱、右辺値を箱に入ってる玉として、玉を見るためには箱から取り出す必要があったとします。
箱に入ってる玉は、箱を開けて取り出してみれば分かります。箱を開けて玉を取り出しました。さて玉はどの箱に入っているのでしょうか?
さっきの箱? いいえ、違います。取り出してるので今手元にある玉はどの箱にも入っていません。
どの箱にも入っていないのだから、箱の場所は分かりません。

実際の計算機上ではきっとレジスタといわれるアドレスの取れない一時的な記憶領域に計算のために、(あるいは計算の結果として)置かれています。

参考URL:http://ja.wikipedia.org/wiki/%E5%80%A4_(%E6%83%8 …
    • good
    • 0
この回答へのお礼

みなさん、ありがとうございました。
とりあえず、浅い理解はできました。
これ以上深追いするのはやめておきます。
数学でもなんでもそうですが、ある時突然わかる日が来る場合もあります。
僕は頭が悪いのでその日は来ないかもしれません。
しかし大変ためになりました。

お礼日時:2008/10/04 07:32

>a+1は7を指差し、a[1]のアドレスも持ち


>a+2は8を指差し、a[2]のアドレスも持っています。
指してはいますが、持ってはいません。
"自分"はそもそも居ません。居ませんから居場所もありません。
aの1つ次、2つ次、です。

int a = 5;
printf ("%d \n", a);
printf ("%d \n", a +1);
printf ("%d \n", a +2);
↑で6、7を保持する変数は居ないのと一緒です。
    • good
    • 0

>a+1は7を指差し、a[1]のアドレスも持ち


>a+2は8を指差し、a[2]のアドレスも持っています。
>それなのに、自分のアドレスは持っていない(居場所が無い)というのは
>どういうことなのでしょうか?
そうですね。確かに、コンピューターの中の、「ある場所」にa+1という値は存在しています。
しかし、コンピュータの中というのは、すべての場所にアドレスがあるわけじゃないんです。CPUのレジスタだったり、あるいはレジスタですらないCPUの回路のどこかだったりします。こういった場所にはアドレスがありません。
 a+1なんて値は、たいていそういった場所にしか存在しないんです。なので、C/C++の規格では、a+1といった値に対してはアドレスが取れないことになっています。そのほうが実行時の効率がいいからです。
 このあたりはアセンブラを勉強してみると、感覚がつかめると思います。
 最近のCPUはいろいろと複雑になっていて、わかりやすい本とかも少ないと思いますが、さらなる興味がおありでしたら、勉強してみるのもいいかと思います。

 
    • good
    • 0

> printf("%p\n",a+1);/*a[1]のアドレスを保持している*/



保持しているとは、メモリー中のどこかにある、という意味ですか?
だとすると、それは勘違いです。
a+1という式で、a[1]という領域を指すことができるだけです。
    • good
    • 0

先ほどの回等に補足します。


pの型:int **
p[i]の型:int *
p[i][j]の型:int
です。
    • good
    • 0

> p=(int **)malloc(sizeof(int *)*3);


> まずここでポインタ配列を3個確保しています。

これはいいですね。ポインター配列の各要素には、p[0], p[1], p[2]で
アクセスできます。

> *(p+i)=(int *)malloc(sizeof(int)*5);
> ここでポインタ配列を15個確保しています。
> この15個の中身はp[i][j]のアドレスが入っています。

ここで勘違いされています。
「ポインター配列を15個確保している」というのは、誤りです。
このmallocで確保するのは、p[0], p[1], p[2]のそれぞれにぶら下がる
5個のint型領域です。5個のint *型領域ではありません。
当該のint型領域には、例えばp[1][2]でアクセスできます。
p[1][2]のアドレスは、通常の配列と同じように、&p[1][2]で求めます。
    • good
    • 1

> printf("%p\n",&*(p+1)+i);


>にしたらコンパイルはできたということなんです。
コンパイルが通ればいいってもんじゃありません。
&*(p+1)+i
というのは、すなわち、
(p+1)+i
p+1+i
と同じです。なので、あなたが表示させたい値ではないと思います。
ではなぜ、
>printf("%p\n",&(*(p+1)+i));
がエラーになるかというと、
*(p+1)+i
に実体がないからです。

int a;
と、したときに、
&a
はaのアドレスを返しますが、
&(a+1)
は実体がないのでエラーになります。これと同じことです。
    • good
    • 0
この回答へのお礼

だんだん見えてきました。
僕の完全な勘違いでした。
昨日書いた定理は間違いでした。
#include<stdio.h>
main()
{
int a[3]={5,7,8};

printf("%d\n",*a);
printf("%d\n",*(a+1));/*指差している*/
printf("%d\n",*(a+2));

printf("%p\n",a);
printf("%p\n",a+1);/*a[1]のアドレスを保持している*/
printf("%p\n",a+2);

printf("%p\n",&a);
printf("%p\n",&(a+1));
printf("%p\n",&(a+2));

return 0;
}
僕はこれが正しいと思っていたんです。
しかし、下3行がコンパイルエラーになります。
しかし、それはいいとして、
a+1は7を指差し、a[1]のアドレスも持ち
a+2は8を指差し、a[2]のアドレスも持っています。
それなのに、自分のアドレスは持っていない(居場所が無い)というのは
どういうことなのでしょうか?
自分はメモリ上に存在しないのに、指を指し、アドレスを保持しているとはどういう理屈なんでしょうか。
存在しないのに指を指しアドレスを保持できるなんていう話は
聞いたことがありません。

お礼日時:2008/09/30 06:31

#8です。

先ほどの回答に書き間違いがありました。

> p[2][0]:p[0]と同じ, p[2][1]:0x00953468, p[2][2]:0x0095346C, p[2][3]:0x00953470, p[2][4]:0x00953474

ではなくて、

p[2][0]:p[2]と同じ, p[2][1]:0x00953468, p[2][2]:0x0095346C, p[2][3]:0x00953470, p[2][4]:0x00953474

です。
    • good
    • 0

せっかくなので、もう少しおつきあいさせてください。


私のところで実験してみると、動的に確保した領域のアドレスは、それぞれ

p:0x00953424
p[0]:0x00953434, p[1]:0x0x0095344C, p[2]:0x00953464
p[0][0]:p[0]と同じ, p[0][1]:0x00953438, p[0][2]:0x0095343C, p[0][3]:0x00953440, p[0][4]:0x00953444
p[1][0]:p[1]と同じ, p[1][1]:0x00953450, p[1][2]:0x00953454, p[1][3]:0x00953458, p[1][4]:0x0095345C
p[2][0]:p[0]と同じ, p[2][1]:0x00953468, p[2][2]:0x0095346C, p[2][3]:0x00953470, p[2][4]:0x00953474

となりました。
どこもダブっていません。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!