3

我最近在升级 debian 测试并获得 gcc 6.2.1 编译器后编译一段代码时遇到了问题。我把它归结为这个简单的例子:

inline int func(void) {
    return 0;
}

int main (int argc, char **argv) {
    func();
}

该代码无法使用以下内容进行编译:

gcc -o exec code.c # gcc 6.2.1

它失败了:

undefined reference to 'func'

我也在同一台主机上尝试过使用 gcc 4.8、4.9 和 5,但都失败了。如果我添加它会编译:

gcc -o exec code.c -O2 # gcc 6.2.1

我真的很好奇为什么它可以与 -O2 标志一起使用,但并非没有,我希望这可以工作?

4

1 回答 1

10

在编译器命令中添加“-O”选项。仅当启用优化时才启用内联。

C99 内联函数

默认情况下,Clang 在 GNU C11 模式下构建 C 代码,因此它对 inline 关键字使用标准 C99 语义。这些语义不同于 GNU C89 模式中的语义,后者是 5.0 之前的 GCC 版本中的默认模式。例如,考虑以下代码:

inline int add(int i, int j) { return i + j; }

int main() {
  int i = add(4, 5);
  return i;
}

在 C99 中,内联意味着一个函数的定义仅用于内联,并且在程序的其他地方还有另一个定义(没有内联)。这意味着这个程序是不完整的,因为如果 add 没有内联(例如,在没有优化的情况下编译时),那么 main 将有一个未解析的对另一个定义的引用。因此,我们将得到一个(正确的)链接时错误,如下所示:

Undefined symbols:
  "_add", referenced from:
      _main in cc-y1jXIr.o

相比之下,GNU C89 模式(在旧版本的 GCC 中默认使用)是 C89 标准加上许多扩展。C89 没有 inline 关键字,但 GCC 将其识别为扩展,并将其视为对优化器的提示。

有几种方法可以解决这个问题:

  1. 更改添加到静态内联函数。如果只有一个翻译单元需要使用该功能,这通常是正确的解决方案。静态内联函数总是在翻译单元中解析,因此您不必在程序的其他地方添加函数的非内联定义。
  2. 从此 add 的定义中删除 inline 关键字。内联函数不需要 inline 关键字,也不保证一定会这样。一些编译器完全忽略它。Clang 将其视为程序员的温和建议。
  3. 在程序的其他地方提供 add 的外部(非内联)定义。这两个定义必须是等价的!
  4. 通过将 -std=gnu89 添加到 Clang 选项集以编译 GNU C89 方言。仅当程序源无法更改或程序还依赖于无法更改的其他 C89 特定行为时,才建议使用此选项。

所有这些仅适用于 C 代码;C++ 中 inline 的含义与其在 GNU89 或 C99 中的含义大不相同。

于 2016-12-19T08:28:58.383 回答