c/c++ 学习总结(3)-- static关键字

被static修饰的内容表示连接性为内部,即static的函数或变量只会在当前翻译单元内部使用,具有内部连接性的名字在编译时就会生成,不需要等到链接时或是运行时,与之相对的是连接为外部的变量和函数(static的反义词可以认为是extern),由于具有外部连接性的名字可能会被多个文件共享使用,根据“单一定义规则ODR”,我们应当保证他们不能重名(函数名和变量名相同也算重名,因为在连接器眼中他们都是强符号),如果重名了,连接器在“符号解析”阶段报错。

需要注意的是通常在函数中定义的非static局部变量是在运行时栈帧上维护的,但static局部变量不是在栈帧上维护的,他们在编译时就确定了,不会等到运行时。

要深入理解static的效果是如何做出来的,需要了解elf文件格式,虚拟存储器的页式管理机制等概念。简单点说,我们通过gcc生成的可执行文件是一个elf(可执行可连接格式)格式的,它里面包含一个“elf头”,“段头部表(又称为程序头部表)”,“节头部表”,“代码段”,“数据段”,可以通过readelf(1)查看详情。当可执行文件被加载器加载时,bash会fork出一个子进程,并通过内核中得“struct vm_area struct”生成对应的“代码段”和“数据段”,并赋予相应的权限,通常是“代码段”为r_x,“数据段”为rw_,x与w两种权限一般是互斥的,这样做是为了限制可执行代码的区域,防止通过缓存区溢出的技巧进行攻击。elf文件中“段头部表”中有程序的“代码段”和“数据段”到虚拟页上相应区域的映射,当程序启动时,物理内存是“冷”的,会产生一种暂时性的异常“缺页”,此时虚拟内存会把相应的页找到并放入物理内存的页框中,由于程序的“局部性”原理,设计良好的程序的工作集(经常使用的页面)往往能驻留在页框中,程序便能良好运行。

在“代码段”中又包含了3个节(section)“.data”表示代码段,“.rodata”只读数据段如c++中const变量,“.init”包含初始化信息;“数据段”中2个节“.data”表示已初始化的全局变量和static变量,“.bss”表示没有初始的全局变量和static变量以及初始化为0的全局变量和是static变量,这个节中得数据不会占用磁盘空间,在程序加载时操作系统统一填充零,需要注意的是在c语言中,目标文件(.o)中的没有初始化的全局变量是放在一个“伪节common”中,因此可以在不同文件中定义多个同名的未初始化全局变量,在链接时链接器会随机挑选一个,这种特性可能会带来潜在的隐患,可以通过选项“-fno-common”,来关闭它。

知道了以上这些内幕,应该就能理解为什么static变量和其他变量不相同。
回到c++中static变量,类中声明的static成员变量和成员函数,是类对象共享的,它不依附某一个对象,可以直接通过“classname::”调用,当然也可以通过“对象::”调用,它的初始化要在类外进行,需要加上"classname::",但不能加上“static”;static成员函数不能调用非static成员函数,因为static成员函数没有隐式的“this”指针,但是非static成员函数有“this”指针;const也不能放在函数最后来修饰static成员函数,原因也是因为static函数没有“this”指针。

关于static等与作用域相关的关键字的理解,可以参看csapp一书中“链接”章节。

你可能感兴趣的:(c++)