存储类、作用域、生命周期、链接属性的关系

---linux下C程序的内存映像
代码段、只读数据段
( 1 )对应着程序中的代码(函数),代码段在linux中又叫文本段(.text)
( 2 )只读数据段就是在程序运行期间只能读不能写的数据, const 修饰的常量有可能是存在只读数据段的(但是不一定, const 常量的实现方法在不同平台是不一样的)
数据段、bss段
( 1 )数据段存: 1 、显式初始化为非 0 的全局变量; 2 、显式初始化为非 0 static 局部变量
( 2 )bss段存: 1 、显式初始化为 0 或者未显式初始化的全局变量; 2 、显式初始化为 0 或未显式初始化的 static 局部变量。

---OS下和裸机下C程序加载执行的差异
( 1 )C语言程序运行时环境有一定要求,意思是单独个人写的C语言程序没法直接在内存中运行,需要外部一定的协助,这段协助的代码叫加载运行代码(或者叫构建C运行时环境的代码,这一段代码在操作系统下是别人写好的,会自动添加到我们写的程序上,这段代码的主要作用是:给全局变量赋值、清bss段)。
( 2 )ARM裸机第十六部分,写shell时有一次定义了一个全局变量初始化为 0 但是实际不为 0 ,后来在裸机的start.S中加了清bss段代码就变 0 了。这就说明在裸机程序中没人帮我们来做这一段加载运行时代码,要程序员自己做(start.S中的重定位和清bss段就是在做这个事);在操作系统中运行程序时程序员自己不用操心,会自动完成重定位和清bss,所以我们看到的现象:C语言中未初始化的全局变量默认为 0 ·····。
( 3 )数据段的全局变量或静态局部变量都是有非 0 的初值的,这些初值在main函数运行之前就已经被初始化了,是重定位期间完成的初始化。

---static
( 2 ) static 的第一种用法是:用来修饰局部变量,形成静态局部变量。要搞清楚静态局部变量和非静态局部变量的区别。本质区别是存储类不同(存储类不同就衍生出很多不同):非静态局部变量分配在栈上,而静态局部变量分配在数据段 / bss段上。
( 3 ) static 的第二种用法是:用来修饰全局变量,形成静态全局变量。要搞清楚静态全局变量和非静态全局变量的区别。区别是在链接属性上不同,讲到链接属性时详细讲。
分析:
     1 、静态局部变量在存储类方面和全局变量一样。
     2 、静态局部变量在生命周期方面和全局变量一样。
     3 、静态局部变量和全局变量的区别是:作用域、连接属性。静态局部变量作用域是代码块作用域(和普通局部变量是一样的)、链接属性是无连接;全局变量作用域是文件作用域(和函数是一样的)、链接属性方面是外连接。

---static 的第二种用法:修饰全局变量和函数
( 1 )普通的(非静态)的函数 / 全局变量,默认的链接属性是外部的
( 2 ) static (静态)的函数 / 全局变量,链接属性是内部链接。


--- register
( 1 ) register 修饰的变量。编译器会尽量将它分配在寄存器中。(平时分配的一般的变量都是在内存中的)。分配在寄存器中一样的用,但是读写效率会高很多。所以 register 修饰的变量用在那种变量被反复高频率的使用,通过改善这个变量的访问效率可以极大的提升程序运行效率时。所以 register 是一种极致提升程序运行效率的手段。
( 2 )uboot中用到了一个 register 类型的变量,gd这个变量是用来存uboot的全局变量(gd就是global data)。因为这个全局变量在整个uboot中到处都被访问,所以定义成 register 的。
( 3 )平时写代码要被定义成 register 这种情况很少,一般慎用。
( 4 ) register 编译器只能承诺尽量将 register 修饰的变量放在寄存器中,但是不保证一定放在寄存器中。主要原因是因为寄存器数量有限,不一定有空用。

---extern
( 1 ) extern 主要用来声明全局变量,声明的目的主要是在a.c中定义全局变量而在b.c中使用该变量。

---volatile
( 1 ) volatile 的字面意思:可变的、易变的。譬如在中断处理程序isr中更改了这个变量的值,譬如多线程中在别的线程更改了这个变量的值,譬如硬件自动更改了这个变量的值(一般这个变量是一个寄存器的值)
( 2 )以上说的三种情况(中断isr中引用的变量,多线程中共用的变量,硬件会更改的变量)都是编译器在编译时无法预知的更改,此时应用使用 volatile 告诉编译器这个变量属于这种(可变的、易变的)情况。编译器在遇到 volatile 修饰的变量时就不会对改变量的访问进行优化,就不会出现错误。
(3 ) volatile 是程序员意识到需要 volatile 然后在定义变量时加上 volatile ,如果你遇到了应该加 volatile 的情况而没有加程序可能会被错误的优化。如果在不应该加 volatile 而加了的情况程序不会出错只是会降低效率。所以我们对于 volatile 的态度应该是:正确区分,该加的时候加不该加的时候不加,如果不能确定该不该加为了保险起见就加上。

---restrict
( 1 )c99中才支持的,所以很多延续c89的编译器是不支持restrict关键字,gcc支持的。
( 2 )restrict也是和编译器行为特征有关的。
( 3 )restrict只用来修饰指针,不能修饰普通变量。
( 4 )http : //blog.chinaunix.net/uid-22197900-id-359209.html
( 5 )memcpy和memmove的区别

---函数名和全局变量的文件作用域
( 1 )文件作用域的意思就是全局的访问权限,也就是说整个.c文件中都可以访问这些东西。这就是平时所说的局部和全局,全局就是文件作用域。
( 2 )详细准确的说:函数和全局变量的作用域是定义所在的整个.c文件之内定义式之后的部分。

---同名变量的掩蔽规则
( 2 )首先,如果两个同名变量作用域不同且没有交叠,这种情况下同名没有任何影响。
( 3 )其次,如果两个同名变量作用域有交叠,C语言规定在作用域交叠范围内,作用域小的一个变量会掩蔽掉作用域大的那个(县官不如现管)。

---编译以文件为单位、链接以工程为单位
( 1 )编译器工作时是将所有源文件依次读进来,单个为单位进行编译的。
( 2 )链接的时候实际上是把第一步编译生成个单个的.o文件整体的输入,然后处理链接成一个可执行程序。
---三种链接属性:外连接、内链接、无链接
( 1 )外连接的意思就是外部链接属性,也就是说这家伙可以在整个程序范围内(言下之意就是可以跨文件)进行链接,譬如普通的函数和全局变量属于外连接。
( 2 )内链接的意思就是(c文件内部)内部链接属性,也就是说这家伙可以在当前c文件内部范围内进行链接(言下之意就是不能在当前c文件外面的其他c文件中进行访问、链接)。 static 修饰的函数 / 全局变量属于内链接。

(3)无连接的意思就是这个符号本身不参与链接,它跟链接没关系。所有的局部变量(auto的、static的)都是无连接的


总结
(1)普通(自动)局部变量分配在栈上,作用域为代码块作用域,生命周期是临时,连接属性为无连接。定义时如果未显式初始化则其值随机,变量地址由运行时在栈上分配得到,多次执行时地址不一定相同,函数不能返回该类变量的地址(指针)作为返回值。
(2)静态局部变量分配在数据段/bss段(显式初始化为非0则在数据段,显式初始化为0或未显示初始化则在bss段),作用域为代码块作用域(人为规定的),生命周期为永久(天然的),链接属性为无连接(天然的)。定义时如果未显式初始化则其值为0(天然的),变量地址由运行时环境在加载程序时确定,整个程序运行过程中唯一不变;静态局部变量其实就是作用域为代码块作用域(同时链接属性为无连接)的全局变量。静态局部变量可以改为用全局变量实现(程序中尽量避免用全局变量,因为会破坏结构性)。
(3)静态全局变量/静态函数和普通全局变量/普通函数的唯一差别是:static使全局变量/函数的链接属性由外部链接(整个程序所有文件范围)转为内部链接(当前c文件内)。这是为了解决全局变量/函数的重名问题(C语言没有命名空间namespace的概念,因此在程序中文件变多之后全局变量/函数的重名问题非常严重,将不必要被其他文件引用的全局变量/函数声明为static可以很大程度上改善重名问题,但是仍未彻底解决)。
(4)写程序尽量避免使用全局变量,尤其是非static类型的全局变量。能确定不会被其他文件引用的全局变量一定要static修饰。
(5)注意区分全局变量的定义和声明。一般规律如下:如果定义的同时有初始化则一定会被认为是定义;如果只是定义而没有初始化则有可能被编译器认为是定义,也可能被认为是声明,要具体分析;如果使用extern则肯定会被认为是声明(实际上使用extern也可以有定义,实际上加extern就是明确声明这个变量为外部链接属性)。
(6)全局变量应该定义在c文件中并且在头文件中声明,而不要定义在头文件中(因为如果定义在头文件中,则该头文件被多个c文件包含时该全局变量会重复定义)。
(7)在b.c中引用a.c中定义的全局变量/函数有2种方法:一是在a.h中声明该函数/全局变量,然后在b.c中#include <a.h>;二是在b.c中使用extern显式声明要引用的函数/全局变量。其中第一种方法比较正式。
(8)存储类决定生命周期,作用域决定链接属性
(9)宏和inline函数的链接属性为无连接。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值