我检查了很多问题,问在stackoverflow,但没有得到确切的答案,我的问题。顺便说一句,我的问题是关于C。
int a[10] = {0,1,2,3,4,5,6,7,8,9};
int (*p)[10]; // ı know it is pointer to int array that consist of 10 elements.
int *d;
d = a;
p = &a; // it is array type
printf("*p = %p , p = %d", (void *)*p, (void*)p); // it prints same thing, start address of "a" array
printf("*d = %p, d = %p",*d,d); // it prints a[0] and start address of a respectively.
我已经知道“p”是一个类型化的数组,但实际上它是一个数组指针(它在内存中占用8个字节,就像大多数系统中的地址数据“d”一样)。当不引用“p”时,不期望“a[0]"。编译器以某种方式处理它并返回“a”数组的地址数据。为什么?这是标准还是历史问题?因为编译器没有找到编译器这样做的任何原因。它通常可以被解引用为“d”。
5条答案
按热度按时间okxuctiv1#
这里,
d
是指向数组元素的指针,即a
的第一个元素。赋值是正确的,因为a
本身在表达式中的意思就是,* 第一个元素的地址。这里,你将
a
数组的地址赋给p
(这是一个指向10个int
元素的数组的指针,所以赋值是正确的,两个指针都指向10个int
s的数组),所以p
* 几乎 * 是a
的别名。但是p
仍然是一个指向数组的指针(所以它存储一个地址),而a
不是。a
是一个有10个整数的数组,而不是一个指针。这里,
p
是一个指向数组的指针,所以*p
本身就是一个数组,并且,当一个数组出现在表达式中时,它被解释为第一个元素的地址,它实际上等价于&p[0]
,它是一个指向int
的指针,指向数组的第一个元素。对于普通的p
,将指向数组的指针转换为void *
指针,并将其传递给printf()
例程,获得相同的值(数组从它的第一个元素开始,所以两个值应该相等,但在这种情况下你打印的是数组的地址,而不是第一个元素的地址)我知道这很奇怪,但是你永远不能在表达式中使用数组,所以当一个表达式作为一个整体产生一个数组时,编译器会生成代码来提取第一个元素的地址。在本例中,
d
是指向int的指针,它已被初始化为数组a
的开头。所以*d
是d
指向的实际元素,你得到 * 数组单元值 *,而不是第一个元素的地址(这应该是0
,但尝试在初始化中更改数组单元以确认)。您将其转换为void *
(这将导致Undefined Behaviour,因此实际值可以为0或不同,不要介意)并打印它。当然,d
是指针值,如果你将其转换为void *
并将其传递给printf()
,你将再次获得a
的虚拟地址。你应该考虑几件事:
***数组和指针不是一回事。**要寻址(通过
p
)p
指向的数组的整数值,需要两次解引用(第一次是通过指针p
访问数组时,第二次是在执行所需的指针算术以获得数组元素的偏移量之后。但是d
只是一个指向int
的普通指针,你不能两次反引用,编译器也不会。*p
时,你得到一个数组(因为p
被定义为指向一个10个元素的数组的指针),所以编译器立即构建一个指向该数组第一个元素的指针(编译器在这里做了两次解引用,一次),使它在语义上确实与&a[0]
相同。您可以通过两个步骤进行转换...知道p
指向a
。首先将*p
更改为a
,然后将a
更改为&a[0]
。当你计算*d
时,你是在计算一个指向int
的指针(它实际上指向a
的第一个元素并解引用,所以你实际上获得了数组单元格的内容,它是一个普通的整数。在这种情况下,没有这样的两个取消引用,而只有您指定的一个。如果去掉所有类型转换,您可能只会在表达式
*d
中看到一个警告,因为它是一个整数,所以不匹配指针。这是一个GCC扩展,可以帮助您识别错误,因为传递的值实际上不是指针,而格式字符串中匹配的格式说明符实际上需要指针。sg24os4d2#
因为
p
的类型是int (*)[10]
,指向大小为10的数组的指针,解引用的p
的类型为int[10]
,即数组大小10。该数组类型作为参数传递给函数,导致数组衰减为指向其第一个成员的指针,结果类型为int *
。所以这条线告诉你:
数组本身的地址与其第一个成员的地址相同。
mpbci0fu3#
以下所有人都有相同的地址:
a
。&a[0]
d
p
当解引用
p
时,得到数组a
。其行为类似于任何数组:每当在表达式中使用时,它都会“衰减”为指向其第一个元素的指针。当不引用“p”时,不期望“a[0]”
这是错误的。为了做到这一点,你必须做两次解引用,
**p
。ecfsfe2w4#
关于“指针与指向数组的指针”的另一件事。
在你的例子中,“p”指向整个数组。如果执行指针递增/递减-它将指向数组后的第一个地址,即每次当你执行相同的操作时,它将以int10 bytes = 40 bytes的方式移动,与它指向的当前地址相比(在你的例子中)。
另一方面,你有一个“d”,它是一个常规指针-它的增量/减量与它所属的数据类型大小一样多。
特别是“d”是多余的,因为数组本身就是一个指针。如果你删除了“[]”括号,你可以直接对数组名应用“”解引用操作符,以提取存储到它所指向的地址的值。你可以用指针算法来代替“[]”括号来遍历数组。
gxwragnw5#
一些尚未涉及的其他问题:
说明符不匹配
不要打印带有
"%p"
和risk undefined behavior(UB)的int
。启用所有警告并使用匹配的说明符。
高级:等同,但不一定是相同的位模式
*p
和p
都是指针,但类型不同。p
是数组a
的地址。*p
是数组元素a[0]
的地址,int
的地址。即使在转换为
void *
之后,结果也可能具有不同的位模式。(void *)*p == (void*)p
仍然是正确的。