ここから本文です

C言語のポインタについて。

nat********さん

2017/5/2907:39:18

C言語のポインタについて。

ポインタの問題で分からないところがあったのでききたいです。
int dt[3][4]={
{1 ,2,3}
{2,4,6}
{3,5,9}
}という二次元配列を並べたら
1 2 3 0
2 4 6 0
3 6 9 0になると思うんですけど、
*(*(dt+1)+2)がなぜdt[1][2]つまり6を指し示すのかがわかりません。
中から一つずつ分解していくと途中でこんがらがってしまいます。
誰か詳しく教えて頂ける方がいたら是非教えてください。お願いします。

閲覧数:
104
回答数:
4

違反報告

ベストアンサーに選ばれた回答

khurataさん

2017/5/3004:06:03

int dt[3][4]={
{1,2,3},
{2,4,6},
{3,5,9},
};

という配列定義は、
1,2,3,0,2,4,6,0,3,5,9,0
という並びの要素をメモリ上に作り出します。

これは、次のような解釈となります。
{{1,2,3,0},{2,4,6,0},{3,5,9,0}}
「最外殻の配列すなわち dt は、{1,2,3,0},{2,4,6,0},{3,5,9,0} という3要素の配列である」
「最外殻配列の 0番目の要素すなわち dt[0] は、1,2,3,0 という4要素の配列である」
「最外殻配列の 1番目の要素すなわち dt[1] は、2,4,6,0 という4要素の配列である」
「最外殻配列の 2番目の要素すなわち dt[2] は、2,4,6,0 という4要素の配列である」
「dt[0] の 0番目の要素すなわち dt[0][0] は 1 という int型の値である」
「(以下略)」

dt[1][2] という式を評価すると、これはポインタ演算 *( *(dt+1)+2) に読み替えられます。

配列変数を評価する場合、配列変数名は、ポインタ変数のような扱いになります。
(ただし完全にポインタと同一ではない)

またポインタ演算における加減算の単位は、そのポインタの型の sizeof です。 そのため、上記例の int が short であろうが char であろうが、正しい位置の要素を指し示します(この規則が、構造体ポインタの時には非常にありがたい…普段、特に意識することは無いが)。

上記を踏まえて、1つずつ考えてみましょう。

*( *( dt+1 ) +2 ) の内側のカッコ部分である ( dt+1 ) は、dt から「配列要素1個分のバイト幅」を 1個ぶん進めたところ、すなわち「dt から sizeof( dt[0] ) *1 だけ進めたところ」という意味です。 dt[0] は { 1,2,3,0 } ですから、型は int[4] です。 従って、これは「dt から sizeof( int[4] ) *1 だけ進めたところ」になります。

つまり、( dt+1 ) は、{ { 1,2,3,0 }, { 2,4,6,0 }, { 3,5,9,0 } } の中にある { 2,4,6,0 } の先頭アドレスを表します。 それに参照演算子 * を付ければ、{ 2,4,6,0 } の並びそのものが得られます。 この型は、もちろん int[4] です。

*( *( dt+1 ) +2 ) の外側のカッコ部分である ( ~ +2 ) は、「~」から「~型の要素」を 2個ぶん進めたところ、という意味です。 この場合の~は { 2,4,6,0 } ですから、「~型の要素を 2個ぶん進めた」というのは、「int[4] 型の中の要素を 2個ぶん進めた」、すなわち「sizeof( int ) *2 だけ進めた」という事になり、{ 2,4,6,0 } の中にある 6 の先頭アドレスを表します。 それに参照演算子 * を付ければ、6 そのものが得られます。 この型は int です。

文章にすると長いですが、内容は簡単です。 混乱してきたら、何度でも、上記をゆっくりと読み返してみてください。 一見、複雑に見えますが、大した事は書いていません。
これに慣れてスラスラ読めるようになると、この先はだいぶラクになります。

質問した人からのコメント

2017/5/30 06:42:11

皆さん、たくさんの詳しい回答ありがとうございました!おかげでスッキリできました。中でもより詳しく書いてくださったkhurataさんをベストアンサーにさせていただきます。

ベストアンサー以外の回答

1〜3件/3件中

並び替え:回答日時の
新しい順
|古い順

2017/5/2912:31:46

> 一つずつ分解していく
それしかないんですけどね...

C言語においては、
1.多次元配列は低次の配列の配列
2.配列だけが記述されると配列の先頭要素へのポインタとみなす
3.T型のポインタptrについて、ptr[n]と*(ptr+n)は等価
という規則があります。今回適用する規則はこれだけです。

dtは、二次元配列ですから一次元の配列の配列です。
dtだけが単独で記述された場合、つまり一次元の配列へのポインタになります。
*(dt+1)とdt[1] は等価で、これはchar[4]の配列そのもの、ということは、規則2よりその先頭要素を指すポインタ(つまり&dt[1][0])として解釈されます。
なので、*(dt[1]+2)はdt[1][2]と等価です。

以上より、
*(*(dt+1)+2)はdt[1][2]と等価です。

久遠人さん

2017/5/2908:53:28

**はポインタの配列
ポインタの+1は単純に1を足すということにはならない

>type test.c
main()
{
char c;
int i;
long l;
char *pc = &c;
int *pi = &i;
long *pl = &l;
int dt[3][4]={
{1 ,2,3},
{2,4,6},
{3,5,9}
};
printf("%p %p\n",pc,pc + 1);
printf("%p %p\n",pi,pi + 1);
printf("%p %p\n",pl,pl + 1);
printf("%p %p\n",*(dt + 1),*(dt + 1) + 2);
for(l = 0;l < 3;l++) {
for(c = 0;c < 4;c++)
printf("%p ",&dt[l][c]);
putchar('\n');
}
}


>cl test.c
Microsoft(R) C/C++ Optimizing Compiler Version 19.10.25019 for x64
Copyright (C) Microsoft Corporation. All rights reserved.

test.c
Microsoft (R) Incremental Linker Version 14.10.25019.0
Copyright (C) Microsoft Corporation. All rights reserved.

/out:test.exe
test.obj

>test
000000A5C84FFB80 000000A5C84FFB81
000000A5C84FFBA0 000000A5C84FFBA4
000000A5C84FFB84 000000A5C84FFB88
000000A5C84FFBB8 000000A5C84FFBC0
000000A5C84FFBA8 000000A5C84FFBAC 000000A5C84FFBB0 000000A5C84FFBB4
000000A5C84FFBB8 000000A5C84FFBBC 000000A5C84FFBC0 000000A5C84FFBC4
000000A5C84FFBC8 000000A5C84FFBCC 000000A5C84FFBD0 000000A5C84FFBD4

*(dt+1)は2番目の配列{2,4,6}の先頭のアドレスを指し、そのアドレスに+2をした場所は6の入っているアドレスになる

ayu********さん

2017/5/2908:12:36

*dtは配列の先頭のポインタです。なので
*(dt+1)
は先頭の次の位置です。
(dt[1])

*(*(dt+1)+2)
はdt[1]のポインタから2つ先の位置です。
(dt[1][2])

この質問につけられたタグ

みんなで作る知恵袋 悩みや疑問、なんでも気軽にきいちゃおう!

Q&Aをキーワードで検索:

Yahoo! JAPANは、回答に記載された内容の信ぴょう性、正確性を保証しておりません。
お客様自身の責任と判断で、ご利用ください。
本文はここまでです このページの先頭へ

「追加する」ボタンを押してください。

閉じる

※知恵コレクションに追加された質問は選択されたID/ニックネームのMy知恵袋で確認できます。

不適切な投稿でないことを報告しました。

閉じる