为什么数组的地址等于它在C中的值?

Ale*_*dre 179 c arrays pointers

在下面的代码中,指针值和指针地址按预期不同.

但数组值和地址不行!

怎么会这样?

产量

my_array = 0022FF00
&my_array = 0022FF00
pointer_to_array = 0022FF00
&pointer_to_array = 0022FEFC
Run Code Online (Sandbox Code Playgroud)
#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;
}
Run Code Online (Sandbox Code Playgroud)

Jer*_*fin 201

阵列的名称通常计算到所述阵列的所述第一元素的地址,所以array&array具有相同的值(但不同类型的,所以array+1&array+1如果数组超过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));
Run Code Online (Sandbox Code Playgroud)

我们可以期望第二个指针比第一个指针大16(因为它是一个16个字符的数组).由于%p通常以十六进制转换指针,因此它可能类似于:

0x12341000    0x12341010
Run Code Online (Sandbox Code Playgroud)

  • @Clifford:如果你将数组传递给一个函数,它会衰减到一个指向它的第一个元素的指针,所以有效地传递`&array [0]`,而不是`&array`,这将是一个指向数组的指针.这可能是挑剔,但我认为明确这一点很重要; 如果函数的原型与传入的指针类型相匹配,编译器将发出警告. (11认同)
  • @Alexandre:`&array`是指向数组第一个元素的指针,其中`array`指的是整个数组.通过比较`sizeof(array)`和`sizeof(&array)`也可以观察到根本的区别.但请注意,如果将`array`作为参数传递给函数,实际上只传递`&array`.除非使用`struct`封装,否则不能按值传递数组. (3认同)
  • @Clifford的第一个评论是错误的,为什么还要保留呢?我认为这可能会导致对那些没有阅读以下(@Charles)回复的人产生误解. (3认同)
  • @Jerry Coffin例如int*p =&a,如果我想要int指针p的内存地址,我可以做&p.由于&数组转换为整个数组的地址(从第一个元素的地址开始).然后我如何找到数组指针的内存地址(存储数组中第一个元素的地址)?它必须在记忆中的某个地方吗? (2认同)
  • @JohnLee:不,在内存中的任何地方都没有指向数组的指针.如果创建指针,则可以获取其地址:`int*p = array; int**pp =&p;`. (2认同)

Eli*_*sky 29

那是因为数组name(my_array)与指向数组的指针不同.它是数组地址的别名,其地址定义为数组本身的地址.

但是,指针是堆栈上的普通C变量.因此,您可以获取其地址并从其中包含的地址获取不同的值.

在这里写了这个主题- 请看一下.

  • “ my_array”,当不是“&”或“ sizeof”运算符的主题时,被评估为指向其第一个元素(即,“&my_array [0]”)的指针-但是,“ my_array”本身是** not **该指针(“ my_array”仍然是数组)。该指针只是一个短暂的右值(例如,给定“ int a;”,就像“ a + 1”一样)-至少从概念上讲,它是“根据需要计算的”。my_array的真正“值”是整个数组的内容-只是将这个值固定在C中就像在罐子中捕捉雾一样。 (2认同)

CB *_*ley 26

在C中,当您在表达式中使用数组的名称(包括将其传递给函数)时,除非它是address-of(&)运算符或sizeof运算符的操作数,否则它将衰减为指向其第一个元素的指针.

也就是说,在大多数情况下array,相当于&array[0]在这两个类型和值.

在您的示例中,my_array具有在将其传递给printf时char[100]衰减到的类型char*.

&my_array有类型char (*)[100](指向数组100的指针char).因为它是操作数&,所以这是my_array不会立即衰减到指向其第一个元素的指针的情况之一.

指向数组的指针具有与指向数组第一个元素的指针相同的地址值,因为数组对象只是其元素的连续序列,但指向数组的指针与指向元素的指针的指针具有不同的类型.那个数组.当您对两种类型的指针进行指针运算时,这很重要.

pointer_to_array具有类型char *- 初始化为指向数组的第一个元素,因为它是my_array初始化表达式中的衰减 - 并且&pointer_to_array 具有类型char **(指向a的指针char).

其中:( my_array在衰减之后char*),&my_array并且pointer_to_array都直接指向数组或数组的第一个元素,因此具有相同的地址值.


R S*_*ahu 6

my_array当您查看数组的内存布局时,可以很容易地理解和&my_array结果相同地址的原因。

假设您有一个包含 10 个字符的数组(而不是代码中的 100 个字符)。

char my_array[10];
Run Code Online (Sandbox Code Playgroud)

内存my_array看起来像这样:

+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array.
Run Code Online (Sandbox Code Playgroud)

在 C/C++ 中,数组衰减为指向表达式中第一个元素的指针,例如

printf("my_array = %p\n", my_array);
Run Code Online (Sandbox Code Playgroud)

如果检查数组的第一个元素所在的位置,您将看到它的地址与数组的地址相同:

my_array[0]
|
v
+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array[0].
Run Code Online (Sandbox Code Playgroud)


sup*_*cat 5

在 B 编程语言(C 的前身)中,指针和整数可以自由互换。系统的行为就好像所有内存都是一个巨大的数组。每个变量名都有一个与之关联的全局地址或堆栈相对地址,对于每个变量名,编译器必须跟踪的唯一事情是它是全局变量还是局部变量,以及相对于第一个全局变量或局部变量的地址多变的。

给定一个像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 编程语言的目标之一是与 B 很大程度上兼容。在 B 中,数组的名称[在 B 的术语中称为“向量”]标识了一个包含指针的变量,该指针最初被分配为指向到给定大小的分配的第一个元素,因此如果该名称出现在函数的参数列表中,则该函数将接收指向该向量的指针。尽管 C 添加了“真正的”数组类型,其名称与分配的地址严格相关,而不是最初指向分配的指针变量,但将数组分解为指针使得声明 C 类型数组的代码表现相同B 代码声明了一个向量,然后从未修改保存其地址的变量。