【问题标题】:Checking for the existence of externally defined identifiers in C检查 C 中是否存在外部定义的标识符
【发布时间】:2010-10-13 05:14:36
【问题描述】:

我在使用 iOS 的 Objective-C 开发时遇到了这个问题,但这应该适用于使用 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 的情况下有效?我不应该也需要检查它的地址吗?还是它实际上是一致的,而我忽略了一些东西?

【问题讨论】:

  • 我认为这通常不适用于 C,因为它依赖于共享库的实现。对于静态链接的库,您只会遇到链接器错误。
  • 很公平,我已经编辑了问题。

标签: c darwin weak-linking


【解决方案1】:

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

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

[脚注 65] 因为没有发生这种转换,sizeof 运算符的操作数仍然是一个函数指示符,并且违反了 6.5.3.4 中的约束 [ed:6.5.3.4 中的约束说你可能不应用 @ 987654324@ 到函数指示符 - 这是一个语义错误]。

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

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

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

【讨论】:

  • 所以你是说BrandNewFunc&BrandNewFunc 一样?数组是怎样的? BrandNewIdentifier是第一个字节的地址; &BrandNewIdentifier是第一个字节的地址。
  • 我的意思是,数组对指针会“衰减”,函数也是如此;你不能通过值传递它们中的任何一个。确实BrandNewIdentifier的类型和&BrandNewIdentifier的类型不一样,如果BrandNewIdentifier是一个数组的话;对于函数而言并非如此,BrandNewFunc&BrandNewFunc 的类型在需要指向函数的上下文中使用时是相同的。
  • 您能否编辑您的问题以解释您所说的“上下文”是什么意思?据我所知,C 中的表达式不会根据上下文改变含义,这适用于像 Perl 这样的疯狂语言。
  • 我已经编辑了我的答案。在大多数情况下,您的“据我所知”是正确的,但是数组和函数有特殊规则,就此而言,“通常的算术转换”可以理解为根据上下文更改表达式的含义,也是。
  • 啊哈!所以函数标识符是个例外。为了保持一致,我也应该在检查时使用 &BrandNewFunc。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-10
  • 2016-08-25
  • 1970-01-01
  • 1970-01-01
  • 2020-02-27
  • 1970-01-01
相关资源
最近更新 更多