Dev*_*Bee 0 c c++ extern forward-declaration linkage
我很困惑是否使用 extern 来向前声明 C 中的函数。情况是每个函数都位于单独的 .c/.cpp 文件中。我通过这个问题-External links of const in C了解到,如果所有都是 .c 文件,我不应该使用 extern 进行前向声明,无论该函数是否在同一文件中定义。
但我想更多地了解何时显式使用 extern 进行前向声明(我认为当前向声明的函数是用与调用函数不同的语言定义时,需要 extern),以及需要注意的任何警告。
我认为当前向声明的函数是用与调用函数不同的语言定义的时,需要 extern 。
我应该警告你,你的问题措辞很糟糕。我很确定您对何时使用感兴趣
extern "C" int foo(int);
Run Code Online (Sandbox Code Playgroud)
在 C++ 中,但我不确定,我希望我没有浪费时间。
让我们区分一下编译器和链接器。我省略了一些细节,但不会影响您问题的答案。
编译器使用前向声明。在声明函数时,您向编译器提供有关如何使用该函数的信息。您声明函数F,当编译器运行时遇到正在使用的F时,它知道要做什么。(在 K&R 时代,在没有声明的情况下,编译器使用默认值,有时会导致搞笑的结果。所以现在它们在 C 和 C++ 中都是强制性的。)
通常函数的前向声明是函数原型:它提供参数类型。在 C 语言中,这并不是绝对必要的。你可以写
int foo();
Run Code Online (Sandbox Code Playgroud)
它告诉编译器foo是一个返回 的函数int,但不告诉编译器它需要什么参数。然后你就有责任确保这些正确,因为编译器无法检查。
声明extern(无论是函数还是变量)通知编译器符号及其类型,并表示定义将由另一个模块提供。编译器留下一个占位符供链接器稍后填充。
例如,如果您查看getoptoptarg (3) 手册页,您将看到声明extern;它是在 C 运行时库中定义的,但您可以使用它,因为它已被声明。
现在我们来看看链接器,以及extern "C"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
Run Code Online (Sandbox Code Playgroud)
在 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
Run Code Online (Sandbox Code Playgroud)
符号名称的编码通常称为名称修改。 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)
Run Code Online (Sandbox Code Playgroud)
请注意,参数类型出现在名称旁边。
在 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
Run Code Online (Sandbox Code Playgroud)
在声明中,它告诉编译器该函数是在其他地方定义的,并使用C符号。
在定义中,它告诉编译器发出一个 C 符号,即不要破坏名称,以便该函数可以被其他 C 模块使用。
我希望这能回答你的问题。
| 归档时间: |
|
| 查看次数: |
5233 次 |
| 最近记录: |