C语言的内存分析

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

注明:变量的类别:参考的网络资源链接:内存管理:参考的网络资源链接:函数:参考网络资源链接一、标识符的作用域和存储类型1.局部变量和全局变量及其作用域(1).局部变量1.概念:局部变量就是指在函数内部和复合语句内部定义的变量,局部变量也称内部变量。函数的形参属于局部变量。2.作用域:在一个函数内部定义的变量,它们只在函数范围内有效,即只有本函数才能使用它们,其他函数不能使用这些变量。不同的函数可以使用具有相同名字的局部变量,它们代表不同的对象,在内存中占不同的单元,互不干扰。而在复合语句内部定义的变量,其只在复合语句内部有效。(2).全局变量1.概念:在函数之外定义的变量称为外部变量,外部变量是全局变量。2.作用域:全局变量可以为本文件其他函数所共用,全部变量作用于全局,在整个程序运行期间值都被保存,全局变量的作用范围是从其定义处开始,直至程序结束,(3).局部变量与全局变量的关系如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量被“屏蔽”,即它不起作用。2.变量的存储类别每一个变量和函数所具有的两个属性是:数据的存储类别和数据类型(1).概念:数据的存储类别指的是数据在内存中存储的方法。(2).分类:1.静态存储类:static(静态),extern(外部)①static(静态型)static称为静态存储类型,在C语言中,既可以在函数体内,也可在函数体外说明static存储类型的变量。在函数体内说明的static存储类型的变量也是一种局部变量,与auto最大不同点是:static存储类型的变量在内存中是以固定地址存放的,而不是以堆栈方式存放的;只要整个程序还在继续运行静态变量就不会随着说明它的程序段的结束而消失,即使是退出函数后,下次再进入该函数时,静态局部变量仍使用原来的存储单元。由于不释放这些存储单元,因而可以继续使用存储单元中的原值。static类型的变量只被初始化一次,其初值是在编译时赋予的,在程序执行期间不再赋初值。对于未赋初值的静态局部变量,C语言编译程序自动给它赋初值为0。表现在外的就是静态局部变量的值有继承性,如,有下面的程序段:voidfun(){inti=0;i++;printf(“%d\n”,i);}intmain(){fun();fun();fun();return0;}在主函数中3次调用fun函数,这时在终端上输出的内容为:1,1,1而我们将程序更改一下,将第三行语句换作:staticinti=0;则程序在终端上的输出为:1,2,3②extern(外部)extern称为外部参照引用型,使用extern说明的变量是想引用在其它文件中或在函数体外部已经声明过的变量(全局变量),而extern关键字的作用是对全局变量进行引用的声明。如下面的程序:inta=1;intmain(){printf(“a=%d\n”,a);return0;}inta=1;该程序可以成功运行,输出a的值为1,而如果改变一下:intmain(){printf(“a=%d\n”,a);return0;}这样在进行编译时,编译器会报错,提示变量a没有定义。也就是说全局变量的作用范围是从定义处开始直至程序结束,而我们在该程序的基础上加上一句声明:externa;intmain(){printf(“a=%d\n”,a);return0;}inta=1;加上一个extern的说明后,该程序可以正常编译执行,在这里要注意extern关键字对于变量的类型说明可以省略,但对于变量的类型及值是没有任何改变权限的,也就是说在上面例子中的extern语言如果写成exterfloata;或者是externinta=3;编译时会提示错误。所以extern对于变量的类型及值没有任何更改的功能,只能是对于外部变量的定义进行一个引用的声明,换句话说,它扩大了外部变量的作用范围,所以extern是一个说明性关键字,而不是定义性的,说明与定义的区别我们可以来对比一下:2.动态存储类:auto(自动),register(寄存器)①auto(自动型)auto存储类型说明的变量都是局部于某个程序范围内的,只能在某个程序范围内使用,通常在函数体内或函数中的复合语句里。C语言中,在函数体的某程序段内说明auto存储类型的变量时可以省略关键字auto。例:autointa;//说明一个auto整型的a变量inta;//省略了auto,说明一个auto整型的a变量说明:这2种定义方式是等价的,通常情况下,在函数内部或复合语句内定义变量时,如果没有指定存储类别,或使用了auto说明符,系统就认为所定义的变量属于自动类别。这是static关键字的用法及特点。②register寄存器型register称为寄存器型,也是一种自动类变量。使用register关键词说明的变量主要目的是想将所说明的变量放入寄存器存储空间中,我们知道寄存器数量有限,且位于CPU的内部,这样可以加快程序的运行速度。但正因为寄存器的资源相对较少,所以编译器会判断程序所指定的需要放在寄存器中的内容有没有必要放入寄存器中去,也就是说,编译器来决定是否将指定内容放入到寄存器中,如果没有没有必要放入寄存器中,就使用auto类型作处理。综上所述,register是一个建议性关键字,编译器可以判断出是否去执行这样一个关键字,所以这个关键字在目前的用处相对地减少很多。二、内存管理1.概述:看下面的简略图大家可以明确对于一个C语言程序而言,内存空间主要由五个部分组成代码段(.text)、数据段(.data)、BSS段(.bss)、堆和栈组成,其中,程序在进行存储时是分为三个区域的,分别为代码段,数据段和bss段,用于存储不同类型和状态的变量,是编译的时候由编译器分配的;而运行起来系统会再为其添加2个段,一个是堆,一个是栈,其中我们手动进行的内存空间的申请是分配在堆区中的,栈区中存放的是程序在运行的过程中产生出来的一些临时信息和数据。堆和栈是程序运行的时候由系统分配的。其布局如下:在上图中,由编译器分配的地址空间都是在连接的时候分配的,而运行时分配的空间是在程序运行时由系统分配的2.分类:(1).BSS段:BSS段(bsssegment)通常是指用来存放程序中未初始化的全局变量和静态变量(这里注意一个问题:一般的书上都会说全局变量和静态变量是会自动初始化的,那么哪来的未初始化的变量呢?变量的初始化可以分为显示初始化和隐式初始化,全局变量和静态变量如果程序员自己不初始化的话的确也会被初始化,那就是不管什么类型都初始化为0,这种没有显示初始化的就是我们这里所说的未初始化。既然都是0那么就没必要把每个0都存储起来,从而节省磁盘空间,这是BSS的主要作用)的一块内存区域。BSS是英文BlockStartedbySymbol的简称。BSS段属于静态内存分配。BSS节不包含任何数据,只是简单的维护开始和结束的地址,即总大小,以便内存区能在运行时分配并被有效地清零。BSS节在应用程序的二进制映象文件中并不存在,即不占用磁盘空间而只在运行的时候占用内存空间,所以如果全局变量和静态变量未初始化那么其可执行文件要小很多。(2).数据段:数据段(datasegment)通常是指用来存放程序中已初始化的全局变量和静态变量的一块内存区域。数据段属于静态内存分配,可以分为只读数据段和读写数据段。字符串常量等,但一般都是放在只读数据段中。(3).代码段:代码段(codesegment/textsegment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等,但一般都是放在只读数据段中。(4).堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)(5).栈(stack):栈又称堆栈,是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。注意:栈空间是向下增长的,每个线程有一个自己的栈,在linux上默认的大小是8M,可以用ulimit查看和修改。3.堆和栈的比较:(1).栈系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活;(2).而堆是函数库提供的功能,特点是灵活方便,数据适应面广泛,但是效率有一定降低。三、动态存储函数1.malloc()函数(1).函数原型:void*malloc(unsignedintsize);(2).函数的作用:系统自动在内存的动态存储区中(即堆栈),分配size的一段连续空间。若此函数执行成功,则函数返回值为指向被分配域的起始地址的指针,即首地址(该函数的返回值的类型是void*,是一个泛型指针,其基本类型为int);若该函数执行失败(如内存空间不足的情况),则函数返回值为空指针(NULL)。(3).头文件:stdlib.h或malloc.h(4).说明:1.在ANSIC中,malloc()函数返回的地址为void*泛型指针,所以在调用该函数时,必须利用强制类型转换将其转换成所需的类型。此处括号中的*号不能少!例:int*p;p=(int*)malloc(sizeof(int));2.一般情况下,若不能确定数据所占字节数,可以使用sizeof运算符来求得,见上3.由动态分配的存储单元没有名字,只能靠指针来引用它。一旦指针改变指向,原存储单元即所存储的数据都将无法再引用,小心使用。4.通过调用malloc()函数所分配的动态存储单元没有确定的初值。2.free()函数(1).函数原型:voidfree(void*p);(2).函数功能:释放由p所指向的那段内存空间,使这段存储空间能为他用。p是最近一次调用malloc()或calloc()函数时放回的值。(3).注意:1.free()函数无返回值2.对曾经使用动态存储来进行分配的指针变量,要在程序结束前,全部用free()函数释放,因为在程序结束前,全部用free()函数释放可以节约内存,同时又保证内存的安全管理。3.calloc()函数(1).函数原型:void*calloc(unsignedn,unsignedsize);(2).函数功能:在内存的动态存储区(即堆栈)中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。(3).跟malloc的区别:calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。(4).头文件:stdlib.h或malloc.h4.realloc()函数(1).函数原型:realloc(void*__ptr,size_t__size);(2).函数功能:更改已经配置的内存空间,即更改由malloc()函数分配的内存空间的大小。如果将分配的内存减少,realloc仅仅是改变索引的信息;如果是将分配的内存扩大,则有以下情况:1)如果当前内存段后面有需要的内存空间,则直接扩展这段内存空间,realloc()将返

1 / 5
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功