【C++】extern
本文介绍一些extern
在C++
中的用法
声明与定义分离
C++程序员应该都知道单一定义规则ODR
在任何一个翻译单元中,只允许存在任何变量、函数、类类型、枚举类型 、概念 (自 C++20 起) 或模板的一个定义(其中一些可能具有多个声明,但只允许一个定义)。 在整个程序(包括任何标准库和用户定义的库)中,需要出现每个非 内联 函数或变量的一个且仅一个定义,这些函数或变量是 odr-使用 的(见下文)。 编译器不要求诊断此违规行为,但违反它的程序的行为是未定义的。
具体来说,如果在头文件中初始化一个变量,在多个源文件包含此头文件,在联合编译时,就会报重定义的错误。
global.h
int global_int = 1;
global1.cc
#include "global.h"
global2.cc
#include "global.h"int main()
{return 0;
}
使用以下命令,联合编译多个文件:
g++ global1.cc global2.cc -o main
结果编译报错:
/tmp/cc4Y38ub.o:(.data+0x0): global_int 的多重定义
/tmp/ccSwS0de.o:(.data+0x0):第一次在此定义
collect2: 错误:ld 返回 1
正确的做法是在头文件中使用extern
声明,在源文件中定义。
global.h
extern int global_int;
globa1.cc
#include "global.h"int global_int = 1;
global2.cc
#include "global.h"
#include <iostream>extern int global_int;int main()
{std::cout << global_int << std::endl;return 0;
}
最后打印的结果为1
.
更一般的做法是,在头文件声明外部变量,在对应的源文件初始化。此时在其他的源文件读取或者修改这个变量,达到跨文件全局变量的效果。在单线程场景下,这种做法十分有效。
以C语言的方式编译函数
在 C++ 里,为支持函数重载和类成员函数等特性,编译器会对函数名、类名等进行修饰,让它们在符号表中保持唯一。可以参考我写的这篇文章:【C++】filt工具的使用
比如刚才的代码:
#include <iostream>int get_int()
{return 1;
}int main()
{std::cout << get_int() << std::endl;return 0;
}
编译后对get_int
提取名字:
0000000000400806 T _Z7get_intv
C语言是没有这个机制的,因为C语言不支持重载,当我们需要将代码打包为C接口供外部调用时,比如【Python】多线程/进程操作C++代码,就需要去掉这种修饰。
此时可以用extern "C"
对函数调用进行包裹。编译器会按C语言的风格编译代码:
#include <iostream>extern "C"
{int get_int(){return 1;}
}int main()
{std::cout << get_int() << std::endl;return 0;
}
此时提取名字:
0000000000400806 T get_int
可以看到,名称修饰已经去掉了。