目录
1 C语言的内存分区
1.1 内存五大分区
1.2 内存分区简介
1.2.1 栈区(stack)
1.2.2 堆区(heap)
1.2.3 (全局)静态区
1.2.4 常量区
1.2.5 代码区
创作不易,如果本篇博客对您有一定的帮助,大家记得留言+点赞哦。
C语言已经持续学习一段时间了,今天特此总结一下关于C语言内存的五大区。它是我们深入理解C语言非常有必要了解的知识点。通过了解五大区,对于进一步学习C语言底层是非常有帮助的。
1 C语言的内存分区
1.1 内存五大分区
C语言内存可大致分为5个区域,图和表如下:
内存影像区 | 内容 | 权限 |
栈区 | 函数中的普通变量 | 可读可写 |
堆区 | 动态申请的内存 | 可读可写 |
(全局)静态变量区 | static修饰的变量 | 可读可写 |
常量区 | 用于初始化变量的常量 | 只读 |
代码区 | 代码指令 | 只读 |
nt k=1;void main(){int i=1;char *j;static int m=1;char *n="hello"; printf("栈区地址为:0X%x\n",&i);j = (char*)malloc(2); //一般不确定需要多大空间的时候用free(j);//及时释放printf("堆区地址为:0X%x\n",j);printf("全局变量地址为:0X%x\n",&k);printf("静态变量地址为:0X%x\n",&m);printf("文字常量区地址为:0X%x\n",n);printf("程序区地址为:0X%x\n",&main);} char *i="hello";char j[10]="hello";printf("0X%x\n",i); //存放在文字常量区printf("0X%x\n",j); //存放在栈区j[1]='*';//可以直接赋值//*(i+1)='*'; //等价于i[1]='*';//不可以这样赋值, 因为i是指针,指向的是文字常量区,里面的内容是不能修改的i=j; //这样可以printf("%s\n",i);printf("%x\n",i);j=i;//这样不可以,因为j虽然也是地址,但是不是指针变量,不能直接赋值
1.2 内存分区简介
1.2.1 栈区(stack)
栈区
由编译器自动分配释放,由操作系统自动管理,无须手动管理。
栈区上的内容只在函数范围内存在,当函数运行结束,这些内容也会自动被销毁。
#includechar *getMem(){char buf[64]; //局部变量 栈区存放strcpy(buf, "123456789");//向buf所代表的内存中写入内容//printf("buf:%s\n", buf);return buf;//返回所分配内存区域的第一个元素的地址} void main(){char *tmp = NULL;tmp = getMem(10);if (tmp == NULL){return ;}printf("tmp:%s\n", tmp);//输出tmp:system("pause");return ;}
内存分析:
- 栈区按内存地址
由高到低
方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。 - 栈区是
先进后出
原则,即先进去的被堵在屋里的最里面,后进去的在门口,释放的时候门口的先出去。 栈区存放内容
- 临时创建的
局部变量
和const定义的局部变量
存放在栈区。 - 函数调用和返回时,其
入口参数
和返回值
存放在栈区。
通俗来说:
栈区是用来存放局部变量的,比如函数内部定义的int a,int b,const int a,char p,char arr[ ],还有函数的形参等等都是存放在栈区。栈区的数据由编译器管理,调用完之后就自动释放,压栈,出栈。先进后出的原则,比如当你执行到函数调用的时候,编译器会先把下一条代码的地址压入栈中,再把你调用的那个函数里的一些局部变量啊,形参啊等等压入栈中,等你函数调用执行完毕。栈就会把你调用的这个函数之前压入栈的变量和形参全部清除出栈,之后根据下一条代码的地址,接着执行程序,以后的程序也都是这么执行。栈区是有大小的,一般是1M左右,所以别定义太大的数组。
1.2.2 堆区(heap)
堆区
由程序员分配内存和释放。若程序员不释放,程序结束时可能由操作系统回收。
#include 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数据,注意不是向指针变量tmp中printf("tmp:%s\n", tmp);//输出tmp:111222system("pause");return ;}
内存分析:
- 堆区按内存地址
由低到高
方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。
堆区动态申请与释放内存
用malloc()
,free()
等函数实现动态分布内存。
void *malloc(size_t);
- 参数
size_t
是分配的字节大小; - 返回值是一个
void*
型的指针,该指针指向分配空间的首地址;
(void *
型指针可以任意转换为其他类型的指针)
用free()
函数进行内存释放,否则会造成内存泄漏。
void free(void * /*ptr*/);
- 参数
ptr
是开辟的内存的首地址。 - 无返回值;
通俗来说:
由程序员手动申请和释放
比如:intp=(int)malloc(sizeof(int)10),表示申请了一块40个字节的堆区空间,然后申请完记得用free释放。
代码区的话就是用来存放代码的,转化为二进制存放。
1.2.3 (全局)静态区
通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量
和静态变量
。
全局区有.bss段
和.data段
组成,可读可写
。
#include char * getStr1(){char *p1 = "abcd";return p1;}char *getStr2(){char *p2 = "abcd";return p2;} void main(){char *p1 = NULL;char *p2 = NULL;p1 = getStr1();p2 = getStr2(); //打印p1 p2 所指向内存空间的数据,不是p1 p2中的数据printf("p1:%s , p2:%s \n", p1, p2);//输出p1:abcd , p2:abcd //打印p1 p2 的值printf("p1:%d , p2:%d \n", p1, p2);//输出p1:19184372 , p2:19184372 system("pause");return;}
内存分析:
.bss段
未初始化的全局变量和未初始化的静态变量存放在.bss段。
初始化为0的全局变量和初始化为0的静态变量存放在.bss段。
.bss段不占用可执行文件空间,其内容由操作系统初始化。.data段
已初始化的全局变量存放在.data段。
已初始化的静态变量存放在.data段。
.data段占用可执行文件空间,其内容有程序初始化。
通俗来说:
全局区比较特殊,里面还分成了全局变量区,静态变量区,常量区。全局变量区用来存放全局变量,静态变量区用来存放带有static修饰的变量(包括静态局部变量和静态全局变量),只要含有static就存在这个区。常量区是用来存放字符常量的,还有const修饰的全局变量的,const 修饰的局部变量不存在这里,别搞混了。全局区存放的一切都是由操作系统管理,等程序结束由操作系统释放。常量区里存放的数据不可更改,就算你用指针也不行,你可能会说const修饰的局部变量都可以用指针改,但是局部变量可不是存放在常量区,这点搞清楚。
1.2.4 常量区
字符串
、数字
等常量存放在常量区。const
修饰的全局变量存放在常量区。
程序运行期间,常量区的内容不可以被修改。
1.2.5 代码区
程序执行代码
存放在代码区,其值不能修改(若修改则会出现错误)。字符串常量
和define定义的常量
也有可能存放在代码区。
以上五区,代码区和全局区是在生成.exe文件之后就有了,双击.exe文件运行程序才会生成栈区和堆区。
下面直接上图:
#include#includeint a = 10;static int b = 20;void fun(int x){char *p = "Hello";printf("形参x的地址=%d\n\n", &x);printf("Hello的地址=%d\n\n", "Hello");printf("指针变量p的地址=%d\n\n", &p);return;}int main(int argc,const char *argv[]){int c = 10;int d = 20;static int e = 30;char *p = "Hello";printf("\n全局变量a的地址=%d\n\n", &a);printf("静态全局变量b的地址=%d\n\n", &b);printf("静态局部变量e的地址=%d\n\n", &e);printf("字符串\"Hello\"的地址=%d\n\n", "Hello");printf("局部变量c的地址=%d\n\n", &c);printf("局部变量d的地址=%d\n\n", &d);printf("指针变量p的地址=%d\n\n", &p);fun(5);system("pause");return 0;}
简单的用图表示了一下,总结:
全局区存放的是全局变量,静态变量,字符常量,const 修饰的全局变量。栈区存放的是局部变量和函数的形参,以及一些代码的地址,栈区的内容是可以修改的
堆区是由程序员手动申请和释放,用malloc函数申请,用free函数释放。