
c语言的内存四区的概念
内存四区模型是C语言学习的一个重点也是一个难点,大多程序员往往注重代码的编写实现而不注重代码在执行中所进行的内存的变化而导致程序出现bug,严重时导致程序失控崩溃。要了解内存四区,我们可以先了解一下打开一个程序的时候。操作系统是如何运行这个程序的,运行过程入图1所示。
内存四区
1 代码区(code area)
代码区code,程序被操作系统加载到内存的时候,所有的可执行代码都加载到代码区,也叫代码段,这块内存是不可以在运行期间修改的。
2 静态区(全局区)(static area)
所有的全局变量以及程序中的静态变量,以及字符串都存储到静态区, C编译器对于相同的字符串会自动进行优化分配为统一的地址。
3 栈区(stack)
栈stack是一种先进后出(FILO)的内存结构,所有的自动变量,函数的形参都是由编译器自动放出栈中,当一个自动变量超出其作用域时,自动从栈中弹出。
4 堆区(heap)
堆heap和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。堆是一个大容器,它的容量要远远大于栈,但是在C语言中,堆内存空间的申请和释放需要手动通过代码来完成。使用malloc()/new和free()/delete来开辟和释放内存。
C语言的内存分析
下面对这这几区域进行分析。栈区 最重要的特性是先进后出的内存结构,当函数运行完成后,自动释放开辟给函数以及该函数的变量的内存,正是因为栈的这种特性使得变量有作用域的说法,main是程序的入口,可以理解为他是最先入栈的函数,所以他最后被释放。栈的方向一般X86的体系是向下生长的。作
至于数组元素与栈的增长方向:C与C++语言规范都规定了数组元素是分布在连续递增的地址上的。引用C语言规范的规定:An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type.A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).
我们不妨使用代码测试一下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//栈的开口向上向下
//一般认为:栈开口向下
//不管栈开口向上还是向下,buf的内存地址buf+1,永远向上的
void main()
{
int a;
int b ;
char buf[128]; //静态联邦的时候 buf所代表的内存空间的标号 就已经定义下来了....
printf("&a:%d , &b: %d \n", &a, &b);
printf("&buf[0]:%d , &buf[1]: %d \n", &buf[0], &buf[1]);
system("pause");
return ;
}
结果
2 静态区的优化
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char * getStr1()
{
char *p1 = "abcdefg2";
return p1;
}
char *getStr2()
{
char *p2 = "abcdefg2";
return p2;
}
void main55()
{
char *p1 = NULL;
char *p2 = NULL;
p1 = getStr1();
p2 = getStr2();
//打印p1 p2 所指向内存空间的数据
printf("p1:%s , p2:%s \n", p1, p2);
//打印p1 p2 的值
printf("p1:%d , p2:%d \n", p1, p2);
printf("hello...\n");
system("pause");
return ;
}
分析以上代码发现指针所指的地址是一样,原因在于字符串是储存在静态区的,相同字符串,系统并不会重新开辟空间。这样做可以减少代码所占有的内存。
3 堆(heap)与栈(stack)的区别
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//堆
char *getMem(int num)
{
char *p1 = NULL;
p1 = (char *)malloc(sizeof(char) * num);
if (p1 == NULL)
{
return NULL;
}
return p1;
}
void main()
{
char *tmp = NULL;
tmp = getMem(10);
if (tmp == NULL)
{
return ;
}
strcpy(tmp, "111222"); //向tmp做指向的内存空间中copy数据
printf("hello..tmp:%s.\n", tmp);
system("pause");
return ;
}
//栈区开辟内存
define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char *getMem1()
{
char buf[64]; //临时变量 栈区存放
strcpy(buf, "123456789");
return buf;
}
void main()
{
char *tmp = NULL;
tmp = getMem1();
if (tmp == NULL)
{
return ;
}
strcpy(tmp, "111222"); //向tmp做指向的内存空间中copy数据
printf("hello..tmp:%s.\n", tmp);
system("pause");
return ;
}
堆区和栈区的不同在于函数运行后会不会自动的释放内存空间。栈会自动释放,所以代码2输出时乱码,因为buf地址中的内容在getnum1()运行完毕之后被释放了,所以buf地址的内容是乱码。而代码1开辟的空间是在堆区,所以不会自动释放内容。注意一般使用molloc()和free()这两个函数来开辟堆区的内存。
当开辟空间,不需要后要对内存进行释放。
内存空间的概念十分重要,影响了对指针的理解,而指针又是C语言的精髓