温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C语言之程序中内存的来源:栈 堆 数据段

发布时间:2020-07-30 01:34:38 来源:网络 阅读:404 作者:菜鸟养成记 栏目:编程语言

程序在运行的时候,其内存的来源主要通过三种方法:  栈  堆  数据段,总体上来讲栈是一般用来存放小内存的局部变量,堆内存和数据段的属性很像,在使用的的时候,如果这个变量是伴随程序一直存在则使用全局变量,也就是放在数据段,如果一个变量使用完了就没用了,那么就适合用堆内存(先申请,然后释放即可),


一:栈(stack):

1:栈在使用的时候是编译器自动分配内存空间的,不需要程序员的干涉,其次栈的大小是有限的,所以当我们定义的变量需要大片的内存的时候就不适合使用栈,

2:栈存放的是普通变量,栈的在使用的时候是反复使用的,所以栈内存是脏的,在定义普通变量的时候,如果不对变量进行初始化,那么变量的值就是随机的。

3:栈是先进后出的,其内存空间是向下增长的。


二:堆(heap):

1:堆得使用时是由程序员来操作的,程序员通过malloc来向内存申请堆内存,使用完以后通过free来释放这部分内存,如果这部分内存在使用完以后没有进行内存的释放,那么这部分内存管理器就会认为这部分内存一直被占用的,体现出来的就是程序吃内存,也就是所谓的内存泄漏,如果没有释放内存,则被占用的内存只有当程序结束以后才会被释放。一般需要大片的内存时才使用堆,堆是先进先出的,其内存空间是向上增长的。

代码示例:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
//申请内存
int *p = (int *)malloc(100);
if(p == NULL)  //错误检查
{
printf("error \n");
return -1;
}
//内存使用S
*(p+4) = 1024;
*(p+3000) = 111;
printf("*(p+3) = %d\n", *(p+4));
printf("*(p+30000)) = %d\n", *(p+3000));
printf("p = %p \n",p);
printf("(p +4) = %p \n",(p +4));

free(p);   //释放内存

printf("*(p+3) = %d\n", *(p+4));
printf("*(p+30000)) = %d\n", *(p+3000));
printf("p = %p \n",p);
printf("(p +4) = %p \n",(p +4));

p = NULL;  //避免野指针

return 0;
}

运行结果:

*(p+3) = 1024

*(p+30000)) = 111

p = 0x8efb008

(p +4) = 0x8efb018

*(p+3) = 1024

*(p+30000)) = 111

p = 0x8efb008

(p +4) = 0x8efb018

分析:

1:堆内存可以越界访问,但是实际中最好还是不要,因为你在使用的时候越界访问就意味着踩到别人了

2:堆内存在释放之后还可以访问,并且访问的值还是不变的,说明堆内存也是脏的,堆内存释放的时候并没有对使用过的没存进行清理。


三:数据段(.data )

1:一个程序主要有数据段(.data) 代码段  和bss段,不同的段具有不同的段属性

数据段:(又叫数据区、静态数据区、静态区)数据段存放的是程序的中显示初始化的全局变量(不包括初始化为0的全局变量),同时需要注意的是全局变量才算是程序的数据,局部变量不是程序变量,只是函数数据

代码段:代码段就是程序中的可执行部分,直观理解代码段就是函数堆叠组成的。代码段是只读的。

bss段:(又叫zero initial 段)bss段存放的是为显示初始化的全局变量已经初始化为0的全局变量(C语言中默认全局变量的初始化就是为0),所以可以理解为bss段就是初始化为0的数据段。

2:放在.data段的变量有两种:第一种是显式初始化为非0的全局变量,另一种是静态局部变量,也就是static修饰的局部变量(普通变量是分配在栈上面,静态局部变量是分布在.data段)



四:代码段中的特殊数据

1:C语言使用char *p = "linux";定义字符串的时候,字符串"linux"实际上是被分配到了代码段上面,换句话说这里的字符串"linux"实际上是一个常量字符串而不是变量字符串。

2:const修饰的类型常量,const的实现方法主要有两种,一种是编译的时候将const修饰的变量放在代码段去实现不可修改(因为代码段是只读的),另一种是由编译器来检查来确保const修饰的常量,但是这种实际上和普通常量样,也是被放在了数据段,所以如果绕过编译器就可以实现修改,具体实现方法是用指针去指向这个常量的内存空间,然后解引用去修改这个常量,gcc中就是这样实现的。






向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI