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

但是数组值和地址却不一样!

这怎么可能呢?

输出

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


评论

来自comp.lang.c常见问题解答:-[那么C中的“指针和数组的等效性”是什么意思? ](c-faq.com/aryptr/aryptrequiv.html)-[由于数组引用会衰减为指针,如果arr是数组,则arr和&arr有什么区别? ](c-faq.com/aryptr/aryvsadr.html)或阅读整个数组和指针部分。

我两年前在这里为这个问题添加了图表答案,那么sizeof(&array)返回什么?

#1 楼

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

有两个例外:当数组名称是sizeof或一元&(地址)的操作数时,该名称是指数组对象本身。因此,sizeof array会为您提供整个数组的大小(以字节为单位),而不是指针的大小。
对于定义为T array[size]的数组,其类型将为T *。当/如果增加它,您将到达数组中的下一个元素。它是指向数组而不是单个元素的指针。如果增加此指针,它将增加整个数组的大小,而不是单个元素的大小。例如,使用如下代码:

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


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

0x12341000    0x12341010


评论


@Alexandre:&array是指向数组第一个元素的指针,其中as array表示整个数组。通过将sizeof(array)与sizeof(&array)进行比较,也可以观察到根本差异。但是请注意,如果将array作为函数的参数传递,则实际上仅&array被传递。您不能按值传递数组,除非将其与结构封装在一起。

–克利福德
10 Mar 27 '10在7:52

@Clifford:如果将数组传递给函数,则它将衰减为指向其第一个元素的指针,因此有效地传递了&array [0],而不是&array,后者将是该数组的指针。可能是挑剔,但我认为必须弄清楚。如果函数具有与传入的指针类型匹配的原型,则编译器将发出警告。

– CB Bailey
10 Mar 27 '10 at 9:53

@Jerry Coffin例如int * p =&a,如果我想要int指针p的内存地址,则可以执行&p。由于&array转换为整个数组的地址(从第一个元素的地址开始)。然后,如何找到数组指针的内存地址(该指针存储数组中第一个元素的地址)?它一定在内存中的某处吧?

–John Lee
2012年9月12日在8:44



@JohnLee:不,在内存中的任何地方都不必指向数组。如果创建一个指针,则可以获取其地址:int * p = array; int ** pp =&p;。

–杰里·科芬(Jerry Coffin)
2012年9月12日14:06

@Clifford第一个评论是错误的,为什么还要保留呢?我认为对于不阅读以下(@Charles)答复的人来说,这可能会引起误解。

–里克
18-4-9的2:24



#2 楼

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

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

我在这里写过有关此主题的内容-请看一看。

评论


&my_array应该不是无效的操作,因为my_array的值不在堆栈中,只有my_array [0 ... length]在堆栈中吗?那就一切都有道理了...

–亚历山大
10 Mar 27 '10在6:22

@亚历山大:我不确定为什么允许这样做。

– Eli Bendersky
2010-3-27在6:46

无论变量的存储持续时间如何,都可以使用其地址(如果未标记寄存器):静态,动态或自动。

– CB Bailey
2010年3月27日10:00

my_array本身在堆栈上,因为my_array是整个数组。

–caf
10 Mar 27 '10 at 12:42

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

–caf
10 Mar 28 '10在21:18

#3 楼

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

,即,在大多数情况下,array的类型和值都等同于&array[0]。将其传递给printf。

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

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

char具有类型&-初始化为指向数组的第一个元素,因为my_array在初始化器表达式中会衰减为-而pointer_to_array的类型为char *(指向my_array的指针的指针)。其中:&pointer_to_array(衰减到char **之后),charmy_array都直接指向数组或数组的第一个元素数组,因此具有相同的地址值。

#4 楼

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

在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代码,然后再不修改保存其地址的变量。

#6 楼

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