C言語関係掲示板

過去ログ

No.1037 配列とポインタについて

[戻る] [ホームページ]
No.13339

配列とポインタについて(初心者)
投稿者---ROXY(2004/03/25 17:16:39)


お世話になっております。次にあるプログラムで*の使われ方が理解できません。どなたか教えてください、お願いします。


#include <stdio.h>

int main(void)
{
int dat[2][3][2] = {{{1,2},{3,4},{5,6}},
{{7,8},{9,10},{11,12}}};

printf("&dat[0][0][0] :%d\n", &dat[0][0][0]);
printf("&dat[0][0][1] :%d\n", &dat[0][0][1]);
printf("&dat[0][1][0] :%d\n", &dat[0][1][0]);
printf("&dat[1][0][0] :%d\n", &dat[1][0][0]);
printf("-------------------------------------\n");
printf("dat+1 :%d\n", dat+1);
printf("*dat+1 :%d\n", *dat+1);
printf("**dat+1 :%d\n", **dat+1);
printf("***dat+1 :%d\n", ***dat+1);

return 0;
}
######################
出力結果
######################

&dat[0][0][0] :1245008
&dat[0][0][1] :1245012
&dat[0][1][0] :1245016
&dat[1][0][0] :1245032
----------------------
dat+1 :1245032
*dat+1 :1245016
**dat+1 :1245012
***dat+1 :2



No.13341

Re:配列とポインタについて(初心者)
投稿者---YuO(2004/03/25 19:03:15)


ソースを貼るときはちゃんと整形してください。


>お世話になっております。次にあるプログラムで*の使われ方が理解できません。

単項*演算子の使い方はどこにあっても変わらないですが……。

    printf("&dat[0][0][0] :%d\n", &dat[0][0][0]);
    printf("&dat[0][0][1] :%d\n", &dat[0][0][1]);
    printf("&dat[0][1][0] :%d\n", &dat[0][1][0]);
    printf("&dat[1][0][0] :%d\n", &dat[1][0][0]);

ポインタは%dではなく%pを使います。

    printf("dat+1         :%d\n", dat+1);
    printf("*dat+1        :%d\n", *dat+1);
    printf("**dat+1       :%d\n", **dat+1);
    printf("***dat+1      :%d\n", ***dat+1);

それぞれ配列表記を使うと,
dat+1
&dat[1]
*dat+1
&dat[0][1]
**dat+1
&dat[0][0][1]
***dat+1
dat[0][0][0]+1
になります。

こういう話はちゃんと型を考えてみればよいです。それぞれ型は
dat
int [2][3][2]型→int (*)[3][2]型
*dat
int [3][2]型→int (*)[2]型
**dat
int [2]型→int *型
***dat
int型
となります。
#→はC++でいうArray-to-pointer conversionによって変換される型

3番目の
**dat+1
について考えてみます。
これは,
(*(*(dat)))+1
に等しいです。

  1. 変換によってdatはint (*)[3][2]型を持ち,datの先頭要素(=dat[0])を指すポインタになります。
  2. 単項*演算子が適用されたので,dat[0]が得られます。この時点で,
    ***dat+1
    (*(*(dat[0])))+1
    と等しくなっています。
  3. 変換によって(*dat)はint (*)[2]型を持ち,(*dat)の先頭要素(=dat[0][0])を指すポインタになります。
  4. 単項*演算子が適用されたので,dat[0][0]が得られます。この時点で,
    ***dat+1
    (*(dat[0][0]))+1
    と等しくなっています。
  5. 変換によって(**dat)はint *型を持ち,(**dat)の先頭要素(=dat[0][0][0])を指すポインタになります。
  6. **datはポインタ型の式なので,
    **dat+1
    は**datが指す配列要素の次の要素を指すポインタになります。
  7. 結果として,
    **dat+1
    はdat[0][0][1]を指すポインタになります。



No.13344

Re:配列とポインタについて(初心者)
投稿者---ROXY(2004/03/26 09:32:37)


ご教授いただきまして、ありがとうございます。

>こういう話はちゃんと型を考えてみればよいです。それぞれ型は
>dat
>int [2][3][2]型→int (*)[3][2]型
>*dat
>int [3][2]型→int (*)[2]型
>**dat
>int [2]型→int *型
>***dat
>int型
>となります。
>#→はC++でいうArray-to-pointer conversionによって変換される型


上記における内容が理解できていませんでした。いまだに、なぜこのような型になるのかわかりません。私は独習Cをメインに学習しておるのですが、このような内容はなくインターネットで調べてもなかなか記載がありません。
配列datがアドレスを持つことは理解できますが、*datがint[3][2]型となるところがまずわかりません。これは、「そういうもの」と理解して問題ないでしょうか?
何度もすいません。

No.13346

Re:配列とポインタについて(初心者)
投稿者---tetrapod(2004/03/26 10:08:23)


配列からポインタへの変換、ってのはCの基本なので WEB に説明が無いわけがありません。
わかりやすさ優先で説明するなら、次の2点となります。

A: type_t array[N]; がある (type_t は任意の型) とき、
array と記述すると(一部の例外を除き)それは &array[0] とみなされる。
B: Cにおいて、多次元配列は「配列の配列」である。

まず A の例を出してみます。
int a[10]; とあるなら a は &a[0] つまり int* 型です。
struct xyz uvw[4]; なら uvw は &uvw[0] つまり struct xyz* 型です。

次に B の例です。
int a23[2][3]; があるとき、これは「「int 3つの配列」が2つある配列」です。
typedef int ia3[3];
ia3 a23[2];
と書いたのと同じです。これに A を適用します。
a23 は &a23[0] つまり ia3* 型です。
日本語で書くなら「「int 3つの配列」へのポインタ」ですね。
typedef を戻すと ia3* 型は int (*)[3] と書けます。

では int a423[4][2][3]; はどうなるでしょうか?
typedef int ia23[2][3];
ia23 a423[4];
ということになります (B が繰り返し適用される)
A を適用すると a423 は &a423[0] つまり ia23* 型です。
(規則 A が適用されるのは一度っきりです)
typedef を展開すると ia23* 型は int(*)[2][3] 型なのです。

元質問である「 * を適用するとどうなるか」は、ここまでわかれば簡単。
* の適用対象となるポインタ (dat) が何型かを考えましょう。


No.13348

Re:配列とポインタについて(初心者)
投稿者---ROXY(2004/03/26 11:39:10)


非常に分かり易いご説明、ありがとうございました。
多次元配列を「配列の配列」と解釈することで、ようやく理解すること
ができました。
今後ともよろしくお願いいたします。



No.13351

Re:配列とポインタについて(初心者) 【Eratta only】
投稿者---YuO(2004/03/26 13:00:08)


  1. 変換によってdatはint (*)[3][2]型を持ち,datの先頭要素(=dat[0])を指すポインタになります。
  2. 単項*演算子が適用されたので,dat[0]が得られます。この時点で,
    ***dat+1
    (*(*(dat[0])))+1
    と等しくなっています。
  3. 変換によって(*dat)はint (*)[2]型を持ち,(*dat)の先頭要素(=dat[0][0])を指すポインタになります。
  4. 単項*演算子が適用されたので,dat[0][0]が得られます。この時点で,
    ***dat+1
    (*(dat[0][0]))+1
    と等しくなっています。
  5. 変換によって(**dat)はint *型を持ち,(**dat)の先頭要素(=dat[0][0][0])を指すポインタになります。
  6. **datはポインタ型の式なので,
    **dat+1
    は**datが指す配列要素の次の要素を指すポインタになります。
  7. 結果として,
    **dat+1
    はdat[0][0][1]を指すポインタになります。


  1. 変換によってdatはint (*)[3][2]型を持ち,datの先頭要素(=dat[0])を指すポインタになります。
  2. 単項*演算子が適用されたので,dat[0]が得られます。この時点で,
    **dat+1
    (*(dat[0]))+1
    と等しくなっています。
  3. 変換によって(*dat)はint (*)[2]型を持ち,(*dat)の先頭要素(=dat[0][0])を指すポインタになります。
  4. 単項*演算子が適用されたので,dat[0][0]が得られます。この時点で,
    **dat+1
    dat[0][0]+1
    と等しくなっています。
  5. 変換によって(**dat)はint *型を持ち,(**dat)の先頭要素(=dat[0][0][0])を指すポインタになります。
  6. **datはポインタ型の式なので,
    **dat+1
    は**datが指す配列要素の次の要素を指すポインタになります。
  7. 結果として,
    **dat+1
    はdat[0][0][1]を指すポインタになります。