C言語ポインタ配列と配列ポインタ-はじめに

1.ポインタ配列と配列ポインタのメモリレイアウト

初心者は、ポインタ配列と配列ポインタの違いを常に理解できるとは限りません。実際、理解するのは簡単です。

ポインタ配列:まず、配列です。配列の要素はポインタです。配列のバイト数は、配列自体によって決まります。「ポインタの配列」の略です。

配列ポインタ:まず、配列を指すポインタです。32ビットシステムでは、常に4バイトを占めます。何バイトを指しているのかはわかりません。「ポインタから配列」の略です。

下面到底哪个是数组指针,哪个是指针数组呢:
A)
int *p1[10];
B)
int (*p2)[10];
每次上课问这个问题,总有弄不清楚的。这里需要明白一个符号之间的优先级问题。

「[]」は「*」よりも優先されます。p1は最初に「[]」と組み合わされて配列の定義を形成します。配列名はp1であり、int *は配列の内容、つまり配列の各要素を変更します。

これで、これがint型データへの10個のポインター、つまりポインターの配列を含む配列であることがわかりました。p2については、理解したほうがいいです。ここでは、「()」の優先度が「[]」の優先度よりも高く、「*」記号とp2がポインタの定義を構成します。ポインタ変数の名前はp2で、intは配列の内容を変更します。つまり、配列の各要素です。

ここでは配列に名前はありません。匿名配列です。これで、p2がポインターであり、10個のint型データを含む配列、つまり配列ポインターを指すことがわかりました次の図を使用して、理解を深めることができます。ここに写真の説明を挿入

2. int(*)[10] p2 -----多分配列ポインタはこのように定義されるべきです

議論する価値のある興味深いトピックがあります。通常、データ型の後にポインタ変数名を使用してポインタを定義しませんか?ポインタp2の定義がこの構文に従って定義されていないのはなぜですか?たぶん、次のようにp2を定義する必要があります。

   int (*)[10] p2;

int(*)[10]はポインター型で、p2はポインター変数です。これは良さそうに見えますが、少しぎこちなく見えます。実際、配列ポインタのプロトタイプは確かにこのようなものですが、ポインタ変数p2は、利便性と美しさのために前方に移動されています。あなたはこれを個人的に理解することができます。コンパイラはそうは思いませんが。_

3. aと&aの違いについてもう一度話し合う

この場合、問題はここにあります。aと&aの違いについては前に説明しましたが、次のコードを見てみましょう。

int main()
{
   char a[5]={'A','B','C','D'};
   char (*p3)[5] = &a;
   char (*p4)[5] = a;
   return 0;
}

上記のp3とp4の使用法のどれが正しいですか?p3 + 1の値はどうなりますか?p4 + 1の値はどうなりますか?
p3とp4が配列ポインタであり、配列全体を指していることは間違いありません。&aは配列全体の最初のアドレス、aは配列の最初の要素の最初のアドレス、その値は同じですが意味が異なります。

C言語では、割り当て記号 "="の両側のデータ型は同じである必要があります。異なる場合は、明示的または暗黙的な型変換が必要です。p3の定義の「=」の両側のデータタイプはまったく同じですが、p4の定義の「=」の両側のデータタイプは一貫していません。

左側のタイプは配列全体へのポインターであり、右側のデータタイプは単一の文字へのポインターです。Visual C ++ 6.0では次の警告が表示されます。

   warning C4047: 'initializing' : 'char (*)[5]' differs in levels of indirection from 'char *'。

幸い、ここでは警告が出ていますが、&aとaの値は同じであり、変数を右辺値として使用する場合、コンパイラは変数の値を取得するだけなので、実行に問題はありません。しかし、私はまだこれを使用しないように警告します。

p3とp4の両方が配列全体を指していることが明らかになったので、p3 +1とp4 + 1の値は簡単に理解できます。

しかし、コードを変更すると、何が問題になりますか?p3 +1とp4 + 1の値は何ですか?

int main()
{
   char a[5]={'A','B','C','D'};
   char (*p3)[3] = &a;
   char (*p4)[3] = a;
   return 0;
}
甚至还可以把代码再修改:
int main()
{
   char a[5]={'A','B','C','D'};
   char (*p3)[10] = &a;
   char (*p4)[10] = a;
   return 0;
}

現時点ではどのような問題がありますか?p3 +1とp4 + 1の値は何ですか?

読者の皆様には、上記の問題を慎重に検討していただきたいと思います。

4、強制変換に対応

最初に次の例を検討してください。

struct Test
{
   int Num;
   char *pcName;
   short sDate;
   char cha[2];
   short sBa[4];
}*p;
假设p 的值为0x100000。如下表表达式的值分别为多少?
   p + 0x1 = 0x___ ?
   (unsigned long)p + 0x1 = 0x___?
   (unsigned int*)p + 0x1 = 0x___?

この質問が何を意味するのか、最初は理解できない人が多いと思います。実際、よく見ると、この知識はおなじみのようです。ポインタ変数と整数を解析する方法は?

以前の「a + 1」と「&a + 1」の表現の違いを覚えていますか?

実はここも同じです。ポインタ変数を整数に加算および減算することは、ポインタ変数のアドレスを使用して整数を直接加算または減算することではありません。この整数の単位はバイトではなく、要素の数です。

したがって、p + 0x1の値は0x100000 + sizof(Test)* 0x1です。この構造のサイズは20バイトですが、前の章で詳しく説明しました。したがって、p + 0x1の値は0x100014です。

(unsigned long)p + 0x1の値はどうですか?
これには、ポインタ変数pに格納されている値を符号なしの長整数に強制する強制が含まれます。値が強制されると、そのタイプが変更されます。
したがって、この式は実際には符号なしの長整数と別の整数です。したがって、その値は0x100001です。

(unsigned int *)p + 0x1の値はどうですか?
ここでのpは、符号なし整数へのポインターに強制されます。したがって、その値は0x100000 + sizof(unsigned int)* 0x1であり、これは0x100004に等しくなります。

上記の質問には技術的な内容が含まれていないようですが、ここに技術的な内容があります:x86システムでのその価値は何ですか?

intmain()
{
   int a[4]={1,2,3,4};
   int *ptr1=(int *)(&a+1);
   int *ptr2=(int *)((int)a+1);
   printf("%x,%x",ptr1[-1],*ptr2);
   return 0;
}

講義中に学生から聞かれた質問ですが、インターネットで見たのですが、n人が困惑したそうです。質問を読んだ後、私は彼に、これらの人々は確かに集会を理解していないと言いました。集会を理解している人にとって、この種の質問は本当に小さなケースです。以下でこの問題を分析して分析しましょう。

上記の説明によると、&a +1とa + 1の違いは明らかです。

ptr1:&a + 1の値を強制的にintタイプに変換し、それをタイプintの変数ptrに割り当てます。ptr1は、配列a内のタイプintの次のデータを指している必要があります。ptr1 [-1]は*(ptr1-1)として解析されます。つまり、ptr1は4バイト戻ります。したがって、その値は0x4です。
ptr2:上記の説明によると、(int)a + 1の値は、要素a [0]の2番目のバイトのアドレスです。次に、このアドレスをintタイプの値に強制し、ptr2に割り当てます。つまり、ptr2の値は、要素a [0]の2番目のバイトから始まる4つの連続したバイトの内容である必要があります。

メモリレイアウトは次のとおりです。
ここに写真の説明を挿入
さて、ここで問題が発生します。これらの4つの連続するバイトに正確に何が格納されますか?言い換えれば、要素a [0]とa [1]の値はどのように保存されますか。これには、システムの大小のエンディアンモードが含まれます。アセンブリを理解していれば、これはまったく問題ではありません。現在のシステムがどのモードであるかわからないので、それをテストする方法を見つける必要があります。スモールエンディアンモードとテスト方法については、第1章でunionキーワードについて説明したときに詳しく説明しましたが、別の場所に移動して確認してください。ここでは詳しく説明しません。次の関数を使用して、現在のシステムモードをテストできます。

int checkSystem( )
{
   union check
   {
      int i;
     char ch;
   } c;
   c.i = 1;
   return (c.ch ==1);
}

現在のシステムがビッグエンディアンモードの場合、この関数は0を返します。リトルエンディアンモードの場合、この関数は1を返します。つまり、この関数の戻り値が1の場合、* ptr2の値は0x2000000です。この関数の戻り値が0の場合、* ptr2の値は0x100です。

元のリンク:http//c.biancheng.net/cpp/html/476.html

おすすめ

転載: blog.csdn.net/zhuyin6553/article/details/88380782