一.变量
1. 从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。
2.从另一个角度,从变量值存在的作时间(即生存期)角度来分,可以分为静态存储方式和动态存储方式。
静态存储方式:是指在程序运行期间分配固定的存储空间的方式。
动态存储方式:是在程序运行期间根据需要进行动态的分配存储空间的方式。
3.全局变量全部存放在静态存储区,在程序开始执行时给全局变量分配存储区,程序行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不动态地进行分配和释放.
动态存储区存放以下数据:
1) 函数形式参数;
2) 自动变量(未加static声明的局部变量);
3) 函数调用实的现场保护和返回地址;
对以上这些数据,在函数开始调用时分配动态存储空间,函数结束时释放这些空间。
- f(int a)
- {auto b=0;//可以不加auto
- static c=3;
- bb=b+1;
- cc=c+1;
- printf("c=%d",c);
- return(a+b+c);
- }
- main()
- {int a=2,i;
- for(i=0;i<3;i++)
- printf("%d\\",f(a));
- }
- 输出c=4\7
- c=5\8
- c=6\9
4.对静态局部变量的说明:
1) 静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储空间,函数调用结束后即释放。
2) 静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
3) 如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。
5.为了提高效率,C语言允许将局部变量得值放在CPU中的寄存器中,这种变量叫“寄存器变量”,用关键字register作声明。
- int fac(int n)
- { register int i,f=1;
- for(i=1;i<=n;i++)
- ff=f*i
- return(f);
- }
- main()
- { int i;
- for(i=0;i<=5;i++)
- printf("%d!=%d\n",i,fac(i));
- }
说明:
1) 只有局部自动变量和形式参数可以作为寄存器变量;
2) 一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;
3) 局部静态变量不能定义为寄存器变量。
6 外部变量(即全局变量)是在函数的外部定义的,它的作用域为从变量定义处开始,到本程序文件的末尾。如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件终了。如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字extern对该变量作“外部变量声明”。表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。
- int max(int x,int y)
- { int z;
- z=x>y?x:y;
- return(z);
- }
- main()
- { extern A,B; //使用extern
- printf("%d\n",max(A,B));
- }
- int A=13,B=-8; //全局变量
二.预处理命令
#ifdef __cplusplus //c++编译环境中才会定义__cplusplus (plus就是"+"的意思)
extern
"C"
{
//告诉编译器下面的函数是c语言函数(因为c++和c语言对函数的编译转换不一样,
主要是c++中存在重载)
#endif
其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。
对于宏定义还要说明以下几点:
1) 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。
2) 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换。
3) 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用# undef命令,终止之后该命令将不再有效。
4) 宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换(被引号括起来相当于是字符串了)
5)宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换
6) 习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母
7) 可用宏定义表示数据类型,使书写方便。
例如:
#define STU struct stu
在程序中可用STU作变量说明:
STU body[5],*p;
#define INTEGER int
在程序中即可用INTEGER作整型变量说明:
INTEGER a,b;
应注意用宏定义表示数据类型和用typedef定义数据说明符的区别(宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。)。
#define PIN1 int *
typedef (int *) PIN2;
从形式上看这两者相似, 但在实际使用中却不相同。
下面用PIN1,PIN2说明变量时就可以看出它们的区别:
PIN1 a,b;在宏代换后变成:
int *a,b;
表示a是指向整型的指针变量,而b是整型变量。
然而:
PIN2 a,b;等价于 int* a; int* b;
表示a,b都是指向整型的指针变量。因为PIN2是一个类型说明符。由这个例子可见,宏定义虽然也可表示数据类型, 但毕竟是作字符代换.
8) 对“输出格式”作宏定义,可以减少书写麻烦。
- #define P printf
- #define D "%d\n"
- #define F "%f\n"
- main(){
- int a=5, c=8, e=11;
- float b=3.8, d=9.7, f=21.08;
- P(D F,a,b);
- P(D F,c,d);
- P(D F,e,f);
- }
2.带参数宏 #define 宏名(形参表) 字符串 调用:宏名(实参表);
- #define M(y) y*y+3*y /*宏定义*/
- k=M(5); /*宏调用*/
- 在宏调用时,用实参5去代替形参y,经预处理宏展开后的语句为:
- k=5*5+3*5
注意:
1)带参宏定义中,宏名和形参表之间不能有空格出现;
2)在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义(带参宏中,只是符号代换,不存在值传递)
3)在宏定义中的形参是标识符,而宏调用中的实参可以是表达式
- #define SQ(y) (y)*(y)
- main(){
- int a,sq;
- printf("input a number: ");
- scanf("%d",&a);
- sq=SQ(a+1); //实参是一个表达式
- printf("sq=%d\n",sq);
- 输入3,得到结果16 (a+1)*(a+1) 4*4=16
- }
4)在宏定义中,字符串内的形参通常要用括号括起来以避免出错。在上例中的宏定义中(y)*(y)表达式的y都用括号括起来,因此结果是正确的。
- #define SQ(y) y*y
- main(){
- int a,sq;
- printf("input a number: ");
- scanf("%d",&a);
- sq=SQ(a+1);
- printf("sq=%d\n",sq);
- } //输入3,得到结果7 a+1*a+1 即3+1×3+1=7