13

当我尝试在类接口上方的头文件中定义 C 函数时,总是会遇到构建错误。

但是当我在实现文件中做同样的事情并在标题中给出一个声明时。事情解决了。

我想知道,为什么会这样,因为我在头文件中定义了枚举、结构、常量 NSStrings,那么为什么不是 C 函数呢?

4

1 回答 1

24

这与 C 链接器(或链接编辑器)的工作方式有关。当 C 编译器遇到函数定义时,它会准备实现该函数的汇编程序代码,并用一个符号标记它,该符号对链接器说“这是具有此名称的函数开始的地方”。该符号通常用下划线命名,后跟函数名,例如_printf

如果您在头文件中定义函数,则导入此头文件的每个.c.m文件都将编译该函数,并导致编译器发出相同的符号。链接器希望只找到每个符号的一个实例,因此这是一个错误。

这与守卫的存在无关#include,也与使用#import而不是#include. C 编译器在单独的翻译单元上工作——这意味着单独的源文件。预处理器策略会阻止您将相同的头文件两次包含到单个源文件中,但不会对跨多个文件的活动进行协调。这意味着在不同的源文件中包含相同的头文件是有效的:这也意味着当您编译不同的文件时,它们可以(合法地)包含相同的符号。

链接编辑器的工作是将这些文件放在一起,解决对编译时未知符号的任何引用。如果您尝试将具有相同符号的对象(已编译和组装的翻译单元的名称)链接到同一存档、共享库或可执行文件,那么您将在此处看到错误。

解决方案:

  • 不要在头文件中定义函数,只需在其中声明并在实现文件中定义即可;正如您已经发现这有效。
  • 在标头中定义函数,但仅在代码中的一处包含该标头。由于设计原因,这通常是不可接受的。
  • 使用修饰符在标题中定义函数inline。内联函数只是由编译器复制到调用它们的函数中,因此永远不会为它们发出链接器符号。这有它自己的权衡,您可能希望阅读更多关于.
于 2012-04-20T08:06:31.660 回答