一.变量

      1. 从变量的作用域(即从空间)角度来分,可以分为全局变量局部变量

2.从另一个角度,从变量值存在的作时间(即生存期)角度来分,可以分为静态存储方式动态存储方式

静态存储方式:是指在程序运行期间分配固定的存储空间的方式。

动态存储方式:是在程序运行期间根据需要进行动态的分配存储空间的方式。

3.全局变量全部存放在静态存储区,在程序开始执行时给全局变量分配存储区,程序行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不动态地进行分配和释放.

 

动态存储区存放以下数据:

1)        函数形式参数;

2)        自动变量(未加static声明的局部变量);

3)        函数调用实的现场保护和返回地址;

对以上这些数据,在函数开始调用时分配动态存储空间,函数结束时释放这些空间。

 

 
  1. f(int a) 
  2. {auto b=0;//可以不加auto 
  3.  static c=3
  4.  bb=b+1; 
  5.  cc=c+1; 
  6.  printf("c=%d",c); 
  7.  return(a+b+c); 
  8. main() 
  9. {int a=2,i; 
  10.  for(i=0;i<3;i++) 
  11.  printf("%d\\",f(a)); 
  12. 输出c=4\7 
  13. c=5\8 
  14. c=6\9 

 

 

4.对静态局部变量的说明:

1)   静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储空间,函数调用结束后即释放。

2)   静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。

3)   如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。

5.为了提高效率,C语言允许将局部变量得值放在CPU中的寄存器中,这种变量叫寄存器变量,用关键字register作声明

 

 
  1. int fac(int n) 
  2. { register int i,f=1
  3.  for(i=1;i<=n;i++) 
  4.     ff=f*i 
  5.  return(f); 
  6. main() 
  7. { int i; 
  8.  for(i=0;i<=5;i++) 
  9.  printf("%d!=%d\n",i,fac(i)); 

 

说明:

1)        只有局部自动变量和形式参数可以作为寄存器变量

2)        一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;

3)        局部静态变量不能定义为寄存器变量。

6 外部变量(即全局变量)是在函数的外部定义的,它的作用域为从变量定义处开始,到本程序文件的末尾。如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件终了。如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字extern对该变量作“外部变量声明”。表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。

 

 
  1. int max(int x,int y) 
  2. { int z; 
  3.   z=x>y?x:y; 
  4.    return(z); 
  5. main() 
  6. {  extern A,B; //使用extern
  7.    printf("%d\n",max(A,B)); 
  8.  int A=13,B=-8; //全局变量

二.预处理命令

 

#ifdef __cplusplus //c++编译环境中才会定义__cplusplus (plus就是"+"的意思)
extern 
"C" 
//告诉编译器下面的函数是c语言函数(因为c++和c语言对函数的编译转换不一样,
主要是c++中存在重载)
#endif
 
1.不带参数宏  #define 标识符  字符串

其中的“#表示这是一条预处理命令。凡是以#开头的均为预处理命令。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;

从形式上看这两者相似, 但在实际使用中却不相同。

下面用PIN1PIN2说明变量时就可以看出它们的区别:

PIN1 a,b;在宏代换后变成:

    int *a,b;

表示a是指向整型的指针变量,而b是整型变量。

然而:

    PIN2 a,b;等价于 int* a; int* b; 

 

表示a,b都是指向整型的指针变量。因为PIN2是一个类型说明符。由这个例子可见,宏定义虽然也可表示数据类型, 但毕竟是作字符代换.

 

8)   对“输出格式”作宏定义,可以减少书写麻烦。

 

 
  1. #define P printf 
  2. #define D "%d\n" 
  3. #define F "%f\n" 
  4. main(){ 
  5.   int a=5c=8e=11
  6.   float b=3.8, d=9.7, f=21.08; 
  7.   P(D F,a,b); 
  8.   P(D F,c,d); 
  9.   P(D F,e,f); 

 

 

 2.带参数宏  #define  宏名(形参表字符串 调用:宏名(实参表); 

 

 
  1.   #define M(y) y*y+3*y      /*宏定义*/ 
  2.     k=M(5);                   /*宏调用*/ 
  3. 在宏调用时,用实参5去代替形参y,经预处理宏展开后的语句为: 
  4.      k=5*5+3*5 
  5.  
  6.  

注意:

1)带参宏定义中,宏名和形参表之间不能有空格出现;

2)在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义(带参宏中,只是符号代换,不存在值传递)

3)在宏定义中的形参是标识符,而宏调用中的实参可以是表达式

 
  1. #define SQ(y) (y)*(y) 
  2. main(){ 
  3.   int a,sq; 
  4.   printf("input a number:    "); 
  5.   scanf("%d",&a); 
  6.   sq=SQ(a+1); //实参是一个表达式
  7.   printf("sq=%d\n",sq); 
  8. 输入3,得到结果16 (a+1)*(a+1) 4*4=16

4)在宏定义中,字符串内的形参通常要用括号括起来以避免出错。在上例中的宏定义中(y)*(y)表达式的y都用括号括起来,因此结果是正确的。

 

 
  1. #define SQ(y) y*y  
  2. main(){  
  3.   int a,sq;  
  4.   printf("input a number:    ");  
  5.   scanf("%d",&a);  
  6.   sq=SQ(a+1);  
  7.   printf("sq=%d\n",sq);  
  8. } //输入3,得到结果7 a+1*a+1 即3+1×3+1=7