存储类、做用域、生命周期、连接属性

1.linux下C语言程序的内存映像代码段(.text)、数据段(.data)、bss段、栈、堆的概念linux

代码段(.text)

(1)对应着程序中的代码(函数),代码段在linux中又叫文本段(.text)
(2)部分平台下的const修饰的变量。编程

数据段(.data)

一、显式初始化为非0的全局变量;架构

二、显式初始化为非0的static局部变量函数

bss段

一、显式初始化为0或者未显式初始化的全局变量;优化

二、显式初始化为0或未显式初始化的static局部变量。spa

局部变量分配在栈上;函数调用传参过程也会用到栈
用时须要手工申请。
文件映射区 文件映射区就是进程打开了文件后,将这个文件的内容从硬盘读到进程的文件映射区,之后就直接在内存中操做这个文件,读写完了后在保存时再将内存中的文件写到硬盘中去。
内核映射区

(1)内核映射区就是将操做系统内核程序映射到这个区域了。
(2)对于linux中的每个进程来讲,它都觉得整个系统中只有它本身和内核而已。它认为内存地址0xC0000000如下都是它本身的活动空间,0xC0000000以上是OS内核的活动空间。
(3)每个进程都活在本身独立的进程空间中,0-3G的空间每个进程是不一样的(由于用了虚拟地址技术),可是内核是惟一的。操作系统

 

2.存储类相关的关键字
(1)讲述存储类关键字auto、static、register,其中重点是static。指针

  auto关键字在C语言中只有一个做用,那就是修饰局部变量。默认就是autorest

  static两种用法:生命周期

  第一种修饰局部变量为静态局部变量。

  一、静态局部变量在存储类方面和全局变量同样。(数据段或bss段)
  二、静态局部变量在生命周期方面和全局变量同样。(整个程序)
  三、静态局部变量和全局变量的区别是:做用域、链接属性。静态局部变量做用域是代码块做用域(和普通局部变量是同样的)、连接属性是无链接;全局变量做用域是文件做用域(和函数是同样的)、连接属性方面是外链接。

  第二种是修饰全局变量,函数名。表示只在当前文件下有用,用于避免重名。(修饰后的连接从外链接变成内连接)

  register:用register修饰的变量编译器优先分配到寄存器中。


(2)讲述存储类关键字extern、volatile、restrict、typedef,其中重点是extern和volatile。

  extern:在声明中用,表示这个变量在其余文件中定义。

  volatile:比较敏感,可能被外界环境改变的量。用这个修饰后能够避免编译器过分优化。

  typedef:

3.做用域

  一、局部变量的代码块做用域

  (1)代码块基本能够理解为一对大括号{}括起来的部分。
  (2)代码块不等于函数,由于if while for都有{}。因此代码块<=函数
  (3)局部变量的做用域是代码块做用域,也就是说一个局部变量能够被访问和使用的范围仅限于定义这个局部变量的代码块中定义式以后的部分。

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

  三、同名变量的掩蔽规则
  (1)问题:编程时,不可避免会出现同名变量。变量同名后不必定会出错。
  (2)首先,若是两个同名变量做用域不一样且没有交叠,这种状况下同名没有任何影响。
  (3)其次,若是两个同名变量做用域有交叠,C语言规定在做用域交叠范围内,做用域小的一个变量会掩蔽掉做用域大的那个(县官不如现管)。

4.生命周期 

  栈变量的生命周期
  (1)局部变量(栈变量)存储在栈上,生命周期是临时的。临时的意思就是说:代码执行过程当中按照须要去建立、使用、消亡的。
  (2)譬如一个函数内定义的局部变量,在这个函数每一次被调用时都会建立一次,而后使用,最后在函数返回的时候消亡。

  堆变量的生命周期
  (1)首先要明白:堆内存空间是客观存在的,是由操做系统维护的。咱们程序只是去申请而后使用而后释放。
  (2)咱们只关心咱们程序使用堆内存的这一段时间,所以堆变量也有了本身的生命周期,就是:从malloc申请时诞生,而后使用,直到free时消亡。

  数据段、bss段变量的生命周期
  (1)全局变量的生命周期是永久的。永久的意思就是在程序被执行时诞生,在程序终止时消亡。
  (2)全局变量所占用的内存是不能被程序本身释放的,因此程序若是申请了过多的全局变量会致使这个程序一直占用大量内存。
  (3)若是说堆内存是图书馆借的书,那么全局变量就是本身买的书。

  代码段、只读段的生命周期
  (1)其实就是程序执行的代码,其实就是函数,它的生命周期是永久的。不过通常代码的生命周期咱们并不关注。
  (2)有时候放在代码段的不仅是代码,还有const类型的常量,还有字符串常量。(const类型的常量、字符串常量有时候放在rodata段,有时候放在代码段,取决于平台)

5.连接属性  

  一、C语言程序的组织架构:多个C文件+多个h文件
  (1)庞大、完整的一个C语言程序(譬如linux内核、uboot)由多个c文件和多个h文件组成的。
  (2)程序的生成过程就是:编译+连接。编译是为了将函数/变量等变成.o二进制的机器码格式,连接是为了将各个独立分开的二进制的函数连接起来造成一个总体的二进制可执行程序。
  二、编译以文件为单位、连接以工程为单位
  (1)编译器工做时是将全部源文件依次读进来,单个为单位进行编译的。
  (2)连接的时候其实是把第一步编译生成个单个的.o文件总体的输入,而后处理连接成一个可执行程序。
  三、三种连接属性:外链接、内连接、无连接
  (1)外链接的意思就是外部连接属性,也就是说这家伙能够在整个程序范围内(言下之意就是能够跨文件)进行连接,譬如普通的函数和全局变量属于外链接。
  (2)内连接的意思就是(c文件内部)内部连接属性,也就是说这家伙能够在当前c文件内部范围内进行连接(言下之意就是不能在当前c文件外面的其余c文件中进行访问、连接)。static修饰的函数/全局变量属于内连接。
  (3)无链接的意思就是这个符号自己不参与连接,它跟连接不要紧。全部的局部变量(auto的、static的)都是无链接的
  四、函数和全局变量的同名冲突(static修饰)
  (1)由于函数和全局变量是外部连接属性,就是说每个函数和全局变量未来在整个程序中全部的c文件都能被访问,所以在一个程序中的全部c文件中不能出现同名的函数/同名的全局变量。
  (2)最简单的解决方案就是起名字不要重复,可是很难作到。主要缘由是一个很大的工程中函数和全局变量名字太多了,并且一个大工程不是一我的完成的,是不少人协做完成,因此很难保证不会重名。解决方案呢?
  (3)现代高级语言中完美解决这个问题的方法是命名空间namespace(其实就是给一个变量带上各个级别的前缀)。可是C语言不是这么解决的。
  (4)C语言比较早碰到这个问题,当时还没发明namespace概念,当时C语言就发明了一种不是很完美可是凑活能用的解决方案,就是三种连接属性的方法。
  (5)C语言的连接属性解决重名问题思路是这样的:咱们将明显不会在其余c文件中引用(只在当前c文件中引用)的函数/全局变量,使用static修饰使其成为内连接属性,这样在未来链接时即便2个c文件中有重名的函数/全局变量,只要其中一个或2个为内连接属性就没事。
  (6)这种解决方案在必定程度上解决了问题。可是没有从根本上解决问题,留下了不少麻烦。因此这个就致使了C语言写很大型的项目难度很大。

  五、static的第二种用法:修饰全局变量和函数
  (1)普通的(非静态)的函数/全局变量,默认的连接属性是外部的
  (2)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函数的连接属性为无链接。

相关文章
相关标签/搜索