请考虑以下代码:
#include <stdio.h>
int ret_five() {
return 5;
}
int main() {
int x[5] = {1,2,3,4,5};
int (*p)();
p = &ret_five;
printf("%d\n", p()); // 1
p = ret_five;
printf("%d\n", p()); // 2
printf("%d\n", sizeof ret_five); // 3
printf("%d\n", sizeof &ret_five); // 4
printf("%d\n", (*p)()); // 5
printf("%d\n", (****p)()); // 6
printf("%p\n", p); // 7 // edited: replaced %d with %p
printf("%p\n", *p); // 8 // same here and in (8), (10)
printf("%p\n", **p); // 9
printf("%p\n", *******p); // 10
printf("%p\n", x); // 11
printf("%p\n", &x); // 12
return 0;
}
我的问题是:
第(1)行和第(2)行打印相同的结果。 ret_five
和&ret_five
具有相同的数据类型吗?似乎没有,因为第(3)和(4)行打印出不同的结果。
从语法的角度来看,在我看来,第(5)行应该是调用p
指向的函数的正确方法,但当然第(1)和(2)行打印5就好了。是否存在技术原因,或者是否因为(1)和(2)中的呼叫看起来更干净而做出设计决策?或其他什么?
第(5)行对我来说很有意义(因为p
是一个函数指针,它的解引用值是函数,我们调用函数,它返回5,我们打印5)。我很惊讶地发现(6)还打印了5张!这是为什么?
类似地,第(7) - (10)行都打印相同的值,即&ret_five
。为什么(10)有效?
第(11)行和第(12)行打印相同的值,即x
的第一个元素存在于内存中的地址。第(12)行是有道理的,但我不完全清楚第(11)行中究竟发生了什么。 x
是否会在此上下文中自动转换或解释为int*
?
要获取存储x
的内存中的位置,我通常会&x[0]
,但似乎&x
也可以正常工作,因为{{1}实际上x
可能是获取此内存地址的更规范的方式,它是一个数组而不是指针。是否有理由偏爱另一个?
一般来说,在上述情况下是否有最佳做法?例如,如果&x
和p = ret_five;
确实做了完全相同的事情,是否有理由选择其中一个?
并且,如果问题7中的两个作业真的完全相同,那么为什么用一种其他如此严格的语言来内置这种松弛呢?
答案 0 :(得分:4)
ret_five和&amp; ret_five是否具有相同的数据类型?
ret_five
是函数指示符,&ret_five
是函数指针。在表达式ret_five
中,转换为函数指针,其值和类型与ret_five
相同。
printf("%d\n", sizeof ret_five); // 3
printf("%d\n", sizeof &ret_five); // 4
sizeof &ret_five
是正确的。它产生类型为int (*)()
的函数指针的大小。
sizeof ret_five
是无效的C代码,在gcc
中被接受为GNU扩展。
printf("%d\n", p); // 7
printf("%d\n", *p); // 8
printf("%d\n", **p); // 9
printf("%d\n", *******p); // 10
如果p
是一个函数指针,p
,*p
或*****p
在C中是等效的。
printf("%p\n", x); // 11
printf("%p\n", &x); // 12
x
是5
int
元素的数组。在一个表达式中(除了少数例外,如果它是&
运算符的操作数),它将转换为指向其第一个元素的指针(转换后x
的类型为int *
})。
&x
是指向5
int
元素数组的指针(&x
的类型为int (*)[5]
。
答案 1 :(得分:1)
函数指示符是具有函数类型的表达式。除非它是 sizeof运算符的操作数或一元&amp;运算符,函数指示符 type»function returns type«转换为类型为»指向的表达式 功能返回类型«。
ret_five和&amp; ret_five都评估为相同的函数指针。 sizeof ret_five是一个约束违规,您的编译器应输出诊断。因此,ret_five是一个函数指示符,它在所有(但是两个(见上文))情况转换为指向所述函数的指针,* ret_five再次是一个函数指示符,如果你使用它,AGAIN转换为指向所述函数的指针在上述两个以外的任何上下文中,所以** ret_five再次是一个函数指示符,依此类推。使用%d打印这样的指针是未定义的行为,因为%d用于整数。
p = ret_five在现代C中是正确的。使用&amp; ret_five是老式的,1980年代C。
除非它是sizeof运算符或一元&amp;运算符的操作数。运营商,或者是 用于初始化数组的字符串文字,类型为«数组类型«的表达式 转换为类型为»指向类型«指针的表达式,指向的初始元素 数组对象并不是左值。
x和&amp; x具有相同的数值(它们是指向x的第一个元素的指针)但不同的类型。 x求值为指向int的指针,但&amp; x求值为指向五个整数的数组的指针。
答案 2 :(得分:-1)
使用%p
说明符时,您需要将参数强制转换为void *
,因为它需要void *
类型参数。
p参数应该是指向
void
的指针。指针的值是 转换为一系列打印字符,在实现定义中 方式。
printf("%p\n", (void *) &x);
行(11)和(12)打印相同的值,即x的第一个元素存在于内存中的地址。第(12)行是有道理的,但我不完全清楚第(11)行中究竟发生了什么。在此上下文中x是否会自动转换或解释为int *?
是的,它会。数组名x
衰减到指向数组x
的第一个元素的指针。它将为您提供第一个元素的位置并具有类型int *
。 &x
是整个数组x
的地址,它将始终打印数组的起始地址,类型为int (*)[5]
。由于数组的第一个元素的地址与数组x
的起始地址相同,这就是为什么你得到相同的值。
为了获得存储x的存储器中的位置,我通常做&amp; x [0],但似乎&amp; x也可以正常工作,因为x是一个数组而不是一个指针,似乎事实上,&amp; x可能是获取此内存地址的更规范的方式。有理由偏爱一个吗?
答案与之前的答案类似。 &x[0]
是第一个元素的地址,而&x
是整个数组的地址。