8

此代码会导致未定义的行为吗?

header.h

#ifdef __cplusplus
extern "C"
{
#endif

inline int foo(int a)
{
    return a * 2;
}

#ifdef __cplusplus
}
#endif

def.c

#include "header.h"

extern inline int foo(int a);

use.c

#include "header.h"

int bar(int a)
{
    return foo(a + 3);
}

main.cpp

#include <stdio.h>
#include "header.h"

extern "C"
{
    int bar(int a);
}

int main(int argc, char** argv)
{
    printf("%d\n", foo(argc));
    printf("%d\n", bar(argc));
}

inline这是一个必须在 C 和 C++ 中都使用函数的程序示例。如果def.c被删除并且foo没有在 C 中使用,它会起作用吗?(这是假设 C 编译器是 C99。)

此代码在编译时有效:

gcc -std=c99 -pedantic -Wall -Wextra -c -o def.o def.c
g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp
gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c
g++ -std=c++11 -pedantic -Wall -Wextra -o extern_C_inline def.o main.o use.o

foo只出现extern_C_inline一次,因为编译器在不同目标文件中输出的不同版本被合并,但我想知道这种行为是否由标准指定。如果我删除并使其extern定义,那么它将多次出现,因为编译器在每个编译单元中输出它。foostaticfooextern_C_inline

4

1 回答 1

8

该程序在编写时是有效的,但def.c需要确保代码始终适用于所有编译器以及不同文件的优化级别的任何组合。

因为上面有一个声明,extern所以def.c提供了函数的外部定义foo(),您可以通过以下方式确认nm

$ nm def.o
0000000000000000 T foo

def.o无论该文件如何编译,该定义都将始终存在。

其中use.c有 的内联定义foo()但根据 C 标准中的 6.7.4,未指定调用是foo()使用该内联定义还是使用外部定义(实际上是否使用内联定义取决于文件是否经过优化或不)。如果编译器选择使用内联定义,它将起作用。如果它选择不使用内联定义(例如,因为它是在没有优化的情况下编译的),那么您需要在其他文件中进行外部定义。

没有优化use.o有一个未定义的引用:

$ gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c
$ nm use.o
0000000000000000 T bar
                 U foo

但是通过优化它不会:

$ gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c -O3
$ nm use.o
0000000000000000 T bar

其中main.cpp会有一个定义,foo()但它通常会生成一个弱符号,因此如果在另一个对象中找到另一个定义,链接器可能不会保留它。如果弱符号存在,它可以满足任何use.o需要外部定义的可能引用 in ,但如果编译器内联foo()inmain.o那么它可能不会发出任何foo()in定义,因此仍然需要main.o定义 in来满足def.ouse.o

没有优化main.o包含一个弱符号:

$ g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp
$ nm main.o
                 U bar
0000000000000000 W foo
0000000000000000 T main
                 U printf

但是使用内联编译main.cpp调用-O3并且foo编译器不会为它发出任何符号:

$ g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp -O3
$ nm main.o
                 U bar
0000000000000000 T main
                 U printf

因此,如果foo()不是use.o而是联,main.o那么您需要外部定义def.o

如果 def.c 被删除并且 foo 没有在 C 中使用,它会起作用吗?

是的。如果foo仅在 C++ 文件中使用,则不需要 in 的外部定义,foo因为def.o要么main.o包含其自己的(弱)定义,要么将内联函数。仅需要满足对来自其他 C 代码foo.o的非内联调用的定义。foo


另外:C++ 编译器允许foo在优化时跳过生成任何符号,main.o因为 C++ 标准规定,inline在一个翻译单元中声明的函数必须在所有翻译单元中内联声明,并且要调用声明的函数inline,定义必须在与调用相同的文件。这意味着编译器知道如果某个其他文件要调用foo(),那么该其他文件必须包含 的定义foo(),因此当编译该其他文件时,编译器将能够生成该函数的另一个弱符号定义(或内联它)如所须。foo因此,main.o如果所有调用main.o都已内联,则无需输出。

这些是与 C 不同的语义,其中的内联定义use.c可能会被编译器忽略,而外部定义def.o必须存在,即使 in 中没有任何内容def.c调用它。

于 2014-10-29T21:01:09.877 回答