非原创(文中红字为本身看法,若有不对,请大神指点)html
程序由指令和数据组成,C语言程序亦是如此。开发者在编写程序的时候每每须要根据不一样数据的特色以及程序需求来选择不一样的数据存储方式,那么在C语言中数据的存储分为哪些方式呢?编程
C程序大体来说能够分为四个数据区:常量区,静态去,堆区,栈区。数组
其中常量区存储了未被做为初始化使用的字符串常量和被const修饰的全局变量,其特色是只可被访问不可被写入,生命周期同程序的运行过程。安全
静态区存储了所有的全局变量,和全部被static修饰的变量(包括全局和局部),其特色是生命周期很长(为一次程序的运行过程)而且只被初始化一次(在编译以后就已完成)。架构
栈区存储了全部自动存储(不加任何存储类型关键字修饰或被auto修饰)的局部变量,其特色是生命周期很短,仅仅是该变量所在函数的一次调用过程。运行时有操做系统分配并在函数结束后回收。函数
堆区是由操做系统负责维护的大片内存池,使用时需手动申请(调用malloc家族函数),但使用完毕后需手动释放,不然会形成严重的内存泄漏,直到该进程退出后才会被操做系统回收。优化
下面详细介绍每种存储类型的特色。spa
一.常量区:操作系统
故名思意,常量区里存放的是一些不可改变的量,好比字符串常量。在实际的ELF(Executable and Linkable Format,可执行链接格式,是UNIX系统实验室(USL)做为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的。Linux 做为类unix系统其程序仍然沿用了该格式。)的程序数据是分段存储的,对应的常量区就是".rodata(只读数据)段"。3d
常量区的数据被标记为只读,也就是程序只有访问权而没有写入权,所以若是开发者须要使用某些不但愿被改变的数据时能够将其放入常量区。
在C语言中常量有不少种,好比常见的:
字符常量:'a', 'A', '*'。
字符串常量:"helloworld","ilovechina","12345"。
整常量: 25,10,012,0x0a,0b00001010。
浮点常量: 3.14,123.456, 3.0E-23;
可是并非全部的常量都会被编译器放在常量区的,如图1-1中代码所示:
图1-1 定义一个变量并被常量初始化
图中程序定义了一个整型局部变量i,而且被初始化为10,其中i是变量,10是常量,可是编译器并不将10放入常量区,而是在指令中直接经过当即数赋值(图1-2)。
图1-2 由图1-1程序编译生成的汇编代码
这是由于编译器认为普通的整型、浮点型或字符型常量在使用的时候是能够经过当即数来实现的,没有必要额外存储到数据区,如此节省了存储空间和运行时的访问时间。那么什么样的数据才将放入常量区呢?
1.字符串常量
如图1-1所示,在C程序中定义了一个局部的字符指针变量p指向一个字符串常量,其中p因为是局部变量被放在栈区,而字符串常量"helloworld"在汇编中被放入.rodata段(图1-2),在编译后生成的ELF格式文件中也将被放入.rodata段(图1-3)
图1-3 C语言的示例程序定义指针变量指向字符串常量。
图1-4 由图1-3中代码生成的汇编程序。
图1-5 由图1-3中程序编译生成的可执行程序分析。
可是,当一个字符常量串被用来为数组初始化的时候,那么该字符串常量将不会放入常量区,而是放入对应的数组中,如图1-6所示:
图1-6 定义一个字符数组并用字符串常量初始化
编译器会将该字符串按照四字节为一组转换成对应的32位整数来为该数组进行初始化,如图1-7所示,其中第13行的10进制整数1819043176转换成16进制为0x6c6c6568,正好是字符'l','l','e','h':
图1-7 图1-6中C代码生成的汇编程序
所以在编译生成的ELF格式文件中的.rodata段也将不会存储该字符串常量:
图1-8 图1-6程序编译后生成的可执行程序片断。
2.被const修饰的全局变量
a)
除了字符串以外,其余常量也能够放在常量区,可是前提是该数据必须被存放在全局变量的空间里,而且被const关键字修饰。如图1-9代 码所示:
图1-9 第4行定义了一个被const修饰的全局变量
编译生成的汇编程序比较:
图1-10
其中value0因为被const修饰因此放在了.rodata段也就是所谓的常量区,而value1是一个普通的全局变量因此放在了.data段也就是所谓的静态数据区。分析编译生成的ELF格式可执行程序以下:
图1-11 value0的存放位置
其中value0的数据被放在常量区(.rodata段)十六进制显示的0a对应了它的十进制初始值10。
图1-12 value1的存放位置
Value1的数据被放在静态区(.data段)十六进制显示的14对应了它的十进制初始值20。
b)
可是并非全部被const修饰过的变量都放在常量区,事实上只有全局变量才是如此,普通的局部变量被const修饰后仅仅意味着在表达式上不能显式地改变该变量值,不然编译器会报语法错误,但该变量仍存放在栈区。C++不一样的地方就在此,C++鼓励使用const来取代#define,由于C++对const进行了优化,若是该变量的值是常量表达式,在C++中就会进行常量折叠(constant folding),何为const folding,百度一大堆,简单来讲就是在编译期间任何出现该变量的地方都会被替换成常量表达式的值,正所以,这种状况下const定义的变量是能够用于定义数组的维度的,而C语言就没有这个优化特性,因此C语言的const修饰的变量是不管如何都不能进行数组维度的定义的(注意,C++中不进行常量折叠的状况下,const修饰的仍没法进行数组定义,具体何时常量折叠请参看个人博文http://www.cnblogs.com/yanqi0124/p/3795019.html)。而因为其存储区域没有发生本质的改变,所以仍然能够经过其余方式改变其值,好比指针。如图1-13所示:
图1-13 定义两个局部变量,其中一个被const修饰
保存,编译,结果以下:
图1-14 编译器发生编译错误
因为 value1被const修饰,因此程序第9行的赋值语句将发生错误。
接着修改程序,经过指针去修改value1的值:
图1-15 定义指针p指向value1并经过指针赋值。
编译,运行:
图1-16 编译运行结果
因为定义的指针变量p和表达式&value1的类型不匹配(p是int *,而&value1的类型是const int *)因此在第7行赋值的时候编译器会产生一个类型不匹配的警告,咱们忽略该警告继续运行,结果改变了value1的值。
3.由常量区引起的段错误
因为常量区的特性是只读,所以当程序试图去向指向常量区的地址写入数据的时候,操做系统处于安全考虑会发出一个段错误的信号而且杀死该进程,以达到保护操做系统的目的。
图1-17 示例代码,经过指针去向常量区写数据。
第10行和11行的都可产生一样的段错误,如图1-18所示
图1-18 非法写入引起的段错误
二.静态区:
静态区是一个抽象笼统的概念,在实际的Linux/C的可执行程序中并无静态区这个区域,具体来说它主要由两个段组成:.data段和.bss段。其中.data段就是程序的数据段,在采用段式内存管理的架构中,数据段(data segment)一般是指用来存放程序中已初始化且不为0的全局变量或静态变量的一块内存区域。相反,BSS(Block Started by Symbol)一般是指用来存放程序中未初始化的或初始化为0的全局变量或静态变量的一块内存区域。.data段在程序编译期间其大小及数据被肯定,而.bss段则没有直接分配空间而是由编译器在.data段以后为其预留空间,在程序装载进内存时被正式分配。尽管静态区由两个不一样的段组成,可是在程序连接并装载进内存以后这两段不作区分,所以咱们在这里不作分开讨论。
静态区的变量拥有如下特征:
1) 生命周期长,直到该进程结束随进程空间一块儿被系统回收。
2) 只初始化一次,它的空间数据在编译期间被初始化,逻辑地址在连接期间固定。
那么哪些变量将被放在静态区呢?
1.全局变量:
顾名思义它是全局的公用的,若是一个变量被定义为全局的,那么在同一个程序中,任何函数均可以去访问、存取该变量的数据。基于此,全局变量除拥有静态区变量的所有特征以外还具备做用域广的特色,其做用域在整个程序中(能够由多个源文件组成)全局可见。
2.静态变量
从字面上理解所谓静态变量就是被static关键字修饰的变量,只要被static修饰为静态变量那么都将被编译器分配在静态区,其也就拥有了静态区变量的所有特征。静态变量分两种:全局静态变量和局部静态变量。不管哪一种只要被static修饰都将放在静态区,拥有静态区变量的所有特征。其区别仅在于做用域:若是是全局静态变量,那么该变量的做用域被限定只能在本源文件内使用(编译以后该变量的符号将不容许对外连接,可是仍然能够经过指针去间接访问);若是是局部变量则没有变化(仅限函数内部使用)。
下面给出一段示例代码用以说明静态变量的特性(图2-1):
图 2-1
代码中定义了一个全局变量gvalue和局部变量lvalue,而且通过两次函数调用。gvalue因为是全局变量被编译器分配在静态区,而lvalue是局部变量放在栈区。因为静态区的特写致使gvalue通过两次函数调用实现了累加,而局部变量lvalue则每次在函数调用时都被从新初始化。地址(图2-2)。
图 2-2
下面更改程序,将局部变量修改成静态局部变量(图2-3):
图 2-3
则因为局部变量lvalue被static修饰放在了静态区只初始化一次,所以也实现了累加(图2-4)。
图 2-4
原文出处:
浅谈C语言的数据存储(一):http://www.embedu.org/Column/Column540.htm
浅谈C语言的数据存储(二) :http://www.embedu.org/Column/Column558.htm