使用extern的正向声明(在C/C++的上下文中)
forward declaration using extern (in context of C/C++)
我不知道是否使用extern对C中的函数进行前向声明。场景是每个函数都在单独的.C/.cpp文件中。我从C中const的外部链接这个问题中了解到,如果所有文件都是.C文件,无论函数是否在同一文件中定义,我都不应该使用extern进行前向声明。
但我想知道更多关于这一点的信息,何时显式地使用extern进行正向声明(我认为,当正向声明的函数是用与调用不同的语言定义的时,将需要extern),以及需要注意的任何注意事项。
我认为,当正向声明的函数是用不同于调用语言的语言定义的时,将需要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
符号名称的编码通常被称为名称篡改nm(1)有一个去映射特征:
$ 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模块可以使用该函数。
我希望这能回答你的问题。
在C语言中,有extern inline
这样的东西,它有着特殊的含义,而显式的extern
在其中起着重要作用。
除此之外,在函数声明中指定显式extern
毫无意义,因为C和C++中的函数总是通过defalt进行外部链接。
"不同的语言"可能是关于extern "C"
之类的东西,但这与普通的extern
是完全不同的结构。
函数默认具有外部链接,这意味着
extern int foo(void);
和
int foo(void);
具有完全相同的含义。对于所有函数声明都是如此,但以某种方式涉及static
的声明除外。
特别是,extern
的存在与否对类型检查没有任何影响,函数是否用不同的语言定义也无关紧要。
- #为""定义宏;静态";针对不同的上下文
- 与互斥锁相比,旋转锁可以保证上下文切换
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- 为什么我不能使用 EGL 创建无头 OpenGl 上下文?
- Visual Studio(或任何其他工具)能否将地址解释为调用堆栈(boost上下文)的开头
- 为什么在逗号分隔符上下文中将预增量的结果强制转换为void
- 错误"Could not find Boost"(缺少:上下文标头)
- 使用 mod_gsoap 部署服务时,如何在 Gsoap 中更改 soap 上下文的模式?
- 将有状态的 lambda 传递到 C 样式函数中,而无需上下文参数
- 安排带有上下文的协同程序
- 我的文件无法正常工作,无法从C++文件中读取上下文
- DCMTK 了解"DIMSE 没有有效的演示上下文 ID"错误
- 具有std::initializer_list参数的非成员函数(/non构造函数上下文)的重载解析
- antlr 规则上下文是否可以独立于目标
- OpenGL 调试上下文警告 -"将使用视频内存作为缓冲区异议的来源
- MSVC 在不知道类型的情况下评估上下文(和错误)
- 为什么 Perf 具有如此高的上下文切换?
- 非推导上下文,如标准库中的"boost::mpl::identity:<T>:type"?
- 使用 std::size 来自非 const 上下文
- 如何使用IExecuteCommand和动词在上下文菜单外壳扩展中显示本地化文本和自定义图标?