C变量的作用域和存储空间分配2Word文档下载推荐.docx
- 文档编号:18639439
- 上传时间:2022-12-30
- 格式:DOCX
- 页数:21
- 大小:320.85KB
C变量的作用域和存储空间分配2Word文档下载推荐.docx
《C变量的作用域和存储空间分配2Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《C变量的作用域和存储空间分配2Word文档下载推荐.docx(21页珍藏版)》请在冰豆网上搜索。
static存储类型修饰符指定了变量具有文件作用域。
换句话说,在一个源码文件里的用static修饰的变量和函数,只能被同一个源码文件里的函数所引用/调用.这在没有private和protected关键字的C语言里,用static是实现数据封装,防止被外部程序改动的一种主要手段,大量在程序中被采用.
上面的程序中变量num和函数add()在声明是采用了static存储类型修饰符,这使得它们具有文件作用域,仅在定义它们的文件内可见。
●局域变量是指定义:
在函数内定义,只在本函数内有效
●main中定义的变量只在main中有效
●不同函数中同名变量,占不同内存单元
●形参属于局部变量
●可定义在复合语句中有效的变量
●局部变量可用存储类型:
autoregisterstatic(默认为auto)
●全局变量---外部变量:
在函数外定义,可为本文件所有函数共用
●有效范围:
从定义变量的位置开始到本源文件结束,及有extern说明的其它源文件
●外部变量声明:
extern数据类型变量表;
●外部变量定义与外部变量说明不同,
●定义是产生实际效果的语句,在整个项目里,同名的全局变量只能定义一次,外部变量声明只是说明全局变量的类型和名字,可以出现多次.
●外部变量可用存储类型:
缺省或static
上面两个例子运行结果都是一样,只是显示如何通过extern来扩大全局的使用范围.
另外一个重要一点是.extern只是声明,不产生实际空间,因此对其声明变量赋初值是有编译错误的.
externinta=1;
/*错误,编译器会产生编译错误*/
按软件工程学的观念,全局应该少用,因为他会带来许多问题
✡全局变量在程序全部执行过程中占用存储单元
✡降低了函数的通用性、可靠性,可移植性
✡降低程序清晰性,容易出错
三.变量的存储修饰符
各种变量的作用域不同,本质上是因为变量存储类型不同.主要是指运行态时存储占用内存空间的方式.变量的存储方式可分为”静态存储”和”动态存储”两种.
静态存储变量通常是指在变量定义时就分布存储单元并一直保持不变.直至整个程序结束.比如上面所说的全局变量.
动态存变量是在程序执行过程中,使用时才分配存储单元,使用完毕立即释放.典型的例子是函数的形式参数,在函数定义时并不给形参分配存储单元,只是函数被调用时才以分配,调用函数完毕立即释放.如果一个函数被多次调用,则反复地分配,释放形参变量的存储单元.
静态存储变量是在程序运行时是一直存在的,而动态存储变量则时而存在时面消失.这种由于存储方式产生不同而产生存在时间不同的特性称为变量的生存期.生存期表示变量存在的时间范围.
生存期和作用域是从时间和空间这两个不同角度来描述变量的特性,这两者即有联系,又有区别.一个变量究竟属于哪一种存储方式,并不能仅仅从它的作用域来判断,还应有明确的存储类型声明.在C语言,对变量的存储类型声明有以下4种
●自动(auto)、
●静态(static)、
●外部(extern)、
●寄存器(regiser)。
其中auto,register属于动态存储方式,extern变量和static变量属于静态存储试式.
1.自动存储类型(auto)
自动存储类型修饰符指定了一个局部变量为自动的,这意味着,每次执行到定义该变量的语句块时,都将会为该变量在内存中产生一个新的拷贝,并对其进行初始化。
实际上,如果不特别指明,局部变量的存储类型就默认为自动的,因此,加不加auto都可以。
main()
{
auto
int
num=5;
/*等于intnum=5;
*/
printf("
%d\n"
num);
}
在这个例子中,不论变量num的声明是否包含关键字auto,代码的执行效果都是一样的。
函数的形式参数存储类型默认也是自动的。
2.静态存储变量(static)
前面已经使用了static关键字,但是对于局部变量,静态存储类型的意义是不一样的,这时,它是和自动存储类型相对而言的。
静态局部变量的作用域仍然近局限于声明它的语句块中,但是在语句块执行期间,变量将始终保持它的值。
而且,初始化值只在语句块第一次执行是起作用。
在随后的运行过程中,变量将保持语句块上一次执行时的值。
/*2.C*/
intadd();
main()
{
intresult;
result=add();
printf("
%d"
result);
%d"
}
intadd()
staticintnum=50;
num++;
returnnum;
上面两个源文件,只有函数add()里的变量声明有所不同,一个是自动存储类型,一个是静态存储类型。
对于1.C文件,输出结果为51
51
51;
这很好理解,每次初始值都是50,然后加1上来。
对于2.C文件,输出结果为51
52
53;
这是由于变量是静态的,只在第一次初始化了50,以后都是使用上次的结果值。
当第一次调用add()时,初始化为50,然后加1,输出为51;
当第二次调用时,就不初始化了,这时num的值为上次的51,然后加1,输出52;
当第三次调用时,num为52,加1就是53了。
在局部变量的定义前加上static标识符就构成静态局域变量.静态局域变量有如下特点,它有如下特点:
●静态局部变量在函数内定义,但不像自动变量一样,当调用时就存在,退出函数时就消失.静态局部变量始终存在,也就是说它的生存期为整个源程序
●静态局部变量的生存期虽然为整个源程序,但是其作用域与自动变量相同,即可能在定义该变量的的函数内使用该变量.退出该函数后,尽管该变量还继续存在,但不能使用它
●允许对静态局域变量赋初值,若未赋初值,则系统自动赋以0值
在全局变量定义前加上static标识符就构成了静态全局变量.他与非静态的全局变量的最大区别是静态全局变量作用域是当前源程序.而全局变量作用域是整个源程序
3.外部存储类型(extern)
外部存储类型声明了程序将要用到的、但尚未定义的外部变量。
通常,外部存储类型都是用于声明在另一个转换单元中定义的变量。
这在前一节已经解释过.
外部变量的声明既可以在引用它的函数的内部,也可以在外部。
如果变量声明在函数外部,那么同一转换单元内的所有函数都可以使用这个外部变量。
反之,如果在函数内部,那么只有这一个函数可以使用该变量。
4.寄存器存储类型(register)
被声明为寄存器存储类型的变量,除了程序无法得到其地址外,其余都和自动变量一样。
使用寄存器存储类型的目的是让程序员指定某个局部变量存放在计算机的某个硬件寄存器里而不是内存中,以提高程序的运行速度。
不过,这只是反映了程序员的主观意愿,编译器可以忽略寄存器存储类型修饰符。
寄存器变量的地址是无法取得的,因为绝大多数计算机的硬件寄存器都不占用内存地址。
而且,即使编译器忽略寄存器类型修饰符把变量放在可设定地址的内存中,我们也无法取地址的限制仍然存在。
关于寄存器变量的说明:
●只有局部自动变量和形式参数才可以定义为寄存器变量,因为寄存器变量属于动态存储方式.凡需要采用静态存储方式的的变量不能定义为寄存器变量
●在某一些C编译器中,如TurboC,MSC中,实际上把寄存器变量当成自动变量处理,因此程序的运行速度并不能提高,在程序中允许使用寄存器变量只是为了与标准C保持一致
●即使能真正使用寄存器变量的机器,由于CPU的寄存器的个数是有限的,因此使用寄存器变量的个数也是有限的.并不是程序使用register,编译器就一定会把它编译成寄存器变量.
●在现代的C编译器,优化已经做得相当出色了,需要优化地方基本上会自动优化.纯粹为了提高运行速度.用register显得多余.
四.动态内存分配
在使用数组的时候,总有一个问题困扰着我们:
数组应该有多大?
在很多的情况下,你并不能确定要使用多大的数组.即便是你的程序在运行时就申请了固定大小的你认为足够大的内存空间。
但是如果因为某种特殊原因人数有增加或者减少,你又必须重新去修改程序,扩大数组的存储范围。
这种分配固定大小的内存分配方法称之为静态内存分配。
但是这种内存分配的方法存在比较严重的缺陷,特别是处理某些问题时:
在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。
那么有没有其它的方法来解决这样的外呢体呢?
有,那就是动态内存分配。
所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。
动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
从以上动、静态内存分配比较可以知道动态内存分配相对于静态内存分配的特点:
●不需要预先分配存储空间;
●分配的空间可以根据程序的需要扩大或缩小。
如何实现动态内存分配及其管理
要实现根据程序的需要动态分配存储空间,就必须用到以下几个函数
1、malloc函数
malloc(配置内存空间)
相关函数
calloc,free,realloc,brk
表头文件
#include<
stdlib.h>
定义函数
void*malloc(size_tsize);
函数说明
malloc()用来配置内存空间,其大小由指定的size决定。
返回值
若配置成功则返回一指针,失败则返回NULL。
范例
void*p=malloc(1024);
/*配置1k的内存*/
其作用是在内存的动态存储区中分配一个长度为size的连续空间。
其参数是一个无符号整形数,返回值是一个指向所分配的连续存储域的起始地址的指针。
还有一点必须注意的是,当函数未能成功分配存储空间(如内存不足)就会返回一个NULL指针。
所以在调用该函数时应该检测返回值是否为NULL并执行相应的操作。
下例是一个动态分配的程序:
上例中动态分配了10个整型存储区域,然后进行赋值并打印。
例中
if((array=(int*)malloc(10*sizeof(int)))==NULL)语句可以分为以下几步:
1)分配10个整型的连续存储空间,并返回一个指向其起始地址的整型指针
2)把此整型指针地址赋给array
3)检测返回值是否为NULL
2、free函数
free(释放原先配置的内存)
malloc,calloc,realloc,brk
voidfree(void*ptr);
参数ptr为指向先前由malloc()、calloc()或realloc()所返回的内存指针。
调用free()后ptr所指的内存空间便会被收回。
假若参数ptr所指的内存空间已被收回或是未知的内存地址,则调用free()可能会有无法预期的情况发生。
若参数ptr为NULL,则free()不会有任何作用。
由于内存区域总是有限的,不能不限制地分配下去,而且一个程序要尽量节省资源,所以当所分配的内存区域不用时,就要释放它,以便其它的变量或者程序使用。
这时我们就要用到free函数。
其函数原型是
voidfree(void*p)
作用是释放指针p所指向的内存区。
其参数p必须是先前调用malloc函数或calloc函数(另一个动态分配存储区域的函数)时返回的指针。
给free函数传递其它的值很可能造成死机或其它灾难性的后果。
注意:
这里重要的是指针的值,而不是用来申请动态内存的指针本身。
例:
int*p1,*p2;
p1=malloc(10*sizeof(int));
p2=p1;
……
free(p2)/*或者free(p1),如果连续释放free(p1);
free(p2);
第二次释放造成程序崩溃*/
malloc返回值赋给p1,又把p1的值赋给p2,所以此时p1,p2都可作为free函数的参数。
/*内存泄漏1:
*/
intmain()
char*p;
p=malloc(1024);
p=malloc(512);
free(p);
malloc函数是对存储区域进行分配的。
free函数是释放已经不用的内存区域的。
所以由这两个函数就可以实现对内存区域进行动态分配并进行简单的管理了。
注意静态分布内存和静态变量是两个领域的划分,如果说出两种联系,可以认为目前变量定义全部是采用静态分配空间.而只有采用malloc分配空间才能叫动态分配内存,如下列方式都是静态分配内存
下列用malloc动态分配空间,才叫动态分配内存.
从实现机制来说,malloc是从程序的堆空间(heap)选择一块空间给用户.
五.变量的存储方式实现机制
变量是对程序中数据的存储空间的抽象,变量存储包括两种情况,一种是被链接器链接成可执行程序后,变量在可执行程序文件里所占的空间.
不同的操作系统的可执行程序的格式不一样,如Windows的可执行程序移为PE格式,而Linux的可执行行格式通常为ELF格式,虽然有不同格式,但是其实现原理和总体结构类似.对于一个C的程序而言,在存储态,即程序还是一个可执行文件时.通常包含如下几个段
●Text段
●Data段
●BSS段(BlockStorageStart).
●如果包含字符串常量,还带的rodata段(在WindowsPE下叫rdata,而gcc编译出来叫rodata段)
●
不同的变量分布在哪一个段里,是一个C开发者必备知识之一.
存储态的分段
编译器会把源代码各部分按如下规则把各部分放到可执行程序文件各个段中
●代码段(textsegment):
存放CPU执行的机器指令(machineinstructions)。
也就是存储你的程序代码编译后的机器代码,在内存中,这一段是只读的.所有可执行代码即C的语句和函数都会被编译到可执行段中
●初始化数据段/数据段(initializeddatasegment/datasegmen,包含静态初始化的数据,所以有初值的全局变量和static变量在data区。
●未初始化数据段/bss段,,bss是英文BlockStartedbySymbol的简称,通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域,在程序载入时由内核清0,所有未初始化的全局变量,包括括静态或非静态的,均保存在这一个段.
●因为没有初始化数据,,为了节约可执行文件空间,所以所有在BSS段里的程序只占一个标识符空间,和记录所占空间大小,其变量所占空间没有展开,
●所有在BSS段里变量在装入时由操作系统统一清0
●rodata/rdata段,ro代表readonly,即只读数据(const)。
表示只读的数据,比如字符串文字量、常量(常量有时可以直接保存在Text);
●注意在可执行文件是不会有局域变量的空间的,因为他们要等于执行后在进程的空间动态创建.
运行态的分段
当程序运行后.除了上述段会装入进程空间,还有两个新的区分配在进程空间中.
●栈段(stack),保存函数的局部变量和函数参数。
是一种“后进先出”(LastInFirstOut,LIFO)的数据结构,这意味着最后放到栈上的数据,将会是第一个从栈上移走的数据。
●对于哪些暂时存贮的信息,和不需要长时间保存的信息来说,LIFO这种数据结构非常理想。
在调用函数或过程后,系统通常会清除栈上保存的局部变量、函数调用信息及其它的信息。
栈另外一个重要的特征是,它的地址空间“向下减少”,即当栈上保存的数据越多,栈的地址就越低。
栈(stack)的顶部在可读写的RAM区的最后。
因为栈是有限度的.因此.无限递归之类调用会将栈空间用关.
●堆段(heap):
用于动态内存分配(dynamicmemoryallocation)。
所有malloc的分配的内存空间都是从这一个区域分配出来.保存函数内部动态分配内存,是另外一种用来保存程序信息的数据结构,更准确的说是保存程序的动态变量。
●堆是“先进先出”(FirstInfirstOut,FIFO)数据结构。
它只允许在堆的一端插入数据,在另一端移走数据。
堆的地址空间“向上增加”,即当堆上保存的数据越多,堆的地址就越高。
下图显示了这5个段在内存中的典型排列。
这是一张逻辑图,表示了一个程序在内存中看起来是怎么样的。
对于一个给定的实现,没有强制的要求说必须按照这种方式来排列这5个段。
然而,这给了我们一种典型的便于描述的排列方式。
,代码段(textsegment)从地址0x08048000开始(往上),栈底从地址0xC0000000往下(在这个特定的表示结构中,栈段从高地址向低地址扩展)。
在堆顶和栈顶之间的虚拟地址空间是很大的(这保证了2个段不会互相干扰)。
关于各个变量在分配段的情况
●在Windows下可以用cl/FAtest.cpp这一命令在编译时同时导出asm语言,汇编语言清晰显示各段分配情况,其中cl.exe是VC++的命令行编译器,IDE实际调用cl.exe自动编译,手工调用是因为写入/FA命令行参数./FA表示产生跟源码同名,后缀名为.asm汇编文件.
●在Linux下,直接对带有调试信息的可执行文件执行nm命令即可得出详细分段情况
以下是编译结果
TITLEtest_cl.cpp
.386P
includelisting.inc
if@Versiongt510
.modelFLAT
else
_TEXTSEGMENTPARAUSE32PUBLIC'
CODE'
_TEXTENDS
_DATASEGMENTDWORDUSE32PUBLIC'
DATA'
_DATAENDS
CONSTSEGMENTDWORDUSE32PUBLIC'
CONST'
CONSTENDS
_BSSSEGMENTDWORDUSE32PUBLIC'
BSS'
_BSSENDS
_TLSSEGMENTDWORDUSE32PUBLIC'
TLS'
_TLSENDS
FLATGROUP_DATA,CONST,_BSS
ASSUMECS:
FLAT,DS:
FLAT,SS:
FLAT
endif
PUBLIC?
a@@3HA;
a
c@@3HA;
c
_BSSSEGMENT
?
a@@3HADD01HDUP(?
);
_DATASEGMENT
_bDD03H
c@@3HADD04H;
PUBLIC_main
EXTRN_printf:
NEAR
_dDB0aHDUP(?
)
$SG582DB'
hello!
'
00H
$SG583DB'
%d,%d,%d,%s,%d'
_TEXTSEGMENT
_e$=-4
_mainPROCNEAR
;
Filetest_cl.cpp
Line7
pushebp
movebp,esp
pushecx
Line8
movDWORDPTR_e$[ebp],3
Line9
pushOFFSETFLAT:
$SG582
call_printf
addesp,4
Line10
moveax,DWORDPTR_e$[ebp]
pusheax
_d
movecx,DWORDPTR?
c
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 变量 作用 存储空间 分配