34

我总是在 C 中理解这一点,func并且&func是等价的。我假设它们都应该是指针类型,在我的 Win64 系统上是 8 个字节。但是,我刚试过这个:

#include <stdio.h>

int func(int x, int y)
{
    printf("hello\n");
}

int main()
{
    printf("%d, %d\n", sizeof(&func), sizeof(func));
    return 0;
}

而期待得到输出8, 8却意外得到8, 1

为什么是这样?究竟 func什么类型?它似乎是类型char或等效的。
这里发生了什么?

gcc -std=c99如果它有所作为,我编译了这个。

4

4 回答 4

38

C中的函数名是什么类型?

函数名称或函数指示符具有函数类型。在表达式中使用时,除了作为sizeofor&运算符的操作数外,都会从“函数返回类型”类型转换为“指向函数返回类型的指针”类型。(这在 C99,6.3.2.1p4 中指定)。

现在

sizeof(func)

是无效的 C 因为sizeof不允许使用函数类型的操作数。这在sizeof运算符的约束中指定:

(C99,6.5.3.4p1 约束)“sizeof 运算符不得应用于具有函数类型或不完整类型的表达式、此类类型的括号名称或指定位域成员的表达式。 "

sizeof(func)

在 GNU C 中是允许的。

在 GNU C 中有一个 GNU 扩展允许它,在 GNU Csizeof中有一个函数类型为 yield 的操作数1

6.23 空指针和函数指针的算术

[...] sizeof 也可以用于 void 和函数类型,并返回 1。

http://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html

于 2013-09-15T20:02:36.257 回答
13

鉴于:

int func(int x, int y) { /* ... */ }

表达式func是函数类型。具体来说,它是 type int(int, int),这是 C 中“函数int返回两个参数的类型”的语法int。(你不会经常看到这种特定的语法,因为直接引用函数类型并不常见。)

大多数情况下,函数类型的表达式被隐式转换为指向函数的指针;在这种情况下,指针的类型是int(*)(int, int)

发生这种隐式转换的上下文是:

  1. 当表达式是 unary 的操作数时&;在这种情况下,&func产生函数的地址(就像func它本身通常所做的那样);和

  2. 当表达式是 的操作数时sizeof。没有这个例外,sizeof func将产生一个函数指针的大小。相反,它是违反约束的,需要编译器进行诊断。

(附注:在函数调用中使用函数名称时确实会发生这种转换。()“运算符”(标准不这么称呼它)需要指向函数类型的前缀。)

gcc 恰好有一个非标准的扩展;它允许对函数指针和 type 进行指针运算void*,其作用类似于指针上的指针运算char*(即,它以字节为单位进行运算)。不幸的是,恕我直言,gcc 通过 kludge 做到了这一点,将函数类型和类型的大小设置void为 1。这就是你得到sizeof func == 1; 如果您启用一种标准的符合模式(例如,gcc -std=c99 -pedantic),您将收到警告。

顺便说一句,不要%d用于打印sizeof. sizeof产生类型的结果size_t。如果您的实现支持它(C99 或更高版本),请使用%zu; 如果没有,您需要使用强制转换将size_t值显式转换为可以打印的内容。例如:

printf("%lu\n", (unsigned long)sizeof &func);
于 2013-09-15T20:11:09.993 回答
5

C中的函数名是什么类型?

它属于函数类型。

我一直都理解在 C 中,func并且&func是等价的

好吧,它们不是“等效的”。但是,函数确实会衰减为指向函数的指针。

我认为它们都应该是指针类型

这是一个错误的假设。

并期望得到输出 8, 8 却惊讶地得到 8, 1。为什么是这样?

因为 1. 如果它编译它就是 UB,2. 它甚至不应该首先编译,因此,你的程序可以自由地做任何事情。

于 2013-09-15T20:05:08.583 回答
1

名称在 C 中没有类型。某些类型的名称表示具有类型的实体,例如 typedef 名称、对象或函数。其他类型的名称表示没有类型的实体,例如预处理器符号或goto标签。还有其他类型的名称只是表示类型本身,即typedef名称。

函数的名称表示具有函数类型的实体。该函数类型包括返回类型和(可能不完整的)参数信息。

当函数用作值时,它们总是作为指向函数的类型来操作。这样的函数不能在可移植的 C 程序中传递,但指向函数的指针可以。

从概念上讲,即使在像这样的直接调用中foo(),会发生的情况是,foo表示函数的表达式在求值时会隐式转换为指向函数值的指针。()函数调用后缀运算符然后通过这个指针调用函数。

有一个规则,具有函数类型的表达式产生一个指针值,除非该表达式是&(地址)或运算符的sizeof操作数。func并且&func仅在它们产生相同值的意义上是等效的。func隐式产生一个指针值。&func抑制指针的隐式生成(func是的操作数,&因此抑制转换),但随后&获取地址。

所以你可以看到sizeof &func并且sizeof func是不同的。前者采用指针的大小,后者尝试采用函数的大小。

获取函数的大小在 C 中是违反约束的:它需要来自符合标准的实现的诊断。如果程序仍在翻译并且在1运行时产生了值,那是特定于您的语言实现的“奖励”行为。它不是标准语言。

于 2013-09-16T03:29:56.557 回答