0

我在开发 iOS 的 Objective-C 时遇到了这个问题,但这应该适用于使用 Mac OS X/iOS 链接器的任何 C/C++/Objective-C 代码。另一个问题涵盖了该解决方案,但我对为什么感兴趣。

假设我正在使用指向定义常量的库的链接。在头文件中有这样的声明:

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?我不应该也需要检查它的地址吗?还是它实际上是一致的,并且我忽略了一些东西?

4

1 回答 1

2

C 标准的相关部分是第 6.3.2.1 节“左值、数组和函数指示符”。以下是关于函数的内容:

函数指示符是具有函数类型的表达式。除非它是运算sizeof65或一元&运算符的操作数,否则类型为“函数返回类型”的函数指示符将转换为类型为“指向函数返回类型的指针”的表达式。

[脚注 65] 因为这种转换不会发生,所以运算符的操作数sizeof仍然是函数指示符,并且违反了 6.5.3.4 中的约束 [ed:6.5.3.4 中的约束说你可能不适sizeof用于函数指示符——它是语义错误]。

命名函数的标识符是最简单的“具有函数类型的表达式”。所以这意味着,如果foo已被声明为函数,则标识符的foo计算结果为指向该函数的指针,除非它是的操作数&(在这种情况下,较大的表达式&foo计算为指向该函数的指针)或sizeof(在这种情况下,较大的表达式sizeof(foo)会引发编译错误)。

tl,dr:Whenfoo是一个函数,foo并且&foo在定义上是等价的。这是函数的特殊规则。这与数组的特殊规则并不完全不同,数组的特殊规则也在许多上下文中“衰减”到指针(该规则是我引用的那一段的上一段)。

旁白:是的,这意味着函数调用运算符始终对指向函数的指针进行操作。Whenpfunc是一个指向函数的变量,(*pfunc)()被处理为好像它读取(&(*pfunc))()... 或只是pfunc().

于 2010-10-12T02:47:40.087 回答