C语言代码内存区域划分

1 内存分配的方式

1)从静态存储区域分配:内存在程序编译的时候就已经分配好了,这块内存在程序的整个运行期间都存在,例如全局变量;
2)在栈上创建:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。处理器的指令集中有关于栈内存分配运算,因此效率很高,但分配的内容容量有限;
3)从堆上分配:也称动态内存分配,程序在运行时用malloc申请任意多少的内存,程序员自己负责在何时用free释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也很多。

从内存划分的角度看,我们可以根据作用进一步分为一下五类区域:
1)栈区(stack):由编译器自动分配和释放,存放函数的参数值、局部变量的值等。操作方式类似于数据结构中的栈。
2)堆区(heap):一般由程序员自己分配和释放,若程序员不释放,程序结束时可能由操作系统回收。注意它与数据结构中堆是两回事,分配方式类似于链表。
3)全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放。
4)文字常量区:常量字符串就是放在这里的(注意文字常量区的字符串不可修改!),程序结束后系统释放。
5)程序代码区:存放函数体的二进制代码。
例如下图:
C语言代码内存区域划分_第1张图片
那么俺举个例子,通过下面的代码对内存进行分析:

int a = 0;// 全局初始化区
char *p1; // 全局未初始化区
int main(void)
{
	int a;// 栈
	char arr[] = “shn”;// 栈
	char *p2;// 栈
	char *p3 =123456; // 123456在常量区,p3在栈上
	static int b = 0; // 全局(静态)初始化区
	p2 = (char *)malloc(10);// 分配10个字节的区域在堆区
	return 0;
}

注意:栈的生长方向(地址由上到下递减)(堆是相反的——>地址由下往上递增)

2 堆区和栈区内存的区别

1)申请方式
栈:由系统自动分配;
堆:需要程序员自己申请,并指明大小,在C语言中一般用malloc函数。

2)申请后系统的响应
栈:只要栈的剩余空间大于所申请的空间,系统将为程序提供内存,否则将报异常提示栈溢出。

堆:首先要知道系统是有一个记录空闲时间的链表,当系统收到程序的申请时,会遍历链表,寻找第一个空间大于所申请空间的节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。

3)申请大小的限制
栈:在Windows下,栈是从高地址向低地址扩展的数据结构,是一块连续的内存区域,即栈顶的地址和栈的最大容量是系统预先设定好的(Windows下栈大小一般是2MB,是一个编译的时候就确定的常数)。

堆:堆是从低地址向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表存储空闲内存地址的,自然是不连续的。而链表的遍历方向是由低地址向高地址,堆大小受限于计算机系统中有效的虚拟内存,所以堆的空间比较灵活,也比较大。

注意:栈是一种线性结构;堆是一种链式结构。

4)申请效率的比较
栈:由系统自动分配,速度较快,程序员无法控制。
堆:由malloc分配,一般速度比较慢,而且容易产生内存碎片,不过使用方便。

5)堆和栈中的存储内容
栈:在函数调用时,第一个进栈的是主函数中的下一条指令的地址(函数调用语句的下一条可执行语句),然后是函数的各个参数,在大多数C编译器中,参数是从右往左入栈的,然后是函数中的局部变量。注意!静态变量是不入栈的!当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是堆的头部用一个字节存放堆的大小,堆中具体内容由程序员安排。

注意:程序在编译期对变量和函数分配内存都是在栈区上进行,而且程序运行过程中函数调用时参数的传递也在栈上进行。

例如:
全局变量放在数据段;函数内部变量static int arr放在数据段;函数内部变量char *p=”AAA”,p的位置在堆栈,指向空间的位置数据段;函数内变量char *p =malloc(),p的位置在栈,指向空间的位置在堆。

堆栈是一种简单的数据结构,是一种允许在其一端进行插入或删除的线性表。允许插入或删除操作的一端称为栈顶,另一端称为栈底。对堆栈的插入和删除操作称为入栈和出栈。

3 编译角度分析内存

C程序在编译后,会以三种形式使用内存:
1)静态/全局内存
静态声明的变量分配在这里,全局变量也使用这部分内存,这些变量在程序运行时分配内存,直到程序终止才消失。所有函数都能访问全局变量,静态变量的作用域则局限在定义它们函数的内部。

2)自动内存
这些变量在函数内部声明,并且在函数被调用时才创建,它的作用域限于函数内部,而且声明周期限制在函数的执行时间内。

3)动态内存
内存分配在堆上,可以根据需要释放,而且直到释放才消失。
使用指针要注意的问题:
◎访问数组和其他数据结构时越界;
◎自动变量消失后被引用;
◎堆上分配的内存释放后被引用;
◎内存分配之前解引指针;

而数据指针是可以进行算数运算的,比如:
1)给指针加上/减去整数
实际上加的数是这个整数和指针数据类型对应字节数的乘积,int *p; p+1就是地址加4。减去整数时,地址会减去数据类型的长度和整数值的乘积。
2)两个指针相减
指针之间的差值是它们之间相差的“单位”数,差的符号取决于操作数的顺序。
例如:a[5]-a[1] = 4; a[1]-a[5] = -4;
3)比较指针
把指针和数组元素相比较时,比较结果可以用来判断数组元素的相对顺序。
例如:int *p1 = arr[1]; int *p2 = arr[2];printf(“%d”, p2>p1)// 1; printf(“%d”, p2

你可能感兴趣的:(C语言和数据结构,c语言,数据结构)