此代码会导致未定义的行为吗?
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));
}
这是一个程序示例,其中必须在C和C ++中使用inline
函数。如果删除def.c
并且在C中未使用foo
,它会有效吗? (这假设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
的{{1}}定义并将其设为foo
,那么static
将多次出现在foo
中,因为编译器会在每个编译单元中输出它。
答案 0 :(得分:5)
该程序在编写时有效,但需要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
中需要外部定义的任何可能引用,但如果编译器在foo()
中内联main.o
,则它可能不会发出{{1}的任何定义在foo()
中1}},因此仍需要main.o
中的定义来满足def.o
没有优化use.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
中<{1}} 而<{1}}中内联 ,那么您需要在foo()
中内联 {1}}
如果删除了def.c并且在C中没有使用foo,它会工作吗?
是。如果use.o
仅用于C ++文件,则main.o
中不需要def.o
的外部定义,因为foo
包含其自己的(弱)定义或将内联功能。 foo
中的定义仅用于满足来自其他C代码的def.o
的非内联调用。
除此之外:在优化main.o
时,允许C ++编译器跳过为foo.o
生成任何符号,因为C ++标准规定在一个转换单元中声明foo
的函数必须在内联声明在所有翻译单元中,并且要调用声明为foo
的函数,该定义必须在与调用相同的文件中可用。这意味着编译器知道如果某个其他文件想要调用main.o
,则其他文件必须包含inline
的定义,因此当编译其他文件时,编译器将能够根据需要生成函数的另一个弱符号定义(或内联它)。因此,如果inline
中的所有调用都已内联,则无需在foo()
中输出foo()
。
这些是来自C的不同语义,其中foo
中的内联定义可能被编译器忽略,即使main.o
中没有任何内容调用,main.o
中的外部定义也必须存在