5
#include<stdio.h>
int main(int argc , char *argv[])
{
    int array[2][2] = {{1,100},{1000,10000}};

    int *pointer    = array;
    int *ppointer   = &array;
    int *pppointer  = array[0];
    int *ppppointer = &array[0];

    printf("%d\n",*pointer);
    printf("%d\n",*ppointer);
    printf("%d\n",*pppointer);
    printf("%d\n",*ppppointer);

    return 0;
}

4 つのポインターは、配列の最初の要素を指します。上に示した定義のうち、どちらが優れていますか? そして、配列と &array に同じ値を指定する理由がわかりません。

4

7 に答える 7

8

すべての定義をコンパイルする唯一の理由は、ポインター型の変換に関して C コンパイラーが許可しすぎていることです。この点でよりペダンティックにするいくつかのスイッチを使用すると、3 番目の初期化のみが有効であり、残りは誤りであることがすぐにわかるはずです。

ほとんどのコンテキスト (いくつかの例外を除く) では、型の配列が式で使用されると、ポインター型 (最初の要素を指すT[N]ポインター) に "減衰" (暗黙的に変換) されます。T *つまり、任意の array のこのようなコンテキストではAA式は と同等&A[0]です。配列型の減衰が発生しない唯一のコンテキストは、配列の初期化子として使用される単項&演算子、sizeof演算子、および文字列リテラルcharです。

あなたの例arrayでは、タイプの値ですint [2][2]。初期化の右側で使用すると、ポインター型に減衰しますint (*)[2]。このため、これは無効です

int *pointer    = array;

右側がint (*)[2]、左側が ですint *。これらは異なるポインター型です。一方を他方で初期化することはできません。

int *ppppointer = &array[0];

は前のものとまったく同じです: 右側はint (*)[2]type の値を生成します。まったく同じ理由で無効です。

式は型の&arrayポインターを生成しますint (*)[2][2]。繰り返しますが、この理由から

int *ppointer   = &array;

無効です。

あなたの例にある唯一の有効な初期化は

int *pppointer  = array[0];

array[0]は型の表現であり、int [2]型に崩壊しint *ます - 左側にあるのと同じ型です。

つまり、ここではどちらが「優れている」かという問題はありません。初期化の 1 つだけが有効で、他は違法です。有効な初期化は、次のようにも記述できます。

int *pppointer  = &array[0][0];

上記の理由によります。さて、どちらの右辺が「より良い」(array[0]または&array[0][0]) かは、個人的な好みの問題です。


他の初期化を有効にするには、ポインターを次のように宣言する必要があります。

int (*pointer)[2]     = array;
int (*ppointer)[2][2] = &array;
int (*ppppointer)[2]  = &array[0];

しかし、そのようなポインターは、ポインターとは異なるセマンティクスを持ちint *ます。そして、あなたはどうやらint *具体的に必要です。

于 2012-07-21T20:17:53.530 に答える
2

実際に正しいことを行うのは 3 番目だけです。他の 3 つはすべて無効な C++ であり、C コンパイラで警告が発生します。一部のプラットフォームでは、C++コンパイラが C の推奨コンパイラでもあるため (MSVC)、有効な C++ でもある C を記述することが望ましい場合がよくあります。これにより、ビルド システムを大幅に変更することなく、C コードを C++ プロジェクトに簡単に含めることができます。

コンパイラが 1、2、4 について不平を言うのはなぜですか? 右側のどちらの式にも、 に変換できる正しい型がありませんint*

  1. arrayint[2][2]に変換できる型がありますがint(*)[2]、ではありませんint*
  2. &arrayへのポインタです。int[2][2]
  3. array[x]実際に型を持っていますint*
  4. &array[x]タイプありint**
于 2012-07-21T20:02:43.697 に答える
1
int *pointer    = array;     //Incorrect
int *ppointer   = &array;    //Incorrect
int *pppointer  = array[0];  //Correct
int *ppppointer = &array[0]; //Incorrect

それがショートバージョンです。理由は次のとおりです。

最初のポインターは正しくありません。これは、「配列」(これ以上の指定がないポインター) を割り当てているためです...しかし、int の 1 つではなく、int *[] の 1 つです。

2 番目のポインターは正しくありません。ポインターのアドレスを割り当てることになるためです。基本的には、データへのポインターを保持する変数のアドレスです。

サイズに関係なく、int 配列へのポインターを取得するため、3 番目は正しいです。

最初の配列のアドレスをコピーしているため、4 番目は正しくありません。これにより、int * ではなく int ** になります。

何度も編集してすみません…疲れているに違いありません。

于 2012-07-21T20:04:35.887 に答える
0

それらのどれも正しくありません (3 番目のものはエラーにはなりませんが、ポスターが望む値になるとは思いません)。

そのはず:

int ** pointer1    = array;
int ** pointer2   = &array;        //This one is wrong
int ** pointer3  = array[0];      //This one is not correct in this case
int * ppointer3  = array[0];
int ** pointer4 = &array[0];
int * pointer5 = &array[0][0];

私は最初と最後の方が好きです。

それが 1 次元配列である場合、配列が基本的にポインターであるという事実を示しているため、最初の配列を優先します。多次元配列の場合は、最後のものを使用します (値を取得するために逆参照が 1 回だけ必要なため、インデックスには注意してください。たとえば、1000 を取得する場合は、pointer5[2]代わりに使用する必要があります。pointer5[1][1]

于 2012-07-21T20:21:41.667 に答える
0

簡潔な答え:

$ cat decls.c

int main(void)
{
    int array[2][2] = {{1,100},{1000,10000}};
    int *pointer    = array;
    int *ppointer   = &array;
    int *pppointer  = array[0];
    int *ppppointer = &array[0];
}

$ clang decls.c -Wall -o decls

decls.c:4:7: warning: incompatible pointer types initializing 'int *' with an
      expression of type 'int [2][2]' [-Wincompatible-pointer-types]
        int *pointer    = array;
             ^            ~~~~~
decls.c:5:7: warning: incompatible pointer types initializing 'int *' with an
      expression of type 'int (*)[2][2]' [-Wincompatible-pointer-types]
        int *ppointer   = &array;
             ^            ~~~~~~
decls.c:7:7: warning: incompatible pointer types initializing 'int *' with an
      expression of type 'int (*)[2]' [-Wincompatible-pointer-types]
        int *ppppointer = &array[0];
             ^            ~~~~~~~~~

したがって、3 番目の宣言のみが正しいです。

少し長い答え: C で何かを宣言するときは、を使用して宣言します。式を評価すると、左側の型が得られます。

したがって、 がある場合は、 があるときにが得られるchar name[][]ことを意味します。これは逆に機能します。どうやってから を得ることができますか? することで、そうです。name[2][3]charA = name[3]charAA[2]Achar *

これが、3 番目の宣言だけが正しい理由です。左側の宣言式と右側の式の両方が同じ型であるためです。

于 2012-07-21T20:16:42.827 に答える
-1

それらはすべて同等です。

&array は配列のアドレスで、最初の要素と同じ位置から開始するため、&array = &array[0]

array は配列ですが、場合によっては、最初の要素へのポインターに崩壊する可能性があります。これが、array = &array = &array[0] の理由です。

はどうかと言うと int *pppointer = array[0];

私の第一印象は、これは間違っているはずだということです。他の誰かが説明できるかもしれません。

更新:私の推測では、配列はコンパイラによってポインターと見なされ、次のようになります。

int *pppointer = (&array)[0] = array[0]

于 2012-07-21T20:03:17.503 に答える
-2

したがって、配列内に配列があります。したがって、変数「配列」は実際には別のポインターへのポインターです。

だからあなたが言うなら:

int *pointer = array;

タイプの不一致があります。*pointerintarrayへのポインターですが、別のポインターへのポインターです。

あなたが言う時:

int *ppointer = &array;

まだタイプの不一致があります。&array は、ポインターへのポインターのアドレスを提供します。これは、ポインターへのポインターへのポインターにのみ割り当てることができます。

あなたが言う時:

int *pppointer = array[0];

正解です。角括弧は、配列変数を逆参照します。したがってarray[0]、実際には の型と一致する int へのポインターを参照します*pppointer

あなたが言う時:

int *ppppointer = &array[0];

ですから、ここで始めたところに戻ってきました。array[0]は int への&array[0]ポインターであり、int へのポインターへのポインターにのみ割り当てることができる int へのポインターのアドレスも同様です。

したがって、最終的には、3 番目のものだけが実際に有効です。ただし、個人的には、これを達成するためのより良い方法は次のようになると思います。

int *pointer = *array;
于 2012-07-21T20:12:53.637 に答える