C函数指针行为,数组/数组指针行为

时间:2013-12-06 21:00:32

标签: c pointers

请考虑以下代码:

#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. 第(1)行和第(2)行打印相同的结果。 ret_five&ret_five具有相同的数据类型吗?似乎没有,因为第(3)和(4)行打印出不同的结果。

  2. 从语法的角度来看,在我看来,第(5)行应该是调用p 指向的函数的正确方法,但当然第(1)和(2)行打印5就好了。是否存在技术原因,或者是否因为(1)和(2)中的呼叫看起来更干净而做出设计决策?或其他什么?

  3. 第(5)行对我来说很有意义(因为p是一个函数指针,它的解引用值是函数,我们调用函数,它返回5,我们打印5)。我很惊讶地发现(6)还打印了5张!这是为什么?

  4. 类似地,第(7) - (10)行都打印相同的值,即&ret_five。为什么(10)有效?

  5. 第(11)行和第(12)行打印相同的值,即x的第一个元素存在于内存中的地址。第(12)行是有道理的,但我不完全清楚第(11)行中究竟发生了什么。 x是否会在此上下文中自动转换或解释为int*

  6. 要获取存储x的内存中的位置,我通常会&x[0],但似乎&x也可以正常工作,因为{{1}实际上x可能是获取此内存地址的更规范的方式,它是一个数组而不是指针。是否有理由偏爱另一个?

  7. 一般来说,在上述情况下是否有最佳做法?例如,如果&xp = ret_five;确实做了完全相同的事情,是否有理由选择其中一个?

  8. 并且,如果问题7中的两个作业真的完全相同,那么为什么用一种其他如此严格的语言来内置这种松弛呢?

3 个答案:

答案 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

x5 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 *类型参数。

7.21.6格式化输入/输出功能:

  

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是整个数组的地址。