• 締切済み

二次元配列とポインタについて

関数内の変数宣言にて (1) int *( p[ 5 ] ); (2) int ( *p )[ 5 ]; の違いを教えて下さい。 (1)は *p[ 5 ] と同義のようで int実体のポインタとなるp[ 0 ]からp[ 4 ]の 配列が作られるようです。 つまり領域に作られるのは 5つの連続したint型へのシングルポインタであり その他のint実体やダブルポインタは 領域に作られないと認識しております。 (2)との違いが分かりません。 領域では実際に何が作られるのかという点と このように演算の優先順位がある場合に どのような順番で解釈すればよいのかという点について ご説明頂けると助かります。 ではよろしくお願いします。

みんなの回答

noname#30727
noname#30727
回答No.3

(1) int *( p[ 5 ] ); pは配列→pはint*を5つ格納する配列 (2) int ( *p )[ 5 ]; pはポインタ→pはint[5]へのポインタ <例> int array[3][5]; sub(array); ~~~~~~~ void sub(int (*p)[5]) { p[2][2] = 3; } 1次元配列では特に意識しなかったものが、2次元以上の配列では考える必要があることがわかると思います。

  • leaz024
  • ベストアンサー率75% (398/526)
回答No.2

宣言される変数がどのような性質を持つのか判読するには、まず宣言される変数に着目し、優先順位や結合規則に従って演算子を見ていきます。 (1) の p は [5] と結合しているので、p は「5つの要素を持つ配列」だと分かります。 それが * と結合しているので、「配列の要素はポインタ」だと分かります。 最後に int を見て、「ポインタが指すデータはint型」だと分かります。 int *p[5] の場合、p に * と [5] が両方くっついていますが、間接参照演算子より配列演算子の方が優先順位が上のため、(1) と同じ意味になります。 (2) の場合、p は ( ) によって * と結合しているので、p は「ポインタ変数」だと分かります。(用意されるのはポインタ1つ分のメモリだけです。) それが [5] と結合しているので、「ポインタが指すデータは、要素5つの配列」だと分かります。 最後に int を見て、「配列の要素はint型」だと分かります。 これだけ読むと何のための変数か分かりませんが、実はこれは「int型ニ次元配列へのポインタ変数」なのです。例えば int a[3][5] という二次元配列があった時、この変数 a へのポインタ p の宣言が int (*p)[5] となります。 配列のメモリ確保の仕組み上、何次元配列でもポインタは *p でいいだろうと思われがちなのですが、このポインタは二次元以上の配列の代わりにはならないのです。これは次の「配列とポインタの関係」から容易に理解できることと思います。 一次元配列の場合、   int a[10];   int *p;   p = a; のようにポインタ p を用意すると、p は a の代わりに使えるようになります。例えば   p[3] = 100; とすると、これは   a[3] = 100; を行ったのと同じことになります。 この配列とポインタの関係は、二次元以上の配列でも成り立つべきです。つまり   int a[3][5];   ?    ・・・a へのポインタ p の宣言   p = a; とした時、   a[1][0] = 100; の代わりに   p[1][0] = 100; とできるべきです。 ところが p の宣言を int *p とした場合、この代替関係は成り立ちません。 アドレス的には p[5] とすればよいだけなのですが、この方法には何のメリットもなく、やはり p[1][0] とできて初めて「これぞ二次元配列へのポインタ」と言えるわけです。 しかし、この p には一次元目の要素数情報がないため、p[1][0] が p から何番目のintデータになるのか計算できないので、このようにアクセスすることができないのです。 そこで二次元配列へのポインタ用に、一次元目の要素数情報を与えた宣言が int (*p)[5] です。 この p は「5つの要素を持つint型配列」単位で操作を行うポインタなので、*p のサイズ(p++ で増加する値)は sizeof(int)*5 バイトであり、p[0]、p[1]、p[2] はそれぞれ a[0]、a[1]、a[2] と等価となります。 また、p[0] や p[1] が指している先は配列なので、さらに配列参照することで各int型データにアクセスすることができます。つまり、p[1][0] で a[1][0] にアクセスできるのです。

  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.1

(1)をp1 (2)をp2として説明します。 私の処理系では sizeof(p1)は20バイトになって これは、4バイトサイズのポインタが5つ確保されたということです。 質問文に書かれている通りです。 sizeof(p2)は4バイトとなって、 たんなる1つのポインタです。 これは、どういう型へのポインタかというと int data[5]; のようなint型の5のサイズをもつ配列へのポインタです。 int (*p2)[5]; int i; static int data[3][5]={ { 1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}}; p2=&data[0]; p2++; for(i=0;i<5;i++) printf("%d,",(*p2)[i]); のようなプログラムでは、 6,7,8,9,10, が表示されます。 宣言における解釈の優先は、カッコによって変えられます。 どうしてそうなるかというと、 それが、有意義だから(そういうのが必要だから)で 例えば int *p[ 5 ]; が int *( p[ 5 ] ); に解釈されて int ( *p )[ 5 ]; に解釈されないのは、妥当なことだと思います。 (普通上に解釈すると思う。そして、処理系もそうなっている) そして、必要な時には、宣言上の解釈の順位を変えることができるのも妥当だと思います。 解釈の簡単な仕方は、*で何が復元されるのかということを考えることだと思います。 (1)の例で言えば *(p[])はintになる (2)の例で言えば (*p)で配列の名前部分になる と考えれば少し考えやすいのではないかなと思います。

関連するQ&A