08-变量类别与编译预处理-c语言初学系列

变量类别与编译预处理

8.1 全局变量和局部变量

8.2 自动变量、静态变量和寄存器变量

8.3 宏定义

8.4 文件包含

示例

#include <stdio.h>
void add(int x,int y)
{int z;
  z=x+y;
  printf(“%d+%d=%d”,a,b,z);
}
main()
{int a,b;
  scanf(“%d%d”,&a,&b);
  add(a,b);
}

8.1 全局变量和局部变量

变量定义的位置可以是在函数内部,即局部变量,也可以在函数外部,即全局变量。

不同的定义位置决定了变量的作用范围(称为作用域)。

C语言根据变量作用域的不同,将变量分为局部变量和全局变量。

局部变量

在函数内部定义的变量称为局部变量,它只在定义它的函数内部有效,即局部变量只有在定义它的函数内部使用,其它函数不能使用它。

#include <stdio.h>
void f2(int x,int y)
{x++; y++; }
void f1(int x,int y)
{int n=0,m=1;
 f2(x, y);
printf(“n=%d,x=%d,y=%d”,n,x,y); }
void main()    
{ int n=2,a=3,b=4;
f1(a,b);
printf(“n=%d,a=%d,b=%d”,n,a,b); }

复合语句中定义局部变量

全局变量

在所有函数外部定义的变量称为全局变量。

全局变量的作用范围是从定义变量的位置开始到源程序结束,即全局变量可以被在其定义位置之后的其它函数所共享。

全局变量主要用于函数之间数据的传递

函数可以将结果保存在全局变量中,这样函数得到多个执行结果,而不局限于一个返回值

由于函数可以直接使用全局变量的数据,从而减少了函数调用时的参数。

#include <stdio.h>
int Num,Max,Min;
int average(int array[])
  {int i, array_aver=array[0];
    Max=Min=array[0];
    for(i=1;i<Num;i++)
     { if(array[i]>Max) 
              Max=array[i];
   if(array[i]<Min) 
          Min=array[i];
   array_aver+=array[i];}
 array_aver/=Num;
 return array_aver;   }

void main()    
{ int aver;
   int  n[5]={23,2,4,5,6};
   Num=5;
   aver=average(n);
printf(“max=%d,min=%d,average=%d\n”,Max,Min,average); }
运行结果 : max=23,min=2,average=8

全局变量使用时要注意以下几个问题:

全局变量可以在多个函数中使用,当其中一个函数改变了全局变量的值,可能会影响其他函数的执行结果。

在一个函数内定义了一个与全局变量名相同的局部变量(或者是形参)时,局部变量有效,而全局变量在该函数内不起作用。

8.2 自动变量、静态变量和寄存器变量

C语言的变量定义时,根据变量分配的存储空间的的不同,可以分为自动变量、静态变量和寄存器变量

自动变量

自动变量的存储空间为内存中的动态数据区,该区域中的数据随程序需要动态地生成或释放。

在函数体内或者复合语句内定义的局部变量都属于自动变量。

自动变量的类型修饰符auto放在变量的类型说明之前, auto int a;

但是auto一般是不写的。局部变量只要不专门说明是static存储类型,都确定为自动变量。

自动变量的特点是当程序执行到自动变量的作用域时,才为自动变量分配存储空间,并且定义自动变量的函数执行结束后,程序将释放该自动变量的存储空间,留给其它自动变量使用。

自动变量的生存期是从定义自动变量的函数或复合语句开始,到程序退出该函数或复合语句时结束。

#include <stdio.h>
void f (int n)
{ auto int s=0;      /*auto可以省略*/
   s=s+1;
   printf(“No.%d:s=%d\n”,n,s);}
void main()    
{ int num;
   for(num=1;num<4;num++)
   f (num);
 }

自动变量的初值在每次分配存储空间后都要重新赋值。

运行结果 :No.1:s=1 No.2:s=1
 No.3:s=1

静态变量

静态变量的存储空间为内存中的静态数据区,该区域中的数据在整个程序的运行期间一直占用这些存储空间,直到整个程序运行结束。

所有的全局变量都是静态变量,而局部变量只有定义时加上类型修饰符static,才为局部静态变量。

例如: static float x;

静态变量的特点是在程序的整个执行过程中始终存在,但是在它作用域之外不能使用。

静态变量的生存期就是整个程序的运行期。

静态变量的使用要特别注意:

函数体内如果在定义静态变量的同时进行了初始化,则以后程序不再进行初始化操作。

#include <stdio.h>
void f (int n)
{ static int s=0;      /*auto可以省略*/
   s=s+1;
   printf(“No.%d:s=%d\n”,n,s);}
void main()    
{ int num;
   for(num=1;num<4;num++)
   f (num);
 }
运行结果 :No.1:s=1 No.2:s=2
 No.3:s=3

在选择使用静态局部变量还是自动变量,可以从以下两点考虑:

如果需要在两次函数调用之间保持上一次函数调用结果,可以使用局部静态变量;

如果在每次调用函数时都必须对局部变量初始化,选择自动变量。

实际上局部静态变量占用内存时间较长,并且可读性差,因此,除非必要,尽量避免使用局部静态变量。

注意:即使局部静态变量定义时不赋初值,系统会自动赋初值0(对数值型变量)或者空字符(对字符型变量)。

寄存器变量

自动变量和静态变量都存放在计算机的内存中。由于计算机从寄存器中读取数据比从内存中读取速度要快,所以为了提高运算速度,C语言允许将一些频繁使用的局部变量定义为寄存器变量,这样程序尽可能地为它分配寄存器存放,而不用内存。

寄存器变量只要在定义时加上类型修饰符register,就是寄存器变量。例如:

register int n;

寄存器变量使用要注意:

只有自动变量(或者形式参数)可以是寄存器变量,全局变量和静态局部变量不行。

函数中的寄存器变量在调用该函数时占用寄存器存放变量的值,当函数结束时释放寄存器,该变量消失。

由于计算机中寄存器数目有限,不能使用太多的寄存器变量。

寄存器变量只限于int型、char型和指针类型变量使用。如果寄存器使用饱和时,程序将寄存器变量自动转换为自动变量处理。

用extern、static声明的全局变量

一个程序可以放在一个源程序文件中,也可以放在几个不同的源程序文件中。如果组成这一个程序的几个文件需要用到同一个全局变量,只要在其它引用该全局变量的源程序文件中说明该全局变量为extern即可

extern int num;

说明全局变量num在其它源程序文件中已经说明。

如果一个源程序文件中的全局变量仅限于该文件使用,只要在该全局变量定义时的类型说明前加一个static。

    static int m;

则说明全局变量m只能在该源程序文件内有效,其它文件不能引用。

8.3 宏定义

C语言在对源程序进行正常编译之前,先对一些特殊的预处理命令作解释,产生一个新的源程序(称作编译预处理),之后再进行通常的编译。

编译预处理主要为程序调试、移植等提供便利,是一个非常实用的功能。

在源程序中,为了区分预处理命令和一般的C语句的不同,所有预处理命令行都以符号“#”开头,并且结尾不用分号。

预处理命令可以出现在程序任何位置,但习惯上尽可能地写在源程序的开头,其作用范围从其出现的位置到文件尾。

C语言提供的预处理命令主要有:宏定义、文件包含和条件编译。其中宏定义分为带参数的宏定义和不带参数的宏定义。

不带参数的宏定义

一般形式为:#define 标识符 语句序列或表达式

它的作用是在编译预处理时,将源程序中所有标识符替换成语句序列。例如:

#define  PR  printf
#define  N    2

则源程序中所有“PR”标识符在编译预处理时将被“printf”代替、所有“N”标识符在编译预处理时将被“2”代替。宏定义常用来定义符号常量。

#include <stdio.h>
#define  IN   scanf
#define  OUT  printf
#define  N  2
void main()    
{ int i;
  char name[N][20]; 
  long num[N];
  OUT(“Please input name and ID:\n”);
  for(i=0;i<N;i++)
     { IN(“%s”,name[i]);
        IN(“%ld”,&num[i]);}
  for(i=0;i<N;i++)
     { OUT(“%s\t”,name[i]);
        OUT(“%ld\n”,num[i]);}
  }
运行结果 :
Please input name and ID:
Wang Hong
20010423
Lee Xiaojing
20010425
Wang Hong   20010423
Lee Xiaojing  20010425

例题8.5中的scanf、printf被便于书写和理解的宏名IN、OUT代替,说明使用宏定义可以简化一些标识符书写。而利用宏定义定义的符号常量N,替换了数组中元素个数2,不仅部分地弥补C语言没有元素个数可变的数组的不足,而且当需要修改元素个数时,只要直接修改宏定义即可,无需修改程序中所有出现元素个数的地方。所以宏定义,不仅提高了程序的可读性、便于调试,而且也方便了程序的移植。

无参数的宏定义使用时,要注意以下几个问题:

宏名一般用大写字母,以便与变量名的区别。当然,用小写字母也不为错。

在编译预处理时宏名与字符串进行替换时,不作语法检查,只是简单的字符替换,只有在编译时才对已经展开宏名的源程序进行语法检查

宏名的有效范围是从定义位置到文件结束。如果需要终止宏定义的作用域,可以用#undef命令。例如:

  #undef  OUT

则该语句之后的OUT不再代表printf。这样可以灵活控制宏定义的范围。

无参数的宏定义使用时,要注意以下几个问题:

宏定义时可以引用已经定义的宏名。例如:

#define  R   2.0
#define  PI   3.14158
#define  L   2*PI*R
#define  S   PI*R*R

在编译预处理时,L 被2*3.14158*2.0替换,S 被3.14158*2.0*2.0替换。

对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作。

带参数的宏定义

为了进一步扩大宏的应用范围,在定义宏时,还可以带参数。

一般形式为:#define 标识符(参数表) 字符串

它的作用是在编译预处理时,将源程序中所有标识符替换成字符串,并且将字符串中的参数用实际使用的参数替换。

例如:    #define  S(a,b,c)  (a+b+c)/2

则源程序中如果使用了S(3,4,5),在编译预处理时将替换为 (3+4+5)/2。

带参数的宏定义

#include <stdio.h>
#define  PI  3.14158
#define  L(r)  2*PI*(r)
#define  S(r)   PI*(r)*(r)
void main()    
{ float  radius,circle,area;
scanf(“%f”,&radius);
circle=L(radius); 
area=S(radius);
printf(“radius:%f\tcircle:%f\tarea:%f\n”,radius,circle,area);}
}

带参数的宏定义使用时,要注意以下几个问题:

在宏定义时,宏名和参数之间不能有空格,否则空格后面的所有字符序列都作为替换的字符串。例如:   #define  L  (r)  2*PI*(r)

带参数的宏展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以一般在定义宏时,字符串中的形式参数外面加一个小括号,例如“2*PI*(r)”。如果省略,则在某些场合会得出错误的结论。例如上例的宏定义如果改为

#define  L(r)  2*PI*r

如果源程序有L(2+3),则编译预处理后变为

circle=2*3.14158*2+3;

这样circle的值不是L(5)的值了。

带参数的宏定义使用时,要注意以下几个问题:

带参数的宏名,虽然在源程序中出现的形式与函数很象,但是二者却有本质的区别。带参数的宏不涉及函数调用的流程控制转移、存储空间的分配、参数类型匹配、参数传递及返回值问题,并且函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以使用带参数的宏比用函数有更高的执行效率。

8.4 文件包含

“文件包含”实际上就是我们前面已经反复用到的#include命令实现的功能,即一个源程序文件可以包含另外一个源程序文件的全部内容。

“文件包含”不仅可以包含头文件,例如:#include <stdio.h>,还可以包含用户自己编写的源程序文件。

提供的文件包含预处理命令的一般形式有两种:

#include  <文件名>
#include  “文件名”

第一种形式系统将到包含C库函数的头文件所在的目录(通常是Turbo C中的include 子目录)中寻找文件。

第二种形式的文件名用双引号括起来,系统先在当前目录下寻找,若找不到,再到操作系统的path命令设置的自动搜索路径中查找,最后才到C语言头文件所在目录中查找。

所以为了节省查找时间,包含C语言的头文件时选择第一种形式,其它情况选择第二种情况

源程序文件l8_7_1.C内容如下:

int string_len(char str[])
{ int i;
for (i=0;str[i]!=’\0’;i++);
return i;
}

源程序文件l8_7.C内容如下:

#include  <stdio.h>
#include  “l8_7_1.c”
void main()    
{ char string[20];
   scanf(“%s”,string);
printf(“%d.\n”,string_len(string));}

“文件包含”的使用时要注意:

一个#include命令只能指定一个被包含的文件。

“文件包含”可以嵌套。在文件包含的嵌套时,如果文件1包含了文件2,而文件2包含了文件3,则在文件1也要包含文件3,并且文件3的包含要写在文件2的包含之前,即文件1中的“文件包含”说明如下:

#include  “文件3”
#include  “文件2”

“文件包含”命令为多个源程序文件的组装提供了一种方法。在编写程序时,习惯上将公共的符号常量定义、数据类型定义和extern类型的全局变量说明构成一个源文件,并以“.H”为文件名的后缀。如果其它文件用到这些说明时,只要包含该文件即可,无需再重新说明,减少了工作量。而且这样编程使得各源程序文件中数据结构、符号常量以及全局变量形式统一,便于程序的修改和调试。

条件编译

根据需要设置条件编译,将字母全改写成大写,或小写

#define LETTER 1
main()
{ char str[20]=“C Language”,c;
   int i;
   i=0;
   while((c=str[i])!=‘\0’)
      {I++;
        #if LETTER
           if(c>=‘a’&&c<=‘z’)
              c=c-32;
        #else
           if (c>=‘A’ && c<=‘Z’)
              c=c+32;
        #endif
        printf(“%c”,c);
}
}

/*------------------------------------------------
  程序L8_8.C : 使用静态变量输出1到10 的阶乘。
 -------------------------------------------------*/
 #include <stdio.h>
 long f(n)
 {static long s=1;
  s=s*n;
  return s;}
void main( )
 { int i;
   for (i=1;i<=10;i++)
      printf("%ld\t",f(i));
  }

练习

1 应用局部静态变量实现输入n,输出1+2+… …+n的结果。

2 定义带参数的宏MAX(a,b)、MIN(a,b)、ABS(a)分别求两个数的最大值、最小值和一个数的绝对值。

3 定义宏名LEAP_YEAR(year)和Isupper(a),判断year是否是闰年以及a是否是大写字母。

4 使用符号常量N代表学生人数,进一步完善“学生成绩管理系统”。

ps: 对应ppt模板下载地址如下:

08-变量类别与编译预处理-自己使用的C语言教程PPT-适合老师备课或者自学.pptx-C文档类资源-CSDN下载


 

 全部课程的PPT已经整理好了,需要的童鞋可以点击下载
总-自己使用的C语言教程PPT-适合老师备课或者自学-15篇PPT合集.zip-C文档类资源-CSDN下载

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

锁住子锁不住

老少爷们向前冲!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值