使用extern转发声明(在C/C++上下文中)

使用extern转发声明(在C/C++上下文中),c++,c,extern,forward-declaration,linkage,C++,C,Extern,Forward Declaration,Linkage,我对是否在C中使用extern来转发函数声明感到困惑。场景是每个函数都在单独的.C/.cpp文件中。通过讨论这个问题,我了解到,如果所有文件都是.c文件,那么无论函数是否定义在同一个文件中,我都不应该使用extern进行转发声明 但是我想知道更多关于何时显式使用extern进行前向声明的信息。我认为,当被前向声明的函数使用不同于调用函数的语言定义时,需要使用extern,并且需要注意任何注意事项。函数默认具有外部链接,这意味着 extern int foo(void); 及 意思完全相同。这适

我对是否在C中使用extern来转发函数声明感到困惑。场景是每个函数都在单独的.C/.cpp文件中。通过讨论这个问题,我了解到,如果所有文件都是.c文件,那么无论函数是否定义在同一个文件中,我都不应该使用extern进行转发声明


但是我想知道更多关于何时显式使用extern进行前向声明的信息。我认为,当被前向声明的函数使用不同于调用函数的语言定义时,需要使用extern,并且需要注意任何注意事项。

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

extern int foo(void);

意思完全相同。这适用于所有函数声明,但以某种方式涉及静态的函数声明除外


特别是,是否存在extern对类型检查没有影响,函数是否用其他语言定义也无关紧要。

在C语言中,extern inline有一个特殊的含义,显式的extern起着作用

除此之外,在函数语句中指定外显式的外显式,没有任何意义,因为C和C++中的函数总是通过DuFalTal.

具有外部链接。 不同语言的问题可能与诸如外部C之类的东西有关,但这与普通外部完全不同

我认为,当正向声明的函数是用不同于调用函数的语言定义时,需要使用extern

我应该警告你,你的问题措词不当。我很确定你对什么时候使用感兴趣

extern "C" int foo(int);
在C++中,但我不确定,我希望我不是在浪费时间。 让我们区分编译器和链接器。我省略了一些细节,但没有影响你问题的答案

编译器使用正向声明。在声明函数时,您将向编译器提供有关如何使用该函数的信息。声明函数F,当编译器在使用的F上运行时,它知道该做什么。在K&R时代,在没有声明的情况下,编译器使用默认值,有时会导致有趣的结果。所以现在它们在C和C++中都是强制性的。 通常,函数的前向声明是函数原型:它提供参数类型。在C语言中,这不是严格必要的。你可以写信

int foo();
它告诉编译器foo是一个返回int的函数,但不是它需要什么参数。这就成为了你的责任,让这些权利,因为编译器无法检查

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

例如,如果您查看getopt3手册页,您将看到optarg被声明为extern;它是在C运行时库中定义的,但是您可以使用它,因为它已声明

现在我们来到了链接器,C++的外部C。p> 模块向链接器公开符号。未声明为静态的全局变量和函数具有外部链接,这意味着它们可以被其他模块使用

在C语言中,函数名和外部符号之间有1:1的对应关系。名称就是符号。例如,getopt3在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
符号名称的编码通常称为名称混乱。nm1有一个需求特性:

$ echo 'int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm -C a.o
0000000000000000 T foo(int)
请注意,参数类型显示在名称旁边

在C++中,ExtEngCalor将函数声明或定义为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模块可以使用该函数


我希望这能回答你的问题

函数默认为extern,您可以根据需要在转发声明中使用或省略此关键字,其含义不会改变。@n.m.使用extern,编译器会忽略签名,我的意思是没有进行类型检查。因此,不需要时不应使用它。编译器忽略签名错误。谁告诉你的?@n.m.对不起,我误解了……当你在c文件中使用extern进行正向声明时,编译器会忽略签名感知问题。编译器实际上完全按照您的要求执行,但是您的外部声明可能与另一个文件中的实际函数声明不匹配。汇编
er不检查,链接器也不检查。这就是为什么我们将前向声明放在头文件中,而不是在c文件中随机散布它们。也就是说:c文件中的extern是否修改了degration,还是默认值?为什么人们一直试图这么做!谢谢,这让我很困惑。我混合了extern函数声明和extern C的用法,想知道extern何时影响wrt函数声明。块范围中的函数声明是否有区别:即void f{extern void g;}?谢谢你写了这样一个完整的答案。我正在编写一些嵌入代码,并有一个从汇编调用的C++函数,这有帮助!
$ echo 'extern "C" int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm a.o
0000000000000000 T foo