得之我幸 失之我命

when someone abandons you,it is him that gets loss because he lost someone who truly loves him but you just lost one who doesn’t love you.

extern "c" 是什么

今天在看编译报错的时候发现一个神奇的 extern “C”

1
2
3
4
5
6
7
8
#ifdef __cplusplus
extern "C"
{
#endif
...
#ifdef __cplusplus
}
#endif

这是什么?我涉及的明明是 c++ 的工程,而且这个 extern 好像也不是单纯声明用的

首先,__cplusplus 是一个 C++ 规范规定的预定义宏,所有的现代 C++ 编译器都预先定义了它;而所有C语言编译器则不会

why name mangling

C++ 编译器会进行 “名字粉碎”(name mangling):当把一个 C++ 的源文件投入编译的时候,它就开始工作,把每一个在源文件里看到的外部可见的名字粉碎的面目全非,然后存储到二进制目标文件的符号表里

之所以需要这个步骤,是因为 C++ 允许对一个名字给予不同的定义,只要在语义上没有二义性就好。比如函数重载(function overloading),两个函数同名,只要它们的参数列表不同即可;让两个函数的原型声明是完全相同的,只要它们所处的名字空间(namespace)不一样即可

C++ 程序的构造方式仍然继承了 C 语言的传统:编译器把每一个通过命令行指定的源代码文件看做一个独立的编译单元,生成目标文件;然后,链接器通过查找这些目标文件的符号表将它们链接在一起生成可执行程序。编译和链接是两个阶段的事情;事实上,编译器和链接器是两个完全独立的工具。编译器可以通过语义分析知道那些同名的符号之间的差别;而链接器却只能通过目标文件符号表中保存的名字来识别对象。所以,编译器进行名字粉碎的目的是为了让链接器在工作的时候不陷入困惑,将所有名字重新编码,生成全局唯一,不重复的新名字,让链接器能够准确识别每个名字所对应的对象

why linkage specification

但 C 语言却是一门单一名字空间的语言,也不允许函数重载,也就是说,在一个编译和链接的范围之内,C 语言不允许存在同名对象。所以,C语言编译器不需要对任何名字进行复杂的处理(或者仅仅对名字进行简单一致的修饰(decoration),比如在名字前面统一的加上单下划线 _)

但两种语言的编译器对待名字的处理方式是不一致的,这就给链接过程带来了麻烦,为了解决这一问题,C++ 引入了链接规范(linkage specification)的概念,表示法为 extern “language string”,C++ 编译器普遍支持的 “language string” 有 “C” 和 “C++”,分别对应 C 语言和 C++ 语言。链接规范的作用是告诉 C++ 编译器:对于所有使用了链接规范进行修饰的声明或定义,应该按照指定语言的方式来处理,比如名字,调用习惯(calling convention)等等

链接规范仅仅用于修饰函数、变量、函数类型,所以,严格的讲,只应该把这三种对象放置于 extern “C” 的内部。但,即使把 C 语言的其它元素,比如非函数类型定义(结构体,枚举等)放入 extern “C” 内部,也不会带来任何影响,更不用说宏定义预处理指令了

be slow to promise and quick to perform.