C变量的作用域,和存储空间分配1.C变量的作用域2.变量修饰符3.动态分配内存4.C变量的存储空间实现机制5.字节对齐.一.概述C变量的作用域C的变量作用域是指一个变量在一个程序中能被有效访问范围.按范围来划分,分为变量的作用域分为局部、全局、文件三种范围.变量的作用域都是通过它在程序中的位置隐式说明的。因此开发者必须对变量的作用域有所了解.变量的存储变量是对程序中数据的存储空间的抽象,变量存储包括两种情况,一种是被链接器链接成可执行程序后,变量在可执行程序文件里所占的空间.一种是运行后变量在内存所占空间的分布.两种情况要分开讨论.前者我们称为存储态,后者称为运行态.两者有共同点也有区别,在后面将分开讨论.C变量的作用域属于C语言的语法问题,所有标准C程序都遵循相同规则.如果错误的使用常造成无法编译通过.但是讨论变量存储不能脱离CPU,操作系统和编译器的类型来谈,特别对需要经常在不同操作系统下编程的开发者.但是此类问题有一定共性.为方便讨论,后文均指32位CPU和Windows操作系统.对变量的存储理解不清楚,最常犯的错识就是对不能修改的字符串区进行修改造成程序崩溃.二.变量的作用域C的变量作用域是指一个变量在一个程序中能被有效访问范围.按范围来划分,分为变量的作用域分为局部、全局、文件三种范围.1.局部作用域声明在函数内部的变量都是局部作用域,无法被其他函数的代码所访问。函数的形式参数的作用域也是局部的,它们的作用范围仅限于函数内部所用的语句块。还有一种特殊是函数中的用{}包括语句块定义变量也是局部作用域,而且只在语法块里有效.上面例子里的三个num变量都是局部变量,只在本身函数里可见。,在两个函数出现同名的变量不会互相干扰。但同一函数定义同名变量,最内层会覆盖外层同名变量(上例中红色num会覆盖蓝色num,在执行完语法块后,红色num失效,程序对num的引用又变成了对函数参数的引用).所以上面的两个输出,在主函数里仍然是5,在add()函数里输出是1,6。2.全局作用域对于具有全局作用域的变量,我们可以在程序的任何位置访问它们。当一个变量是在所有函数的外部声明,那么这个变量就是全局变量。voidadd(int);main(){intnum=5;add(num);printf(%d\n,num);/*输出5*/}voidadd(intnum){num++;{intnum=1;printf(%d\n,num);/*输出1*/}printf(%d\n,num);/*输出6*/}}printf(%d\n,num);/*输出6*/}add()里的num修改是局域变量.对main中的全局变量num不影响.因此输出是6,36.文件作用域文件作用域是指外部标识符仅在声明它的同一个转换单元内的函数汇总可见。所谓转换单元是指定义这些变量和函数的源代码文件(包括任何通过#include指令包含的源代码文件)。static存储类型修饰符指定了变量具有文件作用域。换句话说,在一个源码文件里的用static修饰的变量和函数,只能被同一个源码文件里的函数所引用/调用.这在没有private和protected关键字的C语言里,用static是实现数据封装,防止被外部程序改动的一种主要手段,大量在程序中被采用.上面的程序中变量num和函数add()在声明是采用了static存储类型修饰符,这使得它们具有文件作用域,仅在定义它们的文件内可见。voidadd(int);intnum=3;main(){intn=5;add(n);printf(%d\n,num);/*输出3*/}voidadd(intnum){num++;printf(%d\n,num);/*输出6*/}staticintnum;staticvoidadd(int);main(){scanf(%d,&num);add(num)printf(%d\n,num);}voidadd(num){num++;}局域变量是指定义:在函数内定义,只在本函数内有效main中定义的变量只在main中有效不同函数中同名变量,占不同内存单元形参属于局部变量可定义在复合语句中有效的变量局部变量可用存储类型:autoregisterstatic(默认为auto)全局变量---外部变量:在函数外定义,可为本文件所有函数共用有效范围:从定义变量的位置开始到本源文件结束,及有extern说明的其它源文件外部变量声明:extern数据类型变量表;外部变量定义与外部变量说明不同,定义是产生实际效果的语句,在整个项目里,同名的全局变量只能定义一次,外部变量声明只是说明全局变量的类型和名字,可以出现多次.外部变量可用存储类型:缺省或staticintp=1,q=5;floatf1(inta){intb,c;…….}intf3(){…..}charc1,c2;charf2(intx,inty){inti,j;……}main(){intm,n;…….}c1,c2的作用范围p,q的作用范围externcharc1,c2;externcharc1,c2;c1,c2的作用范围扩展后c1,c2的作用范围扩展后例外部变量定义与说明intmax(intx,inty){intz;z=xy?x:y;return(z);}main(){externinta,b;printf(max=%d,max(a,b));}inta=13,b=-8;运行结果:max=13externinta,b;intmax(){intz;z=ab?a:b;return(z);}main(){printf(max=%d,max());}inta=13,b=-8;上面两个例子运行结果都是一样,只是显示如何通过extern来扩大全局的使用范围.另外一个重要一点是.extern只是声明,不产生实际空间,因此对其声明变量赋初值是有编译错误的.externinta=1;/*错误,编译器会产生编译错误*/按软件工程学的观念,全局应该少用,因为他会带来许多问题全局变量在程序全部执行过程中占用存储单元降低了函数的通用性、可靠性,可移植性降低程序清晰性,容易出错三.变量的存储修饰符各种变量的作用域不同,本质上是因为变量存储类型不同.主要是指运行态时存储占用内存空间的方式.变量的存储方式可分为”静态存储”和”动态存储”两种.静态存储变量通常是指在变量定义时就分布存储单元并一直保持不变.直至整个程序结束.比如上面所说的全局变量.动态存变量是在程序执行过程中,使用时才分配存储单元,使用完毕立即释放.典型的例子是函数的形式参数,在函数定义时并不给形参分配存储单元,只是函数被调用时才以分配,调用函数完毕立即释放.如果一个函数被多次调用,则反复地分配,释放形参变量的存储单元.静态存储变量是在程序运行时是一直存在的,而动态存储变量则时而存在时面消失.这种由于存储方式产生不同而产生存在时间不同的特性称为变量的生存期.生存期表示变量存在的时间范围.生存期和作用域是从时间和空间这两个不同角度来描述变量的特性,这两者即有联系,又有区别.一个变量究竟属于哪一种存储方式,并不能仅仅从它的作用域来判断,还应有明确的存储类型声明.在C语言,对变量的存储类型声明有以下4种自动(auto)、静态(static)、外部(extern)、寄存器(regiser)。其中auto,register属于动态存储方式,extern变量和static变量属于静态存储试式.1.自动存储类型(auto)自动存储类型修饰符指定了一个局部变量为自动的,这意味着,每次执行到定义该变量的语句块时,都将会为该变量在内存中产生一个新的拷贝,并对其进行初始化。实际上,如果不特别指明,局部变量的存储类型就默认为自动的,因此,加不加auto都可以。main(){autointnum=5;/*等于intnum=5;*/printf(%d\n,num);}在这个例子中,不论变量num的声明是否包含关键字auto,代码的执行效果都是一样的。函数的形式参数存储类型默认也是自动的。2.静态存储变量(static)前面已经使用了static关键字,但是对于局部变量,静态存储类型的意义是不一样的,这时,它是和自动存储类型相对而言的。静态局部变量的作用域仍然近局限于声明它的语句块中,但是在语句块执行期间,变量将始终保持它的值。而且,初始化值只在语句块第一次执行是起作用。在随后的运行过程中,变量将保持语句块上一次执行时的值。上面两个源文件,只有函数add()里的变量声明有所不同,一个是自动存储类型,一个是静态存储类型。对于1.C文件,输出结果为515151;这很好理解,每次初始值都是50,然后加1上来。对于2.C文件,输出结果为515253;这是由于变量是静态的,只在第一次初始化了50,以后都是使用上次的结果值。当第一次调用add()时,初始化为50,然后加1,输出为51;当第二次调用时,就不初始化了,这时num的值为上次的51,然后加1,输出52;当第三次调用时,num为52,加1就是53了。在局部变量的定义前加上static标识符就构成静态局域变量.静态局域变量有如下特点,它有如下特点:静态局部变量在函数内定义,但不像自动变量一样,当调用时就存在,退出函数时就消失.静态局部变量始终存在,也就是说它的生存期为整个源程序静态局部变量的生存期虽然为整个源程序,但是其作用域与自动变量相同,即可能在定义该变量的的函数内使用该变量.退出该函数后,尽管该变量还继续存在,但不能使用它允许对静态局域变量赋初值,若未赋初值,则系统自动赋以0值在全局变量定义前加上static标识符就构成了静态全局变量.他与非静态的全局变量的最大区别是静态全局变量作用域是当前源程序.而全局变量作用域是整个源程序3.外部存储类型(extern)外部存储类型声明了程序将要用到的、但尚未定义的外部变量。通常,外部存储类型都是用于声明在另一个转换单元中定义的变量。这在前一节已经解释过./*1.C*/intadd();main(){intresult;result=add()printf(%d,result);result=add();printf(%d,result);result=add();printf(%d,result);}intadd(){intnum=50;num++;returnnum;}/*2.C*/intadd();main(){intresult;result=add();printf(%d,result);result=add();printf(%d,result);result=add();printf(%d,result);}intadd(){staticintnum=50;num++;returnnum;}外部变量的声明既可以在引用它的函数的内部,也可以在外部。如果变量声明在函数外部,那么同一转换单元内的所有函数都可以使用这个外部变量。反之,如果在函数内部,那么只有这一个函数可以使用该变量。4.寄存器存储类型(register)被声明为寄存器存储类型的变量,除了程序无法得到其地址外,其余都和自动变量一样。使用寄存器存储类型的目的是让程序员指定某个局部变量存放在计算机的某个硬件寄存器里而不是内存中,以提高程序的运行速度。不过,这只是反映了程序员的主观意愿,编译器可以忽略寄存器存储类型修饰符。寄存器变量的地址是无法取得的,因为绝大多数计算机的硬件寄存器都不占用内存地址。而且,即使编译器忽略寄存器类型修饰符把变量放在可设定地址的内存中,我们也无法取地址的限制仍然存在。关于寄存器变量的说明:只有局部自动变量和形式参数才可以定义为寄存器变量,因为寄存器变量属于动态存储方式.凡需要采用静态存储方式的的变量不能定义为寄存器变量在某一些C编译器中,如TurboC,MSC中,实际上把寄存器变量当成自动变量处理,因此程序的运行速度并不能提高,在程序中允许使用寄存器变量只是为了与标准C保持一致即使能真正使用寄存器变量的机器,由于CPU的寄存器的个数是有限