检查 C 中外部定义的标识符是否存在

发布于 2024-09-26 21:26:17 字数 1016 浏览 5 评论 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 的情况下有效呢?难道我不应该也被要求检查它的地址吗?或者它实际上是一致的,而我忽略了一些东西?

I ran into this problem while developing in Objective-C for iOS, but this should apply to any C/C++/Objective-C code using the Mac OS X/iOS linker. The solution is covered by another question, but I'm interested in the why.

Let's say I'm using a linking to a library which defines a constant. In a header file there is a declaration like this:

extern char * const BrandNewIdentifier;

I want to compile my application and run it on a system with an earlier version of the library, where that constant has no definition, so to be safe I don't assume it has been defined.

Now, if there's a function that is only defined in the most recent version of the library, I can do this:

if (BrandNewFunc) {
    BrandNewFunc("foobar", 42);
} else {
    printf("Your system does not support some thing.");
}

Instead of containing the address of function code, BrandNewFunc evaluates to NULL. I would think that the constant would behave the same way, but if I try the same pattern, the app dies while performing a check (throws EXC_BAD_ACCESS on iOS). Specifically:

if (BrandNewIdentifier) ... // blows up here

What works instead is checking the address of the identifier:

if (&BrandNewIdentifier) {
    printf("You got it!");
}

I can see the logic: BrandNewIdentifier has no value, so accessing it should fail. But then why does that syntax work in the case of BrandNewFunc? Shouldn't I be required to check its address also? Or is it actually consistent, and there is something I've overlooked?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

土豪我们做朋友吧 2024-10-03 21:26:17

C 标准的相关部分是第 6.3.2.1 节“左值、数组和函数指示符”。这是它关于函数的说法:

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

[脚注 65] 因为这种转换不会发生,所以 sizeof 运算符的操作数仍然是函数指示符,并且违反了 6.5.3.4 中的约束 [ed:6.5.3.4 中的约束表示:您不能将 sizeof 应用于函数指示符 - 这是一个语义错误]。

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

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

旁白:是的,这意味着函数调用运算符始终对函数指针进行操作。当 pfunc 是函数指针变量时,(*pfunc)() 的处理方式就像读取 (&(*pfunc))() ...或者只是pfunc()

The relevant part of the C standard is section 6.3.2.1 "Lvalues, arrays, and function designators". Here's what it says about functions:

A function designator is an expression that has function type. Except when it is the operand of the sizeof operator65 or the unary & operator, a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.

[footnote 65] Because this conversion does not occur, the operand of the sizeof operator remains a function designator and violates the constraint in 6.5.3.4 [ed: the constraint in 6.5.3.4 says that you may not apply sizeof to a function designator - it's a semantic error].

An identifier that names a function is the simplest sort of "expression that has function type". So what this means is, if foo has been declared as a function, the identifier foo evaluates as a pointer to that function, except when it's the operand of & (in which case the larger expression &foo evaluates as a pointer to that function) or the operand of sizeof (in which case the larger expression, sizeof(foo), provokes a compile error).

tl,dr: When foo is a function, foo and &foo are equivalent by definition. This is a special rule for functions. It's not entirely unlike the special rule for arrays, which also "decay" to pointers in many contexts (that rule is one paragraph up from the one I quoted).

Aside: Yes, this means that the function-call operator always operates on a pointer-to-function. When pfunc is a pointer-to-function variable, (*pfunc)() is processed as if it read (&(*pfunc))() ... or just pfunc().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文