c内存划分

一、内存分区:数据区+代码区+堆区+栈区
1、数据区:分为静态数据区,全局变量区的存储是放在一块的。
即static,const修饰的变量、常量、全局变量都定义在此区,此区定义的变量未初始化,系统则会自动初始化为0
初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放空间
const:修饰的变量只能读不能修改他的值,表现的为一个常量的特性。他只能在初始化的时候给他赋值,其他任何时候都不能给他赋值。
2、代码区:存放程序中的普通代码(存放函数体的二进制代码)。
3、堆区(heap):一般由程序员手动申请以及释放, 若程序员不释放,程序结束时可能由OS回收 注意它与数据结构中的堆是两回事,分配方式类似于链表。
有malloc()/calloc()/recalloc()/new()来分配内存,
生命周期有free()/delete()决定何时释放分配的内存,此区使用灵活,空间相对较大。

4、栈区(stack): 该区是有编译器自动分配的一块内存区域,存放函数的参数值、局部变量的值等,甚至函数的调用过程都是用栈来完成,其操作方式类似于数据结构中的栈
    效率高,有编译器自动分配和释放(函数开始的时候分配,结束的时候释放)
5、文字常量区:常量字符串就是放在这里 程序结束后由系统释放空间

下面的例子可以完全展示不同的变量所占的内存区域:

//main.cpp

int a = 0; 全局初始化区
 char *p1; 全局未初始化区

void main()
 {
  int b; //栈中
  char s[] = "abc"; //栈中
  char *p2; //栈中
  char *p3 = "123456"; //123456\0在文字常量区,p3在栈上
  static int c =0; //全局(静态)初始化区

//以下分配得到的10和20字节的区域就在堆区
  p1 = (char *)malloc(10);
  p2 = new char[20];//(char *)malloc(20);
  strcpy(p1, "123456"); //123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方
 }
注意:strcpy()函数用法:定义一个字符串char a[20],和一个字符串c[]="i am a teacher!";把c复制到a中就可以这样用:strcpy(a,c);


char *p="zxcvbnm";
(p+1)='d';//修改第二个字符 因为p和“zxcvbnm”没有保存在同一个区,不能修改。断错误
char *q="zxcvbnm"; //两哥指针的地址相同
指针p 和指针 q都保存在栈区,但字符串zxcvbnm保存在文字常量区,因为没有保存在一个区,通过指针获取某个字符是不对的
并且因为在文字常量区已经开辟存放zxcvbnm的空间,在赋值zxcvbnm的时候不会再开辟空间,会把第一次开辟的地址赋予他们。


****手动在内存上分配一块空间:在堆上用malloc()函数分配一块连续空间
函数原型:(void *)malloc(int size)
头文件:malloc.h/stdlib.h
解析:内存分配好了之后,malloc函数返回这块内存的首地址,默认为void *型。故需要强制转换成你需要的类型,括号里是您需要分配的内存的字节数
例:char *p=(char *)malloc(100)
注:所申请的内存必须小于堆上面某一整块内存,才能成功,故malloc函数请一块内存有可能是不成功的,所以我们需要判断一下他是否成功,
用if(NULL!=p)来验证是否分配成功, 通常是成功的
****手动释放动态内存:
free(p),同时,p=NULL;
释放p所指向的那块内存,这里虽然把p指向的那块内存给释放掉了,但p的值仍没有变,这个地址还是存在的,只是不能再访问这个内存,所以再次使用它之前先要置NULL。


二、栈(stack)和堆(heap)具体的区别

1、在申请方式上
  栈(stack): 现在很多人都称之为堆栈,这个时候实际上还是指的栈它由编译器自动管理,无需我们手工控制
例如,声明函数中的一个局部变量 int b 系统自动在栈中为b开辟空间;在调用一个函数时,系统自动的给函数的形参变量在栈中开辟空间
  堆(heap): 申请和释放由程序员控制,并指明大小容易产生memory leak

在C中使用malloc函数

如:p1 = (char *)malloc(10);

在C++中用new运算符

如:p2 = new char[20];//(char *)malloc(10);

但是注意p1本身在全局区,而p2本身是在栈中的,只是它们指向的空间是在堆中

2、申请后系统的响应上

栈(stack):只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出
  堆(heap): 首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,
然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,
这样,代码中的delete或free语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,
系统会自动的将多余的那部分重新放入空闲链表中
  3、申请大小的限制

栈(stack):在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。
这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,
在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow
因此,能从栈获得的空间较小 例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)当然,我们可以修改:打开工程,依次操作菜单如下:
Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit大的值,可能增加内存的开销和启动时间。

堆(heap): 堆是向高地址扩展的数据结构,是不连续的内存区域(空闲部分用链表串联起来)。正是由于系统是用链表来存储空闲内存,自然是不连续的,
而链表的遍历方向是由低地址向高地址,一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的由此可见,
堆获得的空间比较灵活,也比较大。

4、分配空间的效率上

栈(stack):栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高
但程序员无法对其进行控制
  堆(heap):是C/C++函数库提供的,由new或malloc分配的内存,一般速度比较慢,而且容易产生内存碎片显然,堆的效率比栈要低得多

5、堆和栈中的存储内容

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

6、存取效率的比较

这个应该是显而易见的拿栈上的数组和堆上的数组来说:

void main()
  {
  int arr[5]={1,2,3,4,5};
  int *arr1;
  arr1=new int[5];
  for (int j=0;j<=4;j++)
  {
  arr1[j]=j+6;

}
  int a=arr[1];
  int b=arr1[1];
  }

上面代码中,arr1(局部变量)是在栈中,但是指向的空间确在堆上,两者的存取效率,当然是arr高,因为arr[1]可以直接访问,
但是访问arr1[1],首先要访问数组的起始地址arr1,然后才能访问到arr1[1]


三、内存的释放:
学过C语言的都知道,内存分配了用完之后是要释放的,都是到malloc和calloc函数以及free函数。那么分配了内存之后是不是真就free(pointer)这么简单呢?

 这里提及要注意的地方:参数pointer必须是调用malloc或calloc函数后返回的指针,而给free函数传递其它的值可能会造成死机或者结果是灾难性的。
                       重点是指针的值,而不是用来申请动态内存的指针本身。

可以看下代码,

假如先前有void * p =malloc(sizeof(double)*6);

也有double * dp=(double )malloc(sizeof(double)6));

那么此刻如果free(dp)就会出现不可预知的错误,free(p)是正确的,

若又p=dp,(或者p=(void *)dp),然后free(p)也是正确的

所谓灾难性:无非就是释放内存中出现把不该释放的东西给释放了,然后引起了一些问题。

那么,怎么来验证free(dp)就是错误的呢?这也许是个内存泄露的问题,呵呵。

可以试下这样一段代码:

for(;;)

{

double * p=malloc(sizeof(double)*6);

free(p);

}

然后,看看你的内存是否超支(不够)了?

正确的说法:

假如先前有double * p =malloc(sizeof(double)*6);

那么此刻如果free(p+1)就会出现不可预知的错误,free(p)是正确的,

原因是:分配内存函数对该地址值及对应的内存块有记忆,而p+1不在记忆列表里,所以free(p+1)会引发灾难性错误

可以试下这样一段代码:

for(;;)

{

double * p=malloc(sizeof(double)*6);

free(p+1);

}

再看看realloc函数,它可以用来重新分配经m,c,r三者分配的内存。那么重新分配真的是给一块新的地址嘛?

  事实上并不是这样的,r有两个参数,一个是指针,引用之前分配的内存,重新分配的内存是在原来基础之上,大小则由第二个参数决定。
  也就是说,如果你家庭总收入6000元,总管(通常是母的)给儿子分配了1000元的零花钱,现在由于一些"不可抗力"因素,
  要重新分配money,那么,传递参数realloc(1000元的地址,newsize),newsize<=1000U。而本质上是将儿子手中的money根据newsize抽走一部分,然后剩下的会做一些处理。

动态内存分配的一些原则:

1、需要时分配,用完就释放,特别是堆上的(资源很有限)。

2、避免分配大量小块内存,因为堆上内存的分配由于有系统开销,所以分配许多的小内存比分配几块大内存开销要大,而已不便于释放和管理。

3、编程的时候始终把用户有限的内存放在心上,分配了就要考虑在哪里释放。

4、循环中分配内存一定要小心翼翼

5、释放内存之前,确保不会无意中覆盖堆上分配的内存地址,否则会出现内存泄露

//手动分配一块内存给学生,输入学生的个人基本信息,并打印出来。

include <stdio.h>

include <malloc.h>

struct student
{
int num;
char *name;
char sex;
float score;
};
void main()
{
struct student *p=(struct student *)malloc(sizeof(struct student));
if(NULL==p)
{
printf("is wrong");
return ;
}
p->num=1;
p->name="xxx";
p->sex='m';
p->score=90;
printf("%d %s %c %.1f",p->num,p->name,p->sex,p->score);
}

//手动分配的内存只能通过指向他的指针来访问他,所以这个指针的值不要搞丢了

struct node
{
int data;
struct node *next;
};

struct node *head =(struct noode *)malloc(sizeof(struct node));

struct node *p =(struct noode *)malloc(sizeof(struct node));

head->next=p;

p->next=q;

q->next=NULL;

创建一个链表链接

include <stdio.h>

include <malloc.h>

struct node
{
int data;//数据域
struct node *next;//指针域
};

struct node * create()
{
int n,i;
printf("请输入要创建链表的节点数");
scanf("%d",&n);
getchar();
//创建头节点
struct node *head=(struct node *)malloc(sizeof(struct node));
//新建p节点,首节点
struct node *p =(struct node *)malloc(sizeof(struct node));
printf("请输入数据:");
scanf("%d",&p->data);
getchar();
//链接头节点和首节点
head->next=p;
for(i=1;i<n;i++)
{
//新建q节点
struct node *q =(struct node *)malloc(sizeof(struct node));
printf("请输入数据:");
scanf("%d",&q->data);
getchar();
p->next=q;
p=q;
}
p->next=NULL;
return head;
}
void print(struct node *head)
{
struct node *p=head->next;
while(p)
{
printf("%d\n",p->data);
p=p->next;
}
}
//头插
struct node * T_insert(struct node *head)
{
struct node *q =(struct node *)malloc(sizeof(struct node));
printf("input data:");
scanf("%d",&q->data);
getchar();
q->next=head->next;//先连接后面的节点,再连接前面的节点
head->next=q;
return head;
}

void main()
{
struct node *head;
head=create();
print(head);
head=T_insert(head);
print(head);
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • C语言中内存分配 在任何程序设计环境及语言中,内存管理都十分重要。在目前的计算机系统或嵌入式系统中,内存资源仍然是...
    一生信仰阅读 1,073评论 0 2
  • (JG-2014-08-20)(前半部分经过网上多篇文章对比整理)(后半部分根据ExceptionalCpp、C+...
    JasonGao阅读 5,544评论 2 23
  • 前言:C语言是Java、Objective-C、C++等高级语言的基础、也是跨平台开发的基础,指针是C语言的重中之...
    androidjp阅读 1,649评论 8 40
  • 多线程、特别是NSOperation 和 GCD 的内部原理。运行时机制的原理和运用场景。SDWebImage的原...
    LZM轮回阅读 1,982评论 0 12
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 1,876评论 0 7