|
变量类别与编译预处理
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>Max)
Max=array;
if(array<Min)
Min=array;
array_aver+=array;}
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);
IN(“%ld”,&num);}
for(i=0;i<N;i++)
{ OUT(“%s\t”,name);
OUT(“%ld\n”,num);}
}
运行结果 :
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!=’\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)!=‘\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(&#34;%ld\t&#34;,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模板下载地址如下:
全部课程的PPT已经整理好了,需要的童鞋可以点击下载
总-自己使用的C语言教程PPT-适合老师备课或者自学-15篇PPT合集.zip-C文档类资源-CSDN下载
学习编程,必须要有一款质量过硬的电脑,一般配置建议8G+ 内存,500g硬盘,i5以上处理器。再有就是便宜一点。比如: |
|