使用extern的前向声明(在C / C ++的上下文中)

时间:2017-02-11 02:15:48

标签: c++ c extern forward-declaration linkage

我很困惑是否在C中使用extern来表示函数的前向声明。场景是每个函数都在单独的.c / .cpp文件中。我通过这个问题 - External linkage of const in C理解,如果所有都是.c文件,我不应该使用extern进行前向声明,而不管函数是否在同一个文件中定义。

但是我想知道更多关于这一点的时候明确使用extern进行前向声明(我认为当前向声明的函数是用不同的语言定义而不是调用它时,extern将是必需的),并且任何警告都是意识到了。

3 个答案:

答案 0 :(得分:2)

在C语言中,extern inline具有特殊含义,明确的extern会产生影响。

除此之外,在functon声明中指定明确的extern从来没有任何意义,因为C和C ++中的函数总是通过defualt进行外部链接。

"不同的语言"可能与extern "C"有关,但这与普通extern完全不同。

答案 1 :(得分:2)

  

我认为当前向声明的函数用不同的语言定义而不是调用它时,extern将是必需的。

我应该警告你,你的问题措辞不当。我很确定你对何时使用

感兴趣
extern "C" int foo(int);

在C ++中,但我不确定,我不得不希望我不浪费时间。

让我们区分编译器和链接器。我遗漏了一些细节,但没有任何影响你问题的答案。

编译器使用前向声明。在声明函数时,您将为编译器提供有关如何使用它的信息。声明函数 F ,当编译器在使用 F 时运行时,它知道该怎么做。 (在K& R时代,如果没有声明,编译器会使用默认值,有时会导致热闹的结果。所以现在它们在C和C ++中都是强制性的。)

通常,函数的前向声明是函数原型:它提供参数类型。在C中,这不是绝对必要的。你可以写

int foo();

告诉编译器foo是一个返回int的函数,但不是它需要的参数。因此,编译器无法检查,因此您有责任确保这些正确。

extern声明 - 无论是函数还是变量 - 通知编译器符号及其类型,并说定义将由另一个模块提供。编译器留下占位符以供链接器稍后填充。

例如,如果您查看 getopt (3)手册页,您会看到optarg被声明为extern;它在C运行时库中定义,但您可以使用它,因为它已被声明。

现在我们来到链接器和C ++的extern "C"

对于链接器,模块公开符号。未声明为static的全局变量和函数具有外部链接,这意味着它们可以被其他模块使用。

在C中,函数名和外部符号之间存在1:1的对应关系。名称是符号。例如, getopt (3)在C标准库libc中具有相同名称的符号:

$ nm -g $(find /usr/lib/ -name libc.a) 2>/dev/null | grep 'T getopt' 
0000000000001620 T getopt
0000000000000000 T getopt_long
0000000000000040 T getopt_long_only

在C ++中,名称不是符号。 C ++函数可以重载;同名可以代表采用不同参数的不同功能。编译器构造一个编码参数类型的符号。比较:

$ echo 'int foo(int foo) { return foo * foo; }' > a.c && cc  -c a.c && nm a.o
0000000000000000 T foo
$ echo 'int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm a.o
0000000000000000 T _Z3fooi

符号名称的编码通常称为 name mangling nm (1)具有demangling功能:

$ echo 'int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm -C a.o
0000000000000000 T foo(int)

请注意,参数类型会显示在名称旁边。

在C ++中,extern "C"将函数声明或定义为C函数。这是一个定义:

$ echo 'extern "C" int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm a.o
0000000000000000 T foo
  • 在声明中,它告诉编译器该函数是在别处定义的,并且使用一个C符号。

  • 在定义中,它告诉编译器发出一个C符号,即不破坏名称,以便该函数可以被其他C模块使用。

我希望能回答你的问题。

答案 2 :(得分:1)

默认情况下,函数具有外部链接,这意味着

extern int foo(void);

int foo(void);

具有完全相同的含义。对于所有函数声明都是如此,除了那些以某种方式涉及static的声明。

特别是,extern的存在或不存在对类型检查无效,以及该函数是否以不同的语言定义是无关紧要的。