web-dev-qa-db-ja.com

2次元配列へのCポインター

私は、良い(そして実用的な)解決策を与えるものについていくつかの質問があることを知っていますが、これを達成するための最良の方法は何かを明確に言うIMHOはありません。したがって、2D配列があるとします。

int tab1[100][280];

この2D配列を指すポインターを作成します。これを実現するために、次のことができます。

int (*pointer)[280]; // pointer creation
pointer = tab1; //assignation
pointer[5][12] = 517; // use
int myint = pointer[5][12]; // use

または、代わりに:

int (*pointer)[100][280]; // pointer creation
pointer = &tab1; //assignation
(*pointer)[5][12] = 517; // use
int myint = (*pointer)[5][12]; // use 

両方ともうまくいくようです。今、私は知りたい:

  • 最善の方法は、1番目または2番目ですか?
  • コンパイラーはどちらも同等ですか? (速度、パフォーマンス...)
  • これらのソリューションの1つは他よりも多くのメモリを消費していますか?
  • 開発者がより頻繁に使用するものは何ですか?
27
Chrysotribax
//defines an array of 280 pointers (1120 or 2240 bytes)
int  *pointer1 [280];

//defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
int (*pointer2)[280];      //pointer to an array of 280 integers
int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers

pointer2またはpointer3を使用すると、 WhozCraig で指摘されている++pointer2と同じ操作を除き、同じバイナリが生成されます。

typedefを使用することをお勧めします(上記と同じバイナリコードを生成するpointer3

typedef int myType[100][280];
myType *pointer3;

注:C++ 11以降では、usingの代わりにキーワードtypedefも使用できます。

using myType = int[100][280];
myType *pointer3;

あなたの例では:

myType *pointer;                // pointer creation
pointer = &tab1;                // assignation
(*pointer)[5][12] = 517;        // set (write)
int myint = (*pointer)[5][12];  // get (read)

注:配列tab1が関数本体内で使用される場合、この配列は呼び出しスタックメモリ内に配置されます。ただし、スタックサイズには制限があります。空きメモリスタックよりも大きな配列を使用すると、 stack overflowcrash が生成されます。

完全なスニペットは gcc.godbolt.org でオンラインコンパイル可能です

int main()
{
    //defines an array of 280 pointers (1120 or 2240 bytes)
    int  *pointer1 [280];
    static_assert( sizeof(pointer1) == 2240, "" );

    //defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
    int (*pointer2)[280];      //pointer to an array of 280 integers
    int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers  
    static_assert( sizeof(pointer2) == 8, "" );
    static_assert( sizeof(pointer3) == 8, "" );

    // Use 'typedef' (or 'using' if you use a modern C++ compiler)
    typedef int myType[100][280];
    //using myType = int[100][280];

    int tab1[100][280];

    myType *pointer;                // pointer creation
    pointer = &tab1;                // assignation
    (*pointer)[5][12] = 517;        // set (write)
    int myint = (*pointer)[5][12];  // get (read)

    return myint;
}
25
olibre

_int *pointer[280];_ // int型の280個のポインターを作成します。

32ビットOSでは、各ポインターに4バイト。 4 * 280 = 1120バイトです。

int (*pointer)[100][280]; // [100] [280] intの配列を指すために使用されるポインターを1つだけ作成します。

ここでは4バイトのみです。

質問に来ると、int (*pointer)[280];int (*pointer)[100][280];は異なりますが、[100] [280]の同じ2D配列を指します。

int (*pointer)[280];がインクリメントされると、次の1D配列を指しますが、int (*pointer)[100][280];は2D配列全体を横切り、次のバイトを指します。そのバイトにアクセスすると、そのメモリがプロセスに属していない場合に問題が発生する可能性があります。

9
Jeyaram

どちらの例も同等です。ただし、1つ目はそれほど明白ではなく、より「ハッキング」ですが、2つ目は明確にあなたの意図を述べています。

int (*pointer)[280];
pointer = tab1;

pointerは、280個の整数の1D配列を指します。割り当てでは、実際に最初のrow of tab1を割り当てます。これは、暗黙的に配列をポインター(最初の要素)にキャストできるため機能します。

pointer[5][12]を使用している場合、Cはpointerを配列の配列として処理します(pointer[5]int[280]型です)、別の暗黙があります=ここにキャストします(少なくとも意味的に)。

2番目の例では、2D配列へのポインターを明示的に作成します。

int (*pointer)[100][280];
pointer = &tab1;

セマンティクスはここでより明確です:*pointerは2D配列なので、(*pointer)[i][j]を使用してアクセスする必要があります。

どちらのソリューションも同じ量のメモリ(1ポインター)を使用するため、ほとんどの場合同じ速度で実行されます。内部では、両方のポインターが同じメモリー位置(tab1配列の最初の要素)を指しており、コンパイラーが同じコードを生成することもあります。

最初の解決策は「より高度」です。何が起こっているのかを理解するには、Cで配列とポインターがどのように機能するかを深く理解する必要があるためです。 2つ目はより明示的です。

9
Ferdinand Beyer