コードの次のビットでは、ポインター値とポインターアドレスが予想どおりに異なります。
しかし、配列の値とアドレスはそうではありません!
どうすればいいの?
出力
my_array = 0022FF00
&my_array = 0022FF00
pointer_to_array = 0022FF00
&pointer_to_array = 0022FEFC
#include <stdio.h>
int main()
{
char my_array[100] = "some cool string";
printf("my_array = %p\n", my_array);
printf("&my_array = %p\n", &my_array);
char *pointer_to_array = my_array;
printf("pointer_to_array = %p\n", pointer_to_array);
printf("&pointer_to_array = %p\n", &pointer_to_array);
printf("Press ENTER to continue...\n");
getchar();
return 0;
}
配列の名前は通常、配列の最初の要素のアドレスに評価されるため、array
と&array
の値は同じです(ただし、型は異なるため、array+1
と&array+1
は、配列の長さが1要素を超える場合はnotになります)。
これには2つの例外があります。配列名がsizeof
または単項&
(address-of)のオペランドである場合、名前は配列オブジェクト自体を指します。したがって、sizeof array
は、ポインターのサイズではなく、配列全体のバイト単位のサイズを提供します。
T array[size]
として定義された配列の場合、タイプはT *
になります。インクリメントする場合/場合、配列内の次の要素に到達します。
&array
は同じアドレスを評価しますが、同じ定義を指定すると、T(*)[size]
型のポインターを作成します。つまり、単一の要素ではなく配列へのポインターです。このポインタをインクリメントすると、単一の要素のサイズではなく、配列全体のサイズが追加されます。たとえば、次のようなコードでは:
char array[16];
printf("%p\t%p", (void*)&array, (void*)(&array+1));
2番目のポインターは最初のポインターよりも16大きいことが予想されます(16文字の配列であるため)。通常、%pは16進数でポインターを変換するため、次のようになります。
0x12341000 0x12341010
これは、配列名(my_array
)が配列へのポインタと異なるためです。これはアレイのアドレスのエイリアスであり、そのアドレスはアレイ自体のアドレスとして定義されます。
ただし、ポインターはスタック上の通常のC変数です。したがって、アドレスを取得し、内部に保持しているアドレスとは異なる値を取得できます。
このトピックについて書きました こちら -ご覧ください。
Cでは、式(配列を関数に渡すことを含む)で配列の名前を使用する場合、アドレスの(&
)演算子またはsizeof
演算子のオペランドでない限り、 it decays最初の要素へのポインタへ。
つまり、ほとんどの場合、array
は型と値の両方で&array[0]
と同等です。
あなたの例では、my_array
の型はchar[100]
であり、printfに渡すとchar*
に減衰します。
&my_array
の型はchar (*)[100]
(100の配列へのポインターchar
)です。これは&
のオペランドであるため、my_array
がすぐに最初の要素へのポインターに減衰しない場合の1つです。
配列オブジェクトはその要素の連続したシーケンスであるため、配列へのポインタは配列の最初の要素へのポインタと同じアドレス値を持ちますが、配列へのポインタは次の要素へのポインタに対して異なる型を持ちますその配列。これは、2種類のポインターでポインター演算を行う場合に重要です。
pointer_to_array
の型はchar *
-初期化式でmy_array
が減衰する配列の最初の要素を指すように初期化され、&pointer_to_array
の型はchar **
(char
へのポインターへのポインター)。
これらのうち、my_array
(char*
への減衰後)、&my_array
およびpointer_to_array
はすべて、配列または配列の最初の要素のいずれかを直接指しているため、同じアドレスを持ちます。値。
Cの前身であるBプログラミング言語では、ポインターと整数は自由に交換できました。システムは、すべてのメモリが巨大なアレイであるかのように動作します。各変数名には、グローバルまたはスタック相対アドレスが関連付けられていました。各変数名について、コンパイラーが追跡する必要があるのは、それがグローバル変数であるかローカル変数であるか、および最初のグローバルまたはローカルに対するアドレスのみでした変数。
i;
のようなグローバル宣言が与えられると、[すべてが整数/ポインターであったため、型を指定する必要はありませんでした]コンパイラーはaddress_of_i = next_global++; memory[address_of_i] = 0;
およびi++
のようなステートメントを処理しますmemory[address_of_i] = memory[address_of_i]+1;
として処理されます。
arr[10];
のような宣言は、address_of_arr = next_global; memory[next_global] = next_global; next_global += 10;
として処理されます。その宣言が処理されるとすぐに、コンパイラーはarr
が配列であることをすぐに忘れることができることに注意してください。 arr[i]=6;
のようなステートメントは、memory[memory[address_of_a] + memory[address_of_i]] = 6;
として処理されます。コンパイラは、arr
が配列を表し、i
が整数を表しているかどうか、またはその逆を気にしません。実際、両方が配列であるか、両方が整数であるかは関係ありません。結果の動作が有用であるかどうかに関係なく、記述されているようにコードを完全に幸福に生成します。
Cプログラミング言語の目標の1つは、主にBと互換性があることでした。Bでは、配列の名前[Bの用語で「ベクトル」と呼ばれる)は、最初に指すポインターを保持する変数を識別しました与えられたサイズの割り当ての最初の要素に、その名前が関数の引数リストに現れた場合、関数はベクトルへのポインタを受け取ります。 Cが最初に割り当てを指すポインター変数ではなく、割り当てのアドレスに厳密に関連付けられた「実際の」配列型を追加したにもかかわらず、配列がポインターに分解され、C型配列を宣言するコードが同様に動作するようになりましたベクトルを宣言し、そのアドレスを保持する変数を変更したことのないBコード.
my_array
と&my_array
が同じアドレスになる理由は、アレイのメモリレイアウトを見ると簡単に理解できます。
10文字の配列があるとしましょう(代わりに、コードの100文字)。
char my_array[10];
my_array
のメモリは次のようになります。
+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array.
C/C++では、配列は次のような式の最初の要素へのポインターに減衰します。
printf("my_array = %p\n", my_array);
配列の最初の要素の位置を調べると、そのアドレスが配列のアドレスと同じであることがわかります。
my_array[0]
|
v
+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array[0].
実際、&myarray
とmyarray
は両方ともベースアドレスです。
使用する代わりに違いを確認する場合
printf("my_array = %p\n", my_array);
printf("my_array = %p\n", &my_array);
つかいます
printf("my_array = %s\n", my_array);
printf("my_array = %p\n", my_array);