C++的内部链接、外部链接及inline函数的探讨
2018-01-05 21:11
357 查看
先是几个概念:
编译单元:
首先明确,只有源文件(.cpp/.c)才能被编译器编译。预处理器首先递归包含头文件,形成一个含所有必要信息的单个源文件,此源文件就是一个编译单元。
我可将其理解为被预处理后,包含头文件的.cpp文件。
内部连接:
如果一个编译单元(.cpp)内的名称对编译单元(.cpp)来说是局部的,在链接的时候其他的编译单元无法链接到它且不会与其它编译单元(.cpp)中的同样的名称相冲突。
如被定义为inline的函数(关键字inline须与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声明前不起作用),static函数(关键字static只要放在函数声明前就可以了),还有类(类中定义的函数一般会被处理成inline)。
外部连接:
如果一个编译单元(.cpp)内的名称对编译单元(.cpp)来说不是局部的,而在链接的时候其他的编译单元可以访问它,也就是说它可以和别的编译单元交互。如全局变量,全局函数等。
有个a.cpp:
编辑结果:
由于a.cpp内的两个函数都属于内部连接,即便main.cpp中声明了func()为extern,依然无法连接。
此时,如果main.cpp想使用func(),必须在main.cpp内加上
这样虽然显得很蠢,却也道出了inline函数为什么要在头文件内定义的原因:
1. inline类似与宏,在预编译期间展开。各个.cpp文件包含头文件即是为了定义一份,在其调用之处展开以减少函数调用的开销(调用前先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行)。
如果在.cpp文件中定义,可以。
只不过每个使用这个"inline函数"的.cpp文件都要包含这个.cpp,或者在每个.cpp文件内都定义这么一个"inline函数":这样毫无意义。
2. 在一个a.h文件中定义一个非inline
retAobj函数,供a.cpp main.cpp多个文件调用。编译时不报错而链接时将报错
/tmp/cc0hqPdZ.o: In function `retAobj()':
a.cpp:(.text+0x0): multiple definition of `retAobj()'
/tmp/ccZ5WVA1.o:main.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
这是因为retAobj函数属于外部链接,头文件在两个cpp文件展开后,链接器认为同一个retAobj有两份定义,导致名称冲突而报错。指定为inline后即便名字相同,链接器认为是两个cpp文件内有两个不同的retAobj函数没问题。这是h文件中指定函数inline的另一个原因。一般而言,h文件只提供声明式而非定义(出于降低编译依赖,或者第三方类库隐藏实现等原因),inline则是一个特例。
tips:More Effective C++的条款26提到,
其中非成员函数thePrinter()虽然简短,但不能声明为inline的原因:
static Printer p是个local static对象(local static对象在函数调用时构造;非local static对象在main()开始前就已被构造)
thePrinter()如果是个inline函数,编译器会对该inline展开多份,也导致复制了多份static变量,与初衷违背。
但是经尝试,无论thePrinter()是否inline,都不会导致其内的static Printer p复制多份。最新C++标准规定,extern inline(不写 static 默认就是 extern )里的static变量一定是同一个对象。所以这里的thePrinter()属于external inline。当然了,extern
inline还是inline函数,函数还是会被展开多份而不冲突,只不过里面的static对象确保是同一个。
参考:点击打开链接
编译单元:
首先明确,只有源文件(.cpp/.c)才能被编译器编译。预处理器首先递归包含头文件,形成一个含所有必要信息的单个源文件,此源文件就是一个编译单元。
我可将其理解为被预处理后,包含头文件的.cpp文件。
内部连接:
如果一个编译单元(.cpp)内的名称对编译单元(.cpp)来说是局部的,在链接的时候其他的编译单元无法链接到它且不会与其它编译单元(.cpp)中的同样的名称相冲突。
如被定义为inline的函数(关键字inline须与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声明前不起作用),static函数(关键字static只要放在函数声明前就可以了),还有类(类中定义的函数一般会被处理成inline)。
外部连接:
如果一个编译单元(.cpp)内的名称对编译单元(.cpp)来说不是局部的,而在链接的时候其他的编译单元可以访问它,也就是说它可以和别的编译单元交互。如全局变量,全局函数等。
有个a.cpp:
#include <stdio.h> static void func(void) { printf("call func()\n"); } //inline void func(void) //{ // printf("call func()\n"); //}以及一个main.cpp:
#include<stdio.h> extern void func(void); int main() { func(); return 0; }
编辑结果:
由于a.cpp内的两个函数都属于内部连接,即便main.cpp中声明了func()为extern,依然无法连接。
此时,如果main.cpp想使用func(),必须在main.cpp内加上
#include "a.cpp"
这样虽然显得很蠢,却也道出了inline函数为什么要在头文件内定义的原因:
1. inline类似与宏,在预编译期间展开。各个.cpp文件包含头文件即是为了定义一份,在其调用之处展开以减少函数调用的开销(调用前先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行)。
如果在.cpp文件中定义,可以。
只不过每个使用这个"inline函数"的.cpp文件都要包含这个.cpp,或者在每个.cpp文件内都定义这么一个"inline函数":这样毫无意义。
2. 在一个a.h文件中定义一个非inline
retAobj函数,供a.cpp main.cpp多个文件调用。编译时不报错而链接时将报错
/tmp/cc0hqPdZ.o: In function `retAobj()':
a.cpp:(.text+0x0): multiple definition of `retAobj()'
/tmp/ccZ5WVA1.o:main.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
这是因为retAobj函数属于外部链接,头文件在两个cpp文件展开后,链接器认为同一个retAobj有两份定义,导致名称冲突而报错。指定为inline后即便名字相同,链接器认为是两个cpp文件内有两个不同的retAobj函数没问题。这是h文件中指定函数inline的另一个原因。一般而言,h文件只提供声明式而非定义(出于降低编译依赖,或者第三方类库隐藏实现等原因),inline则是一个特例。
tips:More Effective C++的条款26提到,
class Printer{ ... friend Printer& thePrinter(); ... } Printer& thePrinter() { static Printer p; return p; }
其中非成员函数thePrinter()虽然简短,但不能声明为inline的原因:
static Printer p是个local static对象(local static对象在函数调用时构造;非local static对象在main()开始前就已被构造)
thePrinter()如果是个inline函数,编译器会对该inline展开多份,也导致复制了多份static变量,与初衷违背。
但是经尝试,无论thePrinter()是否inline,都不会导致其内的static Printer p复制多份。最新C++标准规定,extern inline(不写 static 默认就是 extern )里的static变量一定是同一个对象。所以这里的thePrinter()属于external inline。当然了,extern
inline还是inline函数,函数还是会被展开多份而不冲突,只不过里面的static对象确保是同一个。
参考:点击打开链接
相关文章推荐
- C++内部链接与外部链接
- C++编译与链接(2)-浅谈内部链接与外部链接
- C++内部链接和外部链接
- c++ 内部链接 外部链接
- C++编译与链接-浅谈内部链接与外部链接
- C++的内部链接与外部链接问题
- c++ 内部链接 外部链接 分类: 其他 2011-10-05 21:21 2323人阅读 评论(0) 收藏
- c++ 内部链接 外部链接
- c++ 内部链接 外部链接
- C++ 内部链接与外部链接
- const、static、内部链接属性和外部链接属性整理
- 解析C++中的内部连接与外部连接
- const、static、内部链接属性和外部链接属性整理
- 作用域、全局局部变量、链接属性、存储属性、生存期、内部外部函数
- 谷歌重组反向链接分类 内部与外部反链将区分展现
- C++的编译单元、可执行程序、内部连接与外部连接
- 【转帖】内部链接 vs 外部链接
- 解析C++中的内部连接与外部连接
- Python爬虫小实践:获取某个网站所有的外部链接以及内部链接
- 浮点性(float)转化为字符串类型 自定义实现和深入探讨C++内部实现方法