我在使用Objective-C for iOS进行开发时遇到了这个问题,但这应该适用于使用Mac OS X / iOS链接器的任何C / C ++ / Objective-C代码。该解决方案由another question涵盖,但我对为什么感兴趣。
假设我正在使用链接到定义常量的库。在头文件中有一个这样的声明:
extern char * const BrandNewIdentifier;
我想编译我的应用程序并在具有早期版本库的系统上运行它,其中该常量没有定义,所以为了安全起见我不认为它已被定义。
现在,如果有一个只在最新版本的库中定义的函数,我可以这样做:
if (BrandNewFunc) {
BrandNewFunc("foobar", 42);
} else {
printf("Your system does not support some thing.");
}
BrandNewFunc
的计算结果为NULL,而不是包含函数代码的地址。我认为常量的行为方式相同,但如果我尝试相同的模式,应用程序会在执行检查时死亡(在iOS上抛出EXC_BAD_ACCESS)。具体做法是:
if (BrandNewIdentifier) ... // blows up here
取而代之的是检查标识符的地址:
if (&BrandNewIdentifier) {
printf("You got it!");
}
我可以看到逻辑:BrandNewIdentifier
没有值,因此访问它会失败。但那么为什么这种语法适用于BrandNewFunc
?我不应该被要求检查其地址吗?或者它实际上是否一致,还有一些我忽略的东西?
答案 0 :(得分:2)
C标准的相关部分是第6.3.2.1节“左值,数组和函数指示符”。以下是关于功能的说法:
函数指示符是具有函数类型的表达式。除非它是
sizeof
运算符 65 或一元&
运算符的操作数,否则函数指示符的类型为''函数,返回 type ' '被转换为一个表达式,其类型为''指向函数的指针 type ''。[脚注65]因为没有发生这种转换,
sizeof
运算符的操作数仍然是函数指示符,并且违反了6.5.3.4中的约束[编辑:6.5.3.4中的约束说你可能没有将sizeof
应用于函数指示符 - 这是语义错误]。
命名函数的标识符是最简单的“具有函数类型的表达式”。所以这意味着,如果foo
已被声明为函数,则标识符foo
计算为指向该函数的指针,除了,当它是{{1}的操作数时(在这种情况下,较大的表达式&
计算为指向该函数的指针)或&foo
的操作数(在这种情况下,较大的表达式sizeof
会引发编译错误)
tl,dr:当sizeof(foo)
是函数时,foo
和foo
根据定义是等效的。这是功能的特殊规则。它与数组的特殊规则并不完全不同,数组也在许多情况下“衰减”到指针(该规则是我引用的一个段落)。
旁白:是的,这意味着函数调用运算符总是在指向函数的指针上运行。当&foo
是指向函数的指针变量时,pfunc
的处理方式就像读取(*pfunc)()
...或只是(&(*pfunc))()
一样。