簡體   English   中英

為什么數組的地址等於它在 C 中的值?

[英]How come an array's address is equal to its value in C?

在以下代碼中,指針值和指針地址與預期不同。

但是數組值和地址沒有!

怎么會這樣?

輸出

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有相同的值(但是類型不同,所以如果數組大於1, array+1&array+1就不相等了元素長)。

對此有兩個例外:當數組名稱是sizeof或一元& (address-of) 的操作數時,名稱指的是數組對象本身。 因此sizeof array為您提供整個數組的字節大小,而不是指針的大小。

對於定義為T array[size] ,它將具有類型T * 當/如果你增加它,你會得到數組中的下一個元素。

&array計算到相同的地址,但給定相同的定義,它創建一個T(*)[size]類型的指針——即,它是一個指向數組的指針,而不是指向單個元素。 如果你增加這個指針,它會增加整個數組的大小,而不是單個元素的大小。 例如,使用這樣的代碼:

char array[16];
printf("%p\t%p", (void*)&array, (void*)(&array+1));

我們可以期望第二個指針比第一個大 16 個(因為它是一個 16 個字符的數組)。 由於 %p 通常以十六進制轉換指針,因此它可能類似於:

0x12341000    0x12341010

這是因為數組名稱( my_array ) 不同於指向數組的指針。 它是數組地址的別名,它的地址定義為數組本身的地址。

然而,指針是堆棧上的一個普通 C 變量。 因此,您可以獲取其地址並獲得與其內部保存的地址不同的值。

在這里寫過這個話題 - 請看一看。

在 C 中,當您在表達式中使用數組的名稱時(包括將其傳遞給函數),除非它是 address-of ( & ) 運算符或sizeof運算符的操作數,否則它會衰減為指向其第一個的指針元素。

也就是說,在大多數情況下, array在類型和值上都等價於&array[0]

在您的示例中, my_array類型為char[100] ,當您將其傳遞給 printf 時,它會衰減為char*

&my_array類型為char (*)[100] (指向 100 個char數組的指針)。 由於它是&的操作數,這是my_array不會立即衰減到指向其第一個元素的指針的一種情況。

指向數組的指針與指向數組第一個元素的指針具有相同的地址值,因為數組對象只是其元素的連續序列,但指向數組的指針與指向數組元素的指針具有不同的類型那個數組。 當您對兩種類型的指針進行指針運算時,這一點很重要。

pointer_to_array具有類型char * -初始化為點在陣列的第一個元素,因為這是什么my_array衰減到在初始化表達-和&pointer_to_array具有類型char ** (指針的指針到一個char )。

其中: my_array (衰減到char* )、 &my_arraypointer_to_array都直接指向數組或數組的第一個元素,因此具有相同的地址值。

當您查看數組的內存布局時,很容易理解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].

在 B 編程語言中,它是 C 的直接前身,指針和整數可以自由互換。 系統會表現得好像所有內存都是一個巨大的數組。 每個變量名都有一個與之關聯的全局或堆棧相關地址,對於每個變量名,編譯器唯一需要跟蹤的是它是全局變量還是局部變量,以及它相對於第一個全局或局部變量的地址多變的。

給定一個像i;這樣的全局聲明i; [不需要指定類型,因為一切都是整數/指針]將被編譯器處理為: address_of_i = next_global++; memory[address_of_i] = 0; address_of_i = next_global++; memory[address_of_i] = 0; 並且像i++這樣的語句將被處理為: memory[address_of_i] = memory[address_of_i]+1; .

類似arr[10];聲明arr[10]; 將被處理為address_of_arr = next_global; memory[next_global] = next_global; next_global += 10; address_of_arr = next_global; memory[next_global] = next_global; next_global += 10; . 請注意,一旦處理該聲明,編譯器就會立即忘記arr是一個數組 類似arr[i]=6;語句arr[i]=6; 將被處理為memory[memory[address_of_a] + memory[address_of_i]] = 6; . 編譯器不會關心arr是否代表數組而i是整數,反之亦然。 事實上,它不會在乎它們是數組還是整數; 它會很高興地生成所描述的代碼,而不考慮由此產生的行為是否可能有用。

C 編程語言的目標之一是在很大程度上與 B 兼容。在 B 中,數組的名稱 [在 B 的術語中稱為“向量”] 標識了一個變量,該變量包含一個指針,該指針最初被分配為指向到給定大小的分配的第一個元素,因此如果該名稱出現在函數的參數列表中,則該函數將收到一個指向該向量的指針。 盡管 C 添加了“真正的”數組類型,其名稱與分配的地址嚴格相關,而不是最初指向分配的指針變量,但將數組分解為指針使得聲明 C 類型數組的代碼行為相同到 B 代碼,它聲明了一個向量,然后從不修改保存其地址的變量。

實際上&myarraymyarray都是基地址。

如果您想查看差異而不是使用

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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM