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

时间:2010-03-27 05:59:56

标签: c pointers arrays

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

但是数组值和地址没有!

这怎么可能?

输出

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

6 个答案:

答案 0 :(得分:185)

数组的名称通常计算为数组的第一个元素的地址,因此array&array具有相同的值(但不同的类型,因此array+1和{ {1}}如果数组长度超过1个元素,则相等。)

有两个例外:当数组名称是&array+1或一元sizeof(address-of)的操作数时,名称指的是数组对象本身。因此&为您提供整个数组的字节大小,而不是指针的大小。

对于定义为sizeof array的数组,它将具有类型T array[size]。当/如果你增加它,你会到达数组中的下一个元素。

T *计算到相同的地址,但是给定相同的定义,它会创建一个类型为&array的指针 - 即,它是指向数组的指针,而不是指向单个元素的指针。如果递增此指针,它将添加整个数组的大小,而不是单个元素的大小。例如,使用以下代码:

T(*)[size]

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

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

答案 1 :(得分:29)

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

然而,指针是堆栈上的普通C变量。因此,您可以获取其地址并从其保存的地址中获取不同的值。

我写了关于这个话题的here - 请看看。

答案 2 :(得分:24)

在C中,当您在表达式中使用数组的名称(包括将其传递给函数)时,除非它是地址(&)运算符或{{1}的操作数运算符,它衰减指向其第一个元素的指针。

也就是说,在大多数情况下,sizeof在类型和值方面都相当于array

在您的示例中,&array[0]的类型my_array在将其传递给printf时会衰减为char[100]

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

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

my_array的类型为pointer_to_array - 初始化为指向数组的第一个元素,因为char *在初始化表达式中衰减了 - 而my_array具有类型&pointer_to_array(指向char **的指针)。

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

答案 3 :(得分:3)

在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中,数组的名称[称为&#34; vector&#34;在B]的术语中,确定了一个包含指针的变量,该指针最初被指定为指向给定大小的分配的第一个元素,因此如果该名称出现在函数的参数列表中,该函数将接收指针到矢量。即使C添加&#34;真实&#34;数组类型,其名称与分配地址严格关联,而不是最初指向分配的指针变量,数组分解为指针,声明C类型数组的代码与声明向量的B代码行为相同然后从不修改保存其地址的变量。

答案 4 :(得分:2)

当您查看数组的内存布局时,可以很容易地理解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].

答案 5 :(得分:1)

实际上&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);