在下面的代码中,指针值和指针地址按预期不同。
但是数组值和地址没有!
这怎么可能?
输出
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;
}
答案 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)
实际上&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);