1.数据段(.bss .data .rodata)
.bss段
用来存放那些没有初始化和初始化为0的全局变量
bss类型的全局变量只占运行时的内存空间,而不占用文件空间
现在大多数操作系统,在加载程序时,会把所有的bss全局变量清0.但为保证程序的可以移植性,手工把这些变量初始化为0也是一个好习惯,这样这些变量都有个确定的初始值。当然作为全局变量,在这个程序的运行周期内,bss数据是一直存在的。
.data段
data段用来存放那些初始化非零的全局变量
data类型的全局变量既占文件空间,又占用运行时的内存空间。同样作为全局变量,在整个程序的运行周期内,data数据是一直存在的。
.rodata段
ro代表 read only, rodata就是用来存放常量数据的。
在运行过程中不会改变的数据设为rodata类型,在多个进程间共享,可以大大提高空间利用率,甚至不占用RAM空间。同时由于rodata在只读的内存页面(page)中是受保护的,可以提高程序的稳定性。
字符串会被编译器自动放到rodata中,其他数据要放到rodata中,只需要加const关键字修饰。
2.代码段(.text段)
text段存放代码和部分整数常量,它与rodata段很相似,主要不同的在于这个段是可以执行的。
3.栈(stack)
栈用于存放临时变量和函数参数。栈作为一种基本数据结构,可以用来实现函数的调用。
尽管大多数编译器在优化时,会把常用的参数或者局部变量放入寄存器中。但用栈来管理函数的调用时的临时变量是通用做法,前者只是辅助手段,且只在当前函数中使用,一旦调用下一层函数,这些值任然要存入栈中才行。
通常情况下,栈向下增长,每向栈中PUSH一个元素,栈顶就向低地址扩展,每从栈中POP一个元素,栈顶就向高地址回退。
4.堆(heap)
堆是最灵活的一种内存,它的生命周期完全由使用者控制。标准的C语言提供以下几个函数:
· malloc 用来分配一块指定大小的内存
· realloc 用来调整/重分配一块存在的内存
· free 用来释放不在使用的内存
malloc/free 要配对使用。内存分配了不释放我们称为内存泄漏,内存泄漏多了迟早会出现 Out of memory的错误,再分配内存就会失败。
当然释放时也只能释放分配出来的内存,释放无效的内存或者重复free都是不行的,会造成程序crash。分配多少内存用多少,不管是读还是写,都只能在这个范围 ,读多了会读到随机数据,写多了会造成随机的破坏,这种情况称为缓冲区溢出,这是非常严重的,大部分安全问题都是由缓冲区溢出引起的。
注:
r-p 存放于 rodata
rw-p 存放于 bss
r-xp 存放于 text
没有文件名的内存空间,表示用mmap映射的匿名空间
文件名为【stack】的内存区间表示的是栈
文件名为【heap】的内存区间表示的是堆
5.内存分配的方式
有三种
(1)从静态存储区域分配。内存在程序编译时就已经分配好了,这块内存在程序的整个运行期间都存在,如全局变量,static变量等。
(2)在栈上创建。执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算使用内置于处理器的指令集,效率很高,但分配的内存容量有限。
(3)从堆上分配,亦称动态内存分配。程序在运行时用malloc或new申请所需要的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也做多
全局变量和static变量是程序整个程序需要用到的,单独分出一块存储区保存,在程序的整个运行期间该存储区存储的数据不清空;局部变量在函数退出时自动清空,放在栈里进行临时存储。用指令new和malloc分配的内存需要自己在堆中手动申请并用free和delete指令手动释放。
6.指针和数组的对比
数组要么在静态存储区被创建,不么在栈上被创建。数组名对应着一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。
指针可以随时指向任意类型的内存块,它的特性是“可变”,所以常用指针来操作动态内存。指针远比数组灵活,但也更危险。
7.带参函数宏定义与自定义函数区别
(1)在带参宏定义中,形式参数不分配内存空间,因此不必作类型定义;而宏调用中的实参有具体的值,要用它们去代换形参,因此必须作类型说明。这与函数中的情况是不同的,在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏,只是符号代换,不存在值传递的问题。
(2)在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。
注:在宏定义中,字符串内的形参通常要用括号括起来以避免出错。
8.#define和typedef 的区别
typedef只是为了增加可读性而为标志符另起的新名字,而#define原本在C语言中是为了定义常量。
宏定义只是简单的字符串代换,而typedef则不是原地扩展,它的新名字具有一定封装性,以致新名字的标识符具有更易定义变量的功能。
9.结构体和联合体的区别
struct和union都是由多个不同的数据类型成员组成的,但在任何同一时刻,union中只存放了一个被选中的成员,而struct的所有成员都存在。在struct中,各成员都占有自己的内存空间,他们是同时存在的,一个struct变量的总长度等于所有成员长度之和;在union中,所有成员不能同时占有它的内存空间,他们不能同时存在,union变量的长度等于最长的成员的长度。
对于union的不同成员赋值,将会对其他成员重写,原来的成员的值就不存在了,而对于struct的不同成员赋值是互不影响的。
10.位段的使用
·位段的类型只能是int,unsigned int,signed int三种类型,不能是char型或者浮点型。
·位段占的二进制位数不能超过该基本类型所能表达的最大位数,比如在VC中int 占4个字节,那么做多只能是32位。
·无名位段不能被访问,但会占据空间。
·不能对位段进行取地址操作。
·若位段占的二进制位数为0,则这个位段必须是无名位段,下一个位段从下一个位段存储单元开始存放。
·若位段出现在表达式中,则会自动进行整型升级,自动转化为int型或者unsigned int。
·对位段赋值时,最好不要超过位段所能表示的最大范围,否则可能会造成意想不到的结果。
·位段不能出现数组的形式。