C 内联函数:static inline 和 extern inline 的含义

C 内联函数:static inline 和 extern inline 的含义

一,前言  
  Linux之父linus曾说过:“static inline” means “we have to have this function, if you use it, but don’t inline it, then make a static version of it in this compilation unit”. “extern inline” means “I actually have an extern for this function, but if you want to inline it, here’s the inline-version”. “诚如神之所说”,这句话已经深入浅出的区别了extern、static、inline三个关键的区别。
理解这句话:static inline首先我们必须认识到,这是一个static函数,也即函数是文件内链接的,定义放在头文件,即使多处包含也不会出现重定义问题,如果你将它放进源文件反而会出错,因为这时其他文件看不到定义,所以必须定义在头文件,然后在调用处将会做内敛展开。而extern inline首先函数是extern的,也就是说函数可能是从其他文件引入的,如果本文件看不到定义,则只会做普通函数调用,如果能看到定义,则可以尝试inline展开。

二、相关背景知识
1、源文件编译->符号表->目标文件的链接
  在C/C++程序中,是以源文件为单位编译的,每个源文件会被编译成成一个目标文件,目标文件中的变量或函数会生成一张符号表,这些符号不仅包括本文件中定义的函数或变量,还包括外部文件定义的函数或变量,符号对应的变量或函数地址将会在链接时一个符号决议的过程中确定,这里需要说明的是,每个符号带有一个链接属性,链接属性分三种,如下:

外部链接(ExternalLinkage):具有External Linkage的标识符编译后在符号表中是GLOBAL的符号;
内部链接(InternalLinkage):具有Internal Linkage的标识符编译后在符号表中是LOCAL的符号;
无链接(NoLinkage):除以上情况之外的标识符都属于No Linkage的,例如函数的局部变量,以及不表示变量和函数的其它标识符。

2、C/C++变量内存分布
  在C/C++中内存分为:常量存储区域、全局/静态存储区、堆区、栈区、自由存储区,不同的变量或数据存储在不同的区域,其中全局变量和静态变量存储在静态存储区,函数参数等变量存储在栈区,new/delete的变量在自由存储区,free/molloc的变量存储在堆区(堆内存和自由存储区,其实一般来讲是同一块内存区域,只不过堆是操作系统层面给出的概念,而自由存储区才是语言标准提供的概念,实际上目前多数编译器的实现上自由存储区就是堆区)。

可执行文件中分段结构为:

.text: 也称为代码段(Code),用来存放程序执行代码,同时也可能会包含一些常量(如一些字符串常量等)。该段内存为静态分配,只读(某些架构可能允许修改)。 这块内存是共享的,当有多个相同进程(Process)存在时,共用同一个text段。
.data: 也有的地方叫GVAR(global value),用来存放程序中已经初始化的非零全局变量。静态分配。".data"又可分为读写(RW)区域和只读(RO)区域。
-> RW段则是普通非常全局变量,静态变量就在其中

-> RO段保存常量所以也被称为.constdata

.bss: 存放程序中为未初始化的和零值全局变量。静态分配,在程序开始时通常会被清零。(从可执行文件的角度来讲,如果一个数据未被初始化那就不需要为其分配空间,所以.data和.bss一个重要的区别就是.bss并不占用可执行文件的大小,它只是记载需要多少空间来存储这些未初始化数据,而不分配实际的空间)
Stack: 栈,存放Automatic Variables,按内存地址由高到低方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。
Heap: 堆,自由申请的空间,按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。 (每个线程都会有自己的栈,但是堆空间是共用的)
在这里插入图片描述
三、static, extern&inline表示的含义
1、static、extern与inline修饰的含义
  static: 该关键字修饰的全局变量或函数具有内链接属性,所以不可被其他文件引用,所以好处就是即使外部文件具有同名函数或变量也不会发生重命名冲突。此外,当static修饰函数内的局部变量的时候,变量在GVAR内存区开辟内存,也即即使函数执行完成,堆栈释放而static变量不会被释放,但需要注意的是即使变量在全局区,但是可见域并没有改变,只有函数内可见。

extern:该关键子字修饰全局变量或内存具有外链接属性,通过外部文件声明可以在外部文件引用,一般情况下的函数或全局变量,默认为extern属性。

inline:inline其实和链接属性没有直接的关系,且只用来修饰函数,经过inline修饰的函数,实时上是对编译器做代码展开建议,如果代码复杂度较高,inline并不会生效。因为内联函数要在调用点展开,所以编译器必须随处可见内联函数的定义,要不然就成了普通函数的调用了,所以将内联函数的定义放在头文件里实现是合适的,省却你为每个文件实现一次的麻烦。如果你真的打算每个文件里都实现一次该内联函数的话,那么,最好保证每个定义都是一样的,否则行为是未定义的。

Q:既然有可能inline函数产生与普通函数相似的调用效果,那么会在符号表产生符号吗?在头文件定义会出现重定义吗?

A:会产生符号,函数均会产生符号,但是默认是内链接属性的符号,所以不会重定义。

const:const修饰变量属性是内链接的,也即在头文件定义不会造成重定义,但可以通过extern 双重修饰,是变量具有外链接属性,此时定义不能放在头文件(避免重定义),const也可以用来修饰类的成员函数,此时类不能修改对象的内容。

2、inline与宏的区别
  优点:

1)inline定义的内联函数,函数代码被放入符号表中,在使用时进行替换(像宏一样展开),效率很高。

2)类的内联函数也是函数。编绎器在调用一个内联函数,首先会检查参数问题,保证调用正确,像对待真正函数一样,消除了隐患及局限性。

3)inline可以作为类的成员函数,刀可以使用所在类的保护成员及私有成员。

缺点:

内联函数以复制为代价,活动产函数开销

1) 如果函数的代码较长,使用内联将消耗过多内存

2) 如果函数体内有循环,那么执行函数代码时间比调用开销大。

猜你喜欢

转载自blog.csdn.net/qq_46144237/article/details/107396595