tag 标签: 全局变量

相关博文
  • 热度 14
    2013-3-15 10:36
    1300 次阅读|
    0 个评论
    总结如下 1, 对于数组下标的看法:数组下标从0开始而不是1开始。编译器的设计者选择下标从0开始,是因为偏移量的概念在他们心中是根深蒂固的。但是这种设计让一般人 感觉很别扭。从编译器的角度来看,在访问数组中的某个元素的时候,我们先找到数组的首地址,也就是数组名所代表的地址,然后根据数组的下标(也就是地址偏 移量)来找到我们想访问的数组元素的地址,进而访问它。利用偏移量的概念,下标以0开始就不足为奇了。 2, C语言声明的优先级规则:   1   声明从它的名字开始读取,然后按照优先级顺序依次读取   2   优先级从高到低依次是:         2.1   声明中被括号包起来的那部分         2.2   后缀操作符,如()         2.3   前缀操作符:星号*   3   如果const volatile 关键字的后面紧跟类型说明符 那么它作用于类型说明符。 在其他情况下,const volatile 关键字作用于它左边紧邻的指针星号         char * const (*p)();                 这个声明表示: p是一个指针,它指向一个函数,该函数返回另一个指针,该指针指向一个类型为char的常量指针。记住:const表示“只读”,并不能因             为它的意思是常量就认为它所表示的就是一个常量。 3, 关于a.out:       a.out 这个名字怎么来的?它是“assembler output”的缩写!       你是否质疑过a.out这个名字是怎么确定的?所有输入文件都缺省的使用同一个名字a.out可能会带来不便,对于任何一个文件进行下一次编译的时候,       都有可能覆盖它。大多数人有个模糊的印象,觉得这个名字秉承而来unix传统的简洁性,而且a是字母表的第一个字母,所以会首先想到用它来命名新文件。       其实,取这个名字和这些毫无关系。       当然,这个程序并不是真正的汇编程序输出,而是连接器输入!       段(segments):表示二进制文件中简单的区域。部分(section):section 是ELF文件中的最小组织单位,一个段中包含几个section。       size a.out 会告诉你这个文件的三个段(文本段,数据段,BSS段)       BSS(Block Started by Symbol由符号开始的块)段通常是指用来存放程序中未初始化的全局变量和静态变量的一块区域,是可读可写的。在程序执行之       前BSS段会自动清零。所以,为初始化的全局变量在程序执行之前已经是0了。注意和数据段的区别,BSS存放的是未初始化的全局变量和静态变量,数据       段存放的是初始化后的全局变量和静态变量。       数据段:存放初始化后的全局变量的一块内存区域。数据段属于静态内存分配 代码段:通常指用来存放程序执行代码的一块内存区域。这部份区域的大       小在程序运行之前就已经确定,并且在内存中通常属于只读,       a.out中的神奇的数字       在linux中,可执行文件是以一种特殊的方式加上标签,这样系统就能确认他们的特殊属性(可执行文件)。标签就是一组“神奇”的数字,系统根据他们       识别可执行文件。这个神奇的数字就是7F454C46         (7F E L F)(od -x a.out od -c a.out readelf a.out)   实验: 编译“helloworld”程序,在可执行文件中执行ll,得到文件的总体大小。运行size 得到文件的各个段的大小。 增加一个全局变量int a 数组声明,重新编译,再用上面的命令得到总体以及各个段的大小 现在,在数组的声明中,增加初始值。这将使数组从BSS段转换到数据段,重复测量 现在,在函数内声明一个巨大的数组,测量。然后再声明一个巨大数组,加上初始值,测量。   结论: 数据段保存在目标文件中 BSS段不保存在目标文件中(只是记录BSS段在运行时所需的大小) 文本段是最容易受到优化措施影响的段 a.out文件大小受到调试状态下编译的影响,但是段不受影响。   让我们看看为什么a.out要以段的形式组织。段可以方便的映射到链接器在运行时可直接载入的对象中!载入器只是取文件中的每个段的映像,并直接将他们放入内存中。从本质上说,段在正在执行的程序中是一块内存区域,每个区域都有特定的目的。   文本段包含程序的指令。链接器把指令直接从文件拷贝到内存中,以后就再也不用管它。程序的文本无论是内容还是大小,一般都不会再改变。有的操作系统会对文本段设置只读属性。   数 据段包含经过初始化的全局和静态变量以及它们的值。BSS段的大小从可执行文件中得到(a.out),然后链接器得到这个大小的内存快,紧跟在数据段之 后。当这个内存区进入程序的地址空间后全部清零。包括数据段和BSS段的整个区段此时通常统称为数据区。一般情况下,数据段是最大的段。
  • 热度 21
    2012-5-22 15:03
    2992 次阅读|
    0 个评论
    变量的作用域 在讨论函数的形参变量时曾经提到, 形参变量只在被调用期间才分配内存单元,调用结束立即释放。 这一点表明形参变量只有在函数内才是有效的,离开该函数就不能再使用了。这种变量有效性的范围称变量的作用域。不仅对于形参变量, C语言中所有的量都有自己的作用域。变量说明的方式不同,其作用域也不同。 C语言中的变量,按作用域范围可分为两种, 即局部变量和全局变量。 一、局部变量 局部变量也称为内部变量。局部变量是在函数内作定义说明的。其作用域仅限于函数内, 离开该函数后再使用这种变量是非法的。 例如: int f1(int a) /*函数f1*/ { int b,c;  …… }a,b,c作用域 int f2(int x) /*函数f2*/ { int y,z;  }x,y,z作用域 main() { int m,n;  }  m,n作用域 在函数f1内定义了三个变量,a为形参,b,c为一般变量。在 f1的范围内a,b,c有效,或者说a,b,c变量的作用域限于f1内。同理,x,y,z的作用域限于f2内。 m,n的作用域限于main函数内。关于局部变量的作用域还要说明以下几点: 1. 主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用。同时,主函数中也不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平行关系。这一点是与其它语言不同的,应予以注意。 2. 形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。 3. 允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。如在例5.3 中,形参和实参的变量名都为n,是完全允许的。4. 在复合语句中也可定义变量,其作用域只在复合语句范围内。例如: main() { int s,a; …… { int b; s=a+b;  ……b作用域  } ……s,a作用域 } main() { int i=2,j=3,k; k=i+j; { int k=8; if(i==3) printf("%d\n",k); } printf("%d\n%d\n",i,k); } main() { int i=2,j=3,k; k=i+j; { int k=8; if(i=3) printf("%d\n",k); } printf("%d\n%d\n",i,k); }  本程序在main中定义了i,j,k三个变量,其中k未赋初值。而在复合语句内又定义了一个变量k,并赋初值为8。应该注意这两个k不是同一个变量。在复合语句外由main定义的k起作用,而在复合语句内则由在复合语句内定义的k起作用。因此程序第4行的k为main所定义,其值应为5。第7行输出k值,该行在复合语句内,由复合语句内定义的k起作用,其初值为8,故输出值为8,第9行输出i,k值。i是在整个程序中有效的,第7行对i赋值为3,故以输出也为3。而第9行已在复合语句之外,输出的k应为main所定义的k,此k值由第4 行已获得为5,故输出也为5。 二、全局变量 全局变量也称为外部变量,它是在函数外部定义的变量。它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。只有在函数内经过说明的全局变量才能使用。全局变量的说明符为extern。 但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。 例如: int a,b; /*外部变量*/ void f1() /*函数f1*/ { …… } float x,y; /*外部变量*/  int fz() /*函数fz*/ { …… } main() /*主函数*/ { …… }/*全局变量x,y作用域 全局变量a,b作用域*/  从上例可以看出a、b、x、y 都是在函数外部定义的外部变量,都是全局变量。但x,y 定义在函数f1之后,而在f1内又无对x,y的说明,所以它们在f1内无效。 a,b定义在源程序最前面,因此在f1,f2及main内不加说明也可使用。 输入正方体的长宽高l,w,h。求体积及三个面x*y,x*z,y*z的面积。 int s1,s2,s3; int vs( int a,int b,int c) { int v; v=a*b*c; s1=a*b; s2=b*c; s3=a*c; return v; } main() { int v,l,w,h; printf("\ninput length,width and height\n"); scanf("%d%d%d",l,w,h); v=vs(l,w,h); printf("v=%d s1=%d s2=%d s3=%d\n",v,s1,s2,s3); }  本程序中定义了三个外部变量s1,s2,s3, 用来存放三个面积,其作用域为整个程序。函数vs用来求正方体体积和三个面积,函数的返回值为体积v。由主函数完成长宽高的输入及结果输出。由于C语言规定函数返回值只有一个,当需要增加函数的返回数据时,用外部变量是一种很好的方式。本例中,如不使用外部变量,在主函数中就不可能取得v,s1,s2,s3四个值。而采用了外部变量, 在函数vs中求得的s1,s2,s3值在main 中仍然有效。因此外部变量是实现函数之间数据通讯的有效手段。对于全局变量还有以下几点说明: 1. 对于局部变量的定义和说明,可以不加区分。而对于外部变量则不然,外部变量的定义和外部变量的说明并不是一回事。外部变量定义必须在所有的函数之外,且只能定义一次。其一般形式为: 类型说明符 变量名,变量名… 其中方括号内的extern可以省去不写。 例如: int a,b; 等效于: extern int a,b; 而外部变量说明出现在要使用该外部变量的各个函数内, 在整个程序内,可能出现多次,外部变量说明的一般形式为: extern 类型说明符变量名,变量名,…; 外部变量在定义时就已分配了内存单元, 外部变量定义可作初始赋值,外部变量说明不能再赋初始值,只是表明在函数内要使用某外部变量。 2. 外部变量可加强函数模块之间的数据联系, 但是又使函数要依赖这些变量,因而使得函数的独立性降低。从模块化程序设计的观点来看这是不利的, 因此在不必要时尽量不要使用全局变量。 3. 在同一源文件中,允许全局变量和局部变量同名。在局部变量的作用域内,全局变量不起作用。 int vs(int l,int w) { extern int h; int v; v=l*w*h; return v; } main() { extern int w,h; int l=5; printf("v=%d",vs(l,w)); } int l=3,w=4,h=5;  本例程序中,外部变量在最后定义,因此在前面函数中对要用的外部变量必须进行说明。外部变量l,w和vs函数的形参l,w同名。外部变量都作了初始赋值,mian函数中也对l作了初始化赋值。执行程序时,在printf语句中调用vs函数,实参l的值应为main中定义的l值,等于5,外部变量l在main内不起作用;实参w的值为外部变量w的值为4,进入vs后这两个值传送给形参l,wvs函数中使用的h 为外部变量,其值为5,因此v的计算结果为100,返回主函数后输出。变量的存储类型各种变量的作用域不同,就其本质来说是因变量的存储类型相同。所谓存储类型是指变量占用内存空间的方式, 也称为存储方式。 变量的存储方式可分为“静态存储”和“动态存储”两种。  静态存储变量通常是在变量定义时就分定存储单元并一直保持不变,直至整个程序结束。5.5.1节中介绍的全局变量即属于此类存储方式。动态存储变量是在程序执行过程中,使用它时才分配存储单元, 使用完毕立即释放。典型的例子是函数的形式参数,在函数定义时并不给形参分配存储单元,只是在函数被调用时,才予以分配,调用函数完毕立即释放。如果一个函数被多次调用,则反复地分配、 释放形参变量的存储单元。从以上分析可知, 静态存储变量是一直存在的,而动态存储变量则时而存在时而消失。我们又把这种由于变量存储方式不同而产生的特性称变量的生存期。 生存期表示了变量存在的时间。生存期和作用域是从时间和空间这两个不同的角度来描述变量的特性,这两者既有联系,又有区别。 一个变量究竟属于哪一种存储方式,并不能仅从其作用域来判断,还应有明确的存储类型说明。 在C语言中,对变量的存储类型说明有以下四种: auto     自动变量 register   寄存器变量 extern    外部变量 static    静态变量  自动变量和寄存器变量属于动态存储方式, 外部变量和静态变量属于静态存储方式。在介绍了变量的存储类型之后,可以知道对一个变量的说明不仅应说明其数据类型,还应说明其存储类型。 因此变量说明的完整形式应为: 存储类型说明符 数据类型说明符变量名,变量名…; 例如: static int a,b;           说明a,b为静态类型变量 auto char c1,c2;          说明c1,c2为自动字符变量 static int a ={1,2,3,4,5};    说明a为静整型数组 extern int x,y;           说明x,y为外部整型变量 下面分别介绍以上四种存储类型: 一、自动变量的类型说明符为auto 这种存储类型是C语言程序中使用最广泛的一种类型。C语言规定, 函数内凡未加存储类型说明的变量均视为自动变量, 也就是说自动变量可省去说明符auto。 在前面各章的程序中所定义的变量凡未加存储类型说明符的都是自动变量。例如: { int i,j,k; char c; …… }等价于: { auto int i,j,k; auto char c; …… }  自动变量具有以下特点: 1. 自动变量的作用域仅限于定义该变量的个体内。在函数中定义的自动变量,只在该函数内有效。在复合语句中定义的自动变量只在该复合语句中有效。 例如:  int kv(int a) { auto int x,y; { auto char c;  } /*c的作用域*/ …… } /*a,x,y的作用域*/  2. 自动变量属于动态存储方式,只有在使用它,即定义该变量的函数被调用时才给它分配存储单元,开始它的生存期。函数调用结束,释放存储单元,结束生存期。因此函数调用结束之后,自动变量的值不能保留。在复合语句中定义的自动变量,在退出复合语句后也不能再使用,否则将引起错误。例如以下程序:  main() { auto int a,s,p; printf("\ninput a number:\n"); scanf("%d",a); if(a0){ s=a+a; p=a*a; } printf("s=%d p=%d\n",s,p); } { auto int a; printf("\ninput a number:\n"); scanf("%d",a); if(a0){ auto int s,p; s=a+a; p=a*a; } printf("s=%d p=%d\n",s,p); }  s,p是在复合语句内定义的自动变量,只能在该复合语句内有效。而程序的第9行却是退出复合语句之后用printf语句输出s,p的值,这显然会引起错误。 3. 由于自动变量的作用域和生存期都局限于定义它的个体内( 函数或复合语句内), 因此不同的个体中允许使用同名的变量而不会混淆。 即使在函数内定义的自动变量也可与该函数内部的复合语句中定义的自动变量同名。例5.14表明了这种情况。 main() { auto int a,s=100,p=100; printf("\ninput a number:\n"); scanf("%d",a); if(a0) { auto int s,p; s=a+a; p=a*a; printf("s=%d p=%d\n",s,p); } printf("s=%d p=%d\n",s,p); }  本程序在main函数中和复合语句内两次定义了变量s,p为自动变量。按照C语言的规定,在复合语句内,应由复合语句中定义的s,p起作用,故s的值应为a+ a,p的值为a*a。退出复合语句后的s,p 应为main所定义的s,p,其值在初始化时给定,均为100。从输出结果可以分析出两个s和两个p虽变量名相同, 但却是两个不同的变量。 4. 对构造类型的自动变量如数组等,不可作初始化赋值。 二、外部变量外部变量的类型说明符为extern 在前面介绍全局变量时已介绍过外部变量。这里再补充说明外部变量的几个特点: 1. 外部变量和全局变量是对同一类变量的两种不同角度的提法。全局变是是从它的作用域提出的,外部变量从它的存储方式提出的,表示了它的生存期。 2. 当一个源程序由若干个源文件组成时, 在一个源文件中定义的外部变量在其它的源文件中也有效。例如有一个源程序由源文件F1.C和F2.C组成: F1.C int a,b; /*外部变量定义*/ char c; /*外部变量定义*/ main() {  …… } F2.C extern int a,b; /*外部变量说明*/ extern char c; /*外部变量说明*/ func (int x,y) { …… }  在F1.C和F2.C两个文件中都要使用a,b,c三个变量。在F1.C文件中把a,b,c都定义为外部变量。在F2.C文件中用extern把三个变量说明为外部变量,表示这些变量已在其它文件中定义,并把这些变量的类型和变量名,编译系统不再为它们分配内存空间。 对构造类型的外部变量,如数组等可以在说明时作初始化赋值,若不赋初值,则系统自动定义它们的初值为0。
  • 热度 25
    2012-5-22 15:03
    944 次阅读|
    0 个评论
    变量的作用域 在讨论函数的形参变量时曾经提到, 形参变量只在被调用期间才分配内存单元,调用结束立即释放。 这一点表明形参变量只有在函数内才是有效的,离开该函数就不能再使用了。这种变量有效性的范围称变量的作用域。不仅对于形参变量, C语言中所有的量都有自己的作用域。变量说明的方式不同,其作用域也不同。 C语言中的变量,按作用域范围可分为两种, 即局部变量和全局变量。 一、局部变量 局部变量也称为内部变量。局部变量是在函数内作定义说明的。其作用域仅限于函数内, 离开该函数后再使用这种变量是非法的。 例如: int f1(int a) /*函数f1*/ { int b,c;  …… }a,b,c作用域 int f2(int x) /*函数f2*/ { int y,z;  }x,y,z作用域 main() { int m,n;  }  m,n作用域 在函数f1内定义了三个变量,a为形参,b,c为一般变量。在 f1的范围内a,b,c有效,或者说a,b,c变量的作用域限于f1内。同理,x,y,z的作用域限于f2内。 m,n的作用域限于main函数内。关于局部变量的作用域还要说明以下几点: 1. 主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用。同时,主函数中也不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平行关系。这一点是与其它语言不同的,应予以注意。 2. 形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。 3. 允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。如在例5.3 中,形参和实参的变量名都为n,是完全允许的。4. 在复合语句中也可定义变量,其作用域只在复合语句范围内。例如: main() { int s,a; …… { int b; s=a+b;  ……b作用域  } ……s,a作用域 } main() { int i=2,j=3,k; k=i+j; { int k=8; if(i==3) printf("%d\n",k); } printf("%d\n%d\n",i,k); } main() { int i=2,j=3,k; k=i+j; { int k=8; if(i=3) printf("%d\n",k); } printf("%d\n%d\n",i,k); }  本程序在main中定义了i,j,k三个变量,其中k未赋初值。而在复合语句内又定义了一个变量k,并赋初值为8。应该注意这两个k不是同一个变量。在复合语句外由main定义的k起作用,而在复合语句内则由在复合语句内定义的k起作用。因此程序第4行的k为main所定义,其值应为5。第7行输出k值,该行在复合语句内,由复合语句内定义的k起作用,其初值为8,故输出值为8,第9行输出i,k值。i是在整个程序中有效的,第7行对i赋值为3,故以输出也为3。而第9行已在复合语句之外,输出的k应为main所定义的k,此k值由第4 行已获得为5,故输出也为5。 二、全局变量 全局变量也称为外部变量,它是在函数外部定义的变量。它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。只有在函数内经过说明的全局变量才能使用。全局变量的说明符为extern。 但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。 例如: int a,b; /*外部变量*/ void f1() /*函数f1*/ { …… } float x,y; /*外部变量*/  int fz() /*函数fz*/ { …… } main() /*主函数*/ { …… }/*全局变量x,y作用域 全局变量a,b作用域*/  从上例可以看出a、b、x、y 都是在函数外部定义的外部变量,都是全局变量。但x,y 定义在函数f1之后,而在f1内又无对x,y的说明,所以它们在f1内无效。 a,b定义在源程序最前面,因此在f1,f2及main内不加说明也可使用。 输入正方体的长宽高l,w,h。求体积及三个面x*y,x*z,y*z的面积。 int s1,s2,s3; int vs( int a,int b,int c) { int v; v=a*b*c; s1=a*b; s2=b*c; s3=a*c; return v; } main() { int v,l,w,h; printf("\ninput length,width and height\n"); scanf("%d%d%d",l,w,h); v=vs(l,w,h); printf("v=%d s1=%d s2=%d s3=%d\n",v,s1,s2,s3); }  本程序中定义了三个外部变量s1,s2,s3, 用来存放三个面积,其作用域为整个程序。函数vs用来求正方体体积和三个面积,函数的返回值为体积v。由主函数完成长宽高的输入及结果输出。由于C语言规定函数返回值只有一个,当需要增加函数的返回数据时,用外部变量是一种很好的方式。本例中,如不使用外部变量,在主函数中就不可能取得v,s1,s2,s3四个值。而采用了外部变量, 在函数vs中求得的s1,s2,s3值在main 中仍然有效。因此外部变量是实现函数之间数据通讯的有效手段。对于全局变量还有以下几点说明: 1. 对于局部变量的定义和说明,可以不加区分。而对于外部变量则不然,外部变量的定义和外部变量的说明并不是一回事。外部变量定义必须在所有的函数之外,且只能定义一次。其一般形式为: 类型说明符 变量名,变量名… 其中方括号内的extern可以省去不写。 例如: int a,b; 等效于: extern int a,b; 而外部变量说明出现在要使用该外部变量的各个函数内, 在整个程序内,可能出现多次,外部变量说明的一般形式为: extern 类型说明符变量名,变量名,…; 外部变量在定义时就已分配了内存单元, 外部变量定义可作初始赋值,外部变量说明不能再赋初始值,只是表明在函数内要使用某外部变量。 2. 外部变量可加强函数模块之间的数据联系, 但是又使函数要依赖这些变量,因而使得函数的独立性降低。从模块化程序设计的观点来看这是不利的, 因此在不必要时尽量不要使用全局变量。 3. 在同一源文件中,允许全局变量和局部变量同名。在局部变量的作用域内,全局变量不起作用。 int vs(int l,int w) { extern int h; int v; v=l*w*h; return v; } main() { extern int w,h; int l=5; printf("v=%d",vs(l,w)); } int l=3,w=4,h=5;  本例程序中,外部变量在最后定义,因此在前面函数中对要用的外部变量必须进行说明。外部变量l,w和vs函数的形参l,w同名。外部变量都作了初始赋值,mian函数中也对l作了初始化赋值。执行程序时,在printf语句中调用vs函数,实参l的值应为main中定义的l值,等于5,外部变量l在main内不起作用;实参w的值为外部变量w的值为4,进入vs后这两个值传送给形参l,wvs函数中使用的h 为外部变量,其值为5,因此v的计算结果为100,返回主函数后输出。变量的存储类型各种变量的作用域不同,就其本质来说是因变量的存储类型相同。所谓存储类型是指变量占用内存空间的方式, 也称为存储方式。 变量的存储方式可分为“静态存储”和“动态存储”两种。  静态存储变量通常是在变量定义时就分定存储单元并一直保持不变,直至整个程序结束。5.5.1节中介绍的全局变量即属于此类存储方式。动态存储变量是在程序执行过程中,使用它时才分配存储单元, 使用完毕立即释放。典型的例子是函数的形式参数,在函数定义时并不给形参分配存储单元,只是在函数被调用时,才予以分配,调用函数完毕立即释放。如果一个函数被多次调用,则反复地分配、 释放形参变量的存储单元。从以上分析可知, 静态存储变量是一直存在的,而动态存储变量则时而存在时而消失。我们又把这种由于变量存储方式不同而产生的特性称变量的生存期。 生存期表示了变量存在的时间。生存期和作用域是从时间和空间这两个不同的角度来描述变量的特性,这两者既有联系,又有区别。 一个变量究竟属于哪一种存储方式,并不能仅从其作用域来判断,还应有明确的存储类型说明。 在C语言中,对变量的存储类型说明有以下四种: auto     自动变量 register   寄存器变量 extern    外部变量 static    静态变量  自动变量和寄存器变量属于动态存储方式, 外部变量和静态变量属于静态存储方式。在介绍了变量的存储类型之后,可以知道对一个变量的说明不仅应说明其数据类型,还应说明其存储类型。 因此变量说明的完整形式应为: 存储类型说明符 数据类型说明符变量名,变量名…; 例如: static int a,b;           说明a,b为静态类型变量 auto char c1,c2;          说明c1,c2为自动字符变量 static int a ={1,2,3,4,5};    说明a为静整型数组 extern int x,y;           说明x,y为外部整型变量 下面分别介绍以上四种存储类型: 一、自动变量的类型说明符为auto 这种存储类型是C语言程序中使用最广泛的一种类型。C语言规定, 函数内凡未加存储类型说明的变量均视为自动变量, 也就是说自动变量可省去说明符auto。 在前面各章的程序中所定义的变量凡未加存储类型说明符的都是自动变量。例如: { int i,j,k; char c; …… }等价于: { auto int i,j,k; auto char c; …… }  自动变量具有以下特点: 1. 自动变量的作用域仅限于定义该变量的个体内。在函数中定义的自动变量,只在该函数内有效。在复合语句中定义的自动变量只在该复合语句中有效。 例如:  int kv(int a) { auto int x,y; { auto char c;  } /*c的作用域*/ …… } /*a,x,y的作用域*/  2. 自动变量属于动态存储方式,只有在使用它,即定义该变量的函数被调用时才给它分配存储单元,开始它的生存期。函数调用结束,释放存储单元,结束生存期。因此函数调用结束之后,自动变量的值不能保留。在复合语句中定义的自动变量,在退出复合语句后也不能再使用,否则将引起错误。例如以下程序:  main() { auto int a,s,p; printf("\ninput a number:\n"); scanf("%d",a); if(a0){ s=a+a; p=a*a; } printf("s=%d p=%d\n",s,p); } { auto int a; printf("\ninput a number:\n"); scanf("%d",a); if(a0){ auto int s,p; s=a+a; p=a*a; } printf("s=%d p=%d\n",s,p); }  s,p是在复合语句内定义的自动变量,只能在该复合语句内有效。而程序的第9行却是退出复合语句之后用printf语句输出s,p的值,这显然会引起错误。 3. 由于自动变量的作用域和生存期都局限于定义它的个体内( 函数或复合语句内), 因此不同的个体中允许使用同名的变量而不会混淆。 即使在函数内定义的自动变量也可与该函数内部的复合语句中定义的自动变量同名。例5.14表明了这种情况。 main() { auto int a,s=100,p=100; printf("\ninput a number:\n"); scanf("%d",a); if(a0) { auto int s,p; s=a+a; p=a*a; printf("s=%d p=%d\n",s,p); } printf("s=%d p=%d\n",s,p); }  本程序在main函数中和复合语句内两次定义了变量s,p为自动变量。按照C语言的规定,在复合语句内,应由复合语句中定义的s,p起作用,故s的值应为a+ a,p的值为a*a。退出复合语句后的s,p 应为main所定义的s,p,其值在初始化时给定,均为100。从输出结果可以分析出两个s和两个p虽变量名相同, 但却是两个不同的变量。 4. 对构造类型的自动变量如数组等,不可作初始化赋值。 二、外部变量外部变量的类型说明符为extern 在前面介绍全局变量时已介绍过外部变量。这里再补充说明外部变量的几个特点: 1. 外部变量和全局变量是对同一类变量的两种不同角度的提法。全局变是是从它的作用域提出的,外部变量从它的存储方式提出的,表示了它的生存期。 2. 当一个源程序由若干个源文件组成时, 在一个源文件中定义的外部变量在其它的源文件中也有效。例如有一个源程序由源文件F1.C和F2.C组成: F1.C int a,b; /*外部变量定义*/ char c; /*外部变量定义*/ main() {  …… } F2.C extern int a,b; /*外部变量说明*/ extern char c; /*外部变量说明*/ func (int x,y) { …… }  在F1.C和F2.C两个文件中都要使用a,b,c三个变量。在F1.C文件中把a,b,c都定义为外部变量。在F2.C文件中用extern把三个变量说明为外部变量,表示这些变量已在其它文件中定义,并把这些变量的类型和变量名,编译系统不再为它们分配内存空间。 对构造类型的外部变量,如数组等可以在说明时作初始化赋值,若不赋初值,则系统自动定义它们的初值为0。
  • 热度 30
    2012-1-7 00:47
    3477 次阅读|
    11 个评论
    /********************************************************************************* * Filename: 一线研发之声:嵌入式C编程经验 之 全局变量猛于虎 * Author:SedateFire          E-mail:SedateFire@126.com * Version:1.001                 Time: 2012-01-05 * key: 嵌入式  os-less  全局变量  单片机 **********************************************************************************/       工作也有些年头了,从一位技术新人成长到现在自诩小牛级别的人物,少不了要自己寻找资料阅读。论坛上、书店里、杂志上......要嘛是些菜鸟浅薄的自炫处女贴,要嘛是高屋建瓴云里来雾里去的概念文,好不容易遇到个实践型高手写的文章,却在渐入佳境之际嘎然而止。本是隔靴搔痒,看完后心中更是郁结不已。也罢,今日且强装回大牛,献丑谈一谈嵌入式C编程中全局变量问题。           嵌入式特别是单片机os-less的程序,最易范的错误是全局变量满天飞。 这个现象在早期汇编转型过来的程序员以及初学者中常见,这帮家伙几乎把全局变量当作函数形参来用。在.h文档里面定义许多杂乱的结构体,extern一堆令人头皮发麻的全局变量,然后再这个模块里边赋值123,那个模块里边判断123分支决定做什么。每当看到这种程序, 我总要戚眉变脸而后拍桌怒喝 。没错,就是怒喝。我不否认全局变量的重要性,但我认为要十分谨慎地使用它,滥用全局变量会引申带来其它更为严重的结构性系统问题。   诸位看官,且听我细细道来。   1. 它会造成不必要的常量频繁使用,特别当这个常量没有用宏定义“正名”时,代码阅读起来将万分吃力。   2. 它会导致软件分层的不合理, 全局变量相当于一条快捷通道,它容易使程序员模糊了“设备层”和“应用层”之间的边界。 写出来的底层程序容易自作多情地关注起上层的应用。这在软件系统的 构建初期的确效率很高 ,功能调试进度一日千里,但到了 后期往往bug一堆 ,处处“补丁”,雷区遍布。说是度日如年举步维艰也不为过。   3. 由于 软件的分层不合理 ,到了 后期维护 ,哪怕仅是增加修改删除小功能, 往往要从上到下掘地三尺地修改 ,涉及大多数模块,而 原有的代码注释却忘了更新修改 ,这个时候,交给后来维护者的系统会越来越像一个“泥潭”,注释的唯一作用只是使泥潭上方再加一些迷烟瘴气。   4. 全局变量大量使用,少不了 有些变量流连忘返于中断与主回圈程序之间 。这个时候如果处理不当,系统的bug就是随机出现的,无规律的,这时候初步显示出病入膏肓的特征来了,没有大牛来力挽狂澜,注定慢性死亡。           无需多言,您已经成功得到一个畸形的系统, 它处于一个神秘的稳定状态! 你看着这台机器,机器也看着你,相对无言,心中发毛。你不确定它什么时候会崩溃,也不晓得下一次投诉什么时候道理。   然后,我告诉大家现实层面的后果是什么。   1.“老人”气昂昂, 因为系统离不开他,所有“雷区”只有他了然于心。当出现紧急的bug时,只有他能够搞定。 你不但不能辞退他,还要给他加薪。 2. 新人见光死, 但凡招聘来维护这个系统的,除了改出更多的bug外,基本上一个月内就走人,到了外面还宣扬这个公司的软件质量有够差够烂。 3.随着产品的后续升级,几个月没有接触这个系统的原创者会发现,很多雷区他本人也忘记了,于是每次的产品 升级维护周期越来越长 ,因为修改一个功能会冒出很多bug,而 按下一个bug,会弹出其他更多的bug 。在这期间,又会产生更多的全局变量。终于有一天他告诉老板,不行啦不行啦,资源不够了,ram或者flash空间太小了,升级升级。 4. 客户投诉不断,售后也快崩溃了,业务员也不敢推荐此产品了 ,市场份额越来越小,公司形象越来越糟糕。          要问我的对策吗,只有两个原则: 1. 能不用全局变量尽量不用 ,我想除了系统状态和控制参数、通信处理和一些需要效率的模块,其他的基本可以靠合理的软件分层和编程技巧来解决。 2. 如果不可避免需要用到,那能藏多深就藏多深。 1)如果只有某.c文件用,就static到该文件中,顺便把结构体定义也收进来; 2)如果只有一个函数用,那就static到函数里面去; 3)如果非要开放出去让人读取,那就用函数return出去,这样就是只读属性了; 4)如果非要遭人蹂躏赋值,好吧,我开放函数接口让你传参赋值;5)实在非要extern强奸我,我还可以严格控制包含我.h档的对象,而不是放到公共的includes.h中被人围观,丢人现眼。           如此,你可明白我对全局变量的感悟有多深刻。悲催的我,已经把当年那些“老人”交给我维护的那些案子加班 全部重新翻写 了。你能明白吗,不要让人背后唾弃你哦。   2011-12-29 续篇          承蒙小编抬爱,推荐了本博文,感激之余心中惶恐,特地详细看了下回复。这个主题最早是在论坛发表的,我发现那里的回复还是比较热烈的,也很高兴能够听到不同的声音。对于一些网友 提到的,如果大量使用局部变量也会容易造成栈溢出的问题,还提到程序模型的概念。言之有理。所以特地来补充一下意见: 1.全局变量是不可避免要用到的 ,每一个设备底层几乎都需要它来记录当前状态,控制时序,起承转合。但是尽量不要用来传递参数,这个很忌讳的。 2.尽量把变量的作用范围控制在使用它的模块里面 ,如果其他模块要访问,就开个读或写函数接口出来,严格控制访问范围。这一点,C++的private属性就是这么干的。这对将来程序的调试也很有好处。C语言之所以有++版本,很大原因就是为了控制它的灵活性,要说面向对象的思想,C语言早已有之,亦可实现。 3.当一个模块里面的全局变量超过3个(含)时,就用结构体包起来吧。 要归0便一起归0,省得丢三落四的。 4.在函数里面开个静态的 全局变量 , 全局 数 组,是不占用栈空间的。 只是有些编译器对于大块的全局数组,会放到和一般变量不同的地址区。若是在keil C51,因为是静态编译,栈爆掉了会报警,所以大可以尽情驰骋,注意交通规则就是了。 5. 单片机的os-less系统中,只有栈没有堆的用法,那些默认对堆分配空间的“startup.s”,可以大胆的把堆空间干掉。 6.程序模型?如何分析抽象出来呢,从哪个角度进行模型构建呢?很愿意聆听网友的意见。本人一直以来都是从两个角度分析系统 ,事件--状态机迁移图 和 数据流图,前者分析控制流向,完善UI,后者可知晓系统数据的缘起缘灭。 这些理论,院校的《软件工程》教材都有,大家不妨借鉴下。只不过那些理论,终究是起源于大型系统软件管理的,牛刀杀鸡,还是要裁剪一下的。
相关资源
  • 所需E币: 0
    时间: 2020-12-18 23:46
    大小: 15.49KB
    上传者: samewell
    全局变量、局部变量、静态变量三者的区别
  • 所需E币: 0
    时间: 2020-12-6 17:53
    大小: 15.49KB
    上传者: kaidi2003
    全局变量、局部变量、静态变量三者的区别
  • 所需E币: 4
    时间: 2020-11-17 20:01
    大小: 1.09MB
    上传者: xgp416
    [摘要]本文档的主要内容详细介绍的是LABVIEW初级教程之局部变量与全局变量的详细资料说明   LabVIEW是以数据流决定程序框图元素的执行顺序,但在某些程序框图中需要消除数据流的依赖性,这时可以考虑使用变量。LabVIEW中的变量是程序框图中的元
  • 所需E币: 4
    时间: 2020-11-16 17:18
    大小: 71.5KB
    上传者: stanleylo2001
    全局变量、局部变量、静态变量三者的区别
  • 所需E币: 0
    时间: 2020-9-18 20:34
    大小: 174.41KB
    上传者: LGWU1995
    我和LabVIEW:一个NI工程师的编程经验
  • 所需E币: 0
    时间: 2020-9-10 03:29
    大小: 231.5KB
    上传者: Goodluck2020
    29.全局变量.doc
  • 所需E币: 5
    时间: 2019-12-25 12:16
    大小: 192KB
    上传者: 微风DS
    μCOSⅡ范例第一章:范例在这一章里将提供三个范例来说明如何使用µC/OS-II。笔者之所以在本书一开始就写这一章是为了让读者尽快开始使用µC/OS-II。在开始讲述这些例子之前,笔者想先说明一些在这本书里的约定。这些例子曾经用BorlandC/C++编译器(V3.1)编译过,用选择项产生Intel/AMD80186处理器(大模式下编译)的代码。这些代码实际上是在IntelPentiumIIPC(300MHz)上运行和测试过,IntelPentiumIIPC可以看成是特别快的80186。笔者选择PC做为目标系统是由于以下几个原因:首先也是最为重要的,以PC做为目标系统比起以其他嵌入式环境,如评估板,仿真器等,更容易进行代码的测试,不用不断地烧写EPROM,不断地向EPROM仿真器中下载程序等等。用户只需要简单地编译、链接和执行。其次,使用BorlandC/C++产生的80186的目标代码(实模式,在大模式下编译)与所有Intel、AMD、Cyrix公司的80x86CPU兼容。1.00安装µC/OS-II本书附带一张软盘包括了所有我们讨论的源代码。是假定读者在80x86,Pentium,或者Pentium-II处理器上运行DOS或Windows95。至少需要5Mb硬盘空间来安装uC/OS-II。请按照以下步骤安装:1.进入到DOS(或在Windows95下打开DOS窗口……
  • 所需E币: 3
    时间: 2019-12-25 12:05
    大小: 1.89MB
    上传者: 16245458_qq.com
    较深入的问题和技巧第八章较深入的问题和技巧本章介绍局部变量、全局变量、属性节点和其他一些有助于提高编程技巧的问题,恰当地运用这些技巧可以提高程序的质量。8.1局部变量严格的语法尽管可以保证程序语言的严密性,但有时它也会带来一些使用上的不便。在LabVIEW这样的数据流式的语言中,将变量严格地分为控制器(Control)和指示器(Indicator),前者只能向外流出数据,后者只能接受流入的数据,反过来不行。在一般的代码式语言中,情况不是这样的。例如我们有变量a、b和c,只要需要我们可以将a的值赋给b,将b的值赋给c等等。前面所介绍的LabVIEW内容中,只有移位积存器即可输入又可输出。另外,一个变量在程序中可能要在多处用到,在图形语言中势必带来过多连线,这也是一件烦人的事。还有其他需要,因此LabVIEW引入了局部变量。8.1.1局部变量的创建我们在框图上设置三个变量,两个控制器分别为Numeric和Numeric2,现在增加局部变量。选择Function→Structures→LocalVariable然后将其拖到框图上,就可得到一个代“?”的图标,下一步将其与框图中已有的变量建立关联,有鼠标右键单击图标,进入SelectItem选择“input1”,最后框图就变成了图7-1右边的样子。图7-1局部变量的创建局部变量只是原变量的一个数据拷贝,但是它的属性可以修改,并且这种改变不会影响原变量。例如上图中的这个局部变量可以利用快捷菜单中的ChangeToR……
  • 所需E币: 3
    时间: 2019-12-25 12:05
    大小: 930.5KB
    上传者: quw431979_163.com
    LabVIEW的编程技巧第八章LabVIEW的编程技巧本章介绍局部变量、全局变量、属性节点和其他一些有助于提高编程技巧的问题,恰当地运用这些技巧可以提高程序的质量。8.1局部变量严格的语法尽管可以保证程序语言的严密性,但有时它也会带来一些使用上的不便。在LabVIEW这样的数据流式的语言中,将变量严格地分为控制器(Control)和指示器(Indicator),前者只能向外流出数据,后者只能接受流入的数据,反过来不行。在一般的代码式语言中,情况不是这样的。例如我们有变量a、b和c,只要需要我们可以将a的值赋给b,将b的值赋给c等等。前面所介绍的LabVIEW内容中,只有移位积存器即可输入又可输出。另外,一个变量在程序中可能要在多处用到,在图形语言中势必带来过多连线,这也是一件烦人的事。还有其他需要,因此LabVIEW引入了局部变量。8.1.1局部变量的创建我们在框图上设置三个变量,两个控制器分别为Numeric和Numeric2,现在增加局部变量。选择Function→Structures→LocalVariable然后将其拖到框图上,就可得到一个代“?”的图标,下一步将其与框图中已有的变量建立关联,有鼠标右键单击图标,进入SelectItem选择“input1”,最后框图就变成了图7-1右边的样子。图7-1局部变量的创建局部变量只是原变量的一个数据拷贝,但是它的属性可以修改,并且这种改变不会影响原变量。例如上图中的这个局部变量可以利用快捷菜单中的ChangeTo……