博文
C语言初学者入门讲座 第十五讲 预处理(2007-05-04 17:22:00)
摘要:C语言初学者入门讲座 第十五讲 预处理
概述
在前面各章中,已多次使用过以“#”号开头的预处理命令。如包含命令# include,宏定义命令# define等。在源程序中这些命令都放在函数之外, 而且一般都放在源文件的前面,它们称为预处理部分。
所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是C语言的一个重要功能, 它由预处理程序负责完成。当对一个源文件进行编译时, 系统将自动引用预处理程序对源程序中的预处理部分作处理, 处理完毕自动进入对源程序的编译。
C语言提供了多种预处理功能,如宏定义、文件包含、 条件编译等。合理地使用预处理功能编写的程序便于阅读、修改、 移植和调试,也有利于模块化程序设计。本章介绍常用的几种预处理功能。
宏定义
在C语言源程序中允许用一个标识符来表示一个字符串, 称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换, 这称为“宏代换”或“宏展开”。
宏定义是由源程序中的宏定义命令完成的。 宏代换是由预处理程序自动完成的。在C语言中,“宏”分为有参数和无参数两种。 下面分别讨论这两种“宏”的定义和调用。
无参宏定义
无参宏的宏名后不带参数。其定义的一般形式为: #define 标识符 字符串其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。 “标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。在前面介绍过的符号常量的定义就是一种无参宏定义。此外,常对程序中反复使用的表达式进行宏定义。例如: # define M (y*y+3*y) 定义M表达式(y*y+3*y)。在编写源程序时,所有的(y*y+3*y)都可由M代替,而对源程序作编译时,将先由预处理程序进行宏代换,即用 (y*y+3*y)表达式去置换所有的宏名M,然后再进行编译。
#define M (y*y+3*y)
main(){
int s,y;
printf("input a number: ");
scanf("%d",&y);
s=3*M+4*M+5*M;
printf("s=%d/n",s);
}
......
C语言初学者入门讲座 第十四讲 枚举与位运算(2)(2007-05-04 17:21:00)
摘要:C语言初学者入门讲座 第十四讲 枚举与位运算(2)
位域
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:
struct 位域结构名
{ 位域列表 };
其中位域列表的形式为: 类型说明符 位域名:位域长度
例如:
struct bs
{
int a:8;
int b:2;
int c:6;
};
位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:
struct bs
{
int a:8;
int b:2;
int c:6;
}data;
说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:
1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
struct bs
{
unsigned a:4
unsigned :0 /*空域*/
unsigned b:4 /*从下一单元开始存放*/
unsigned c:4
}
在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。
2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。
3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
struct k
{
int a:1
int :2 /*该2位不能使用*/
int b:3
int c:2
};
......
C语言初学者入门讲座 第十四讲 枚举与位运算(1)(2007-05-04 17:21:00)
摘要:C语言初学者入门讲座 第十四讲 枚举与位运算(1)
在实际问题中,有些变量的取值被限定在一个有限的范围内。例如,一个星期内只有七天,一年只有十二个月, 一个班每周有六门课程等等。如果把这些量说明为整型,字符型或其它类型显然是不妥当的。 为此,C语言提供了一种称为“枚举”的类型。在“枚举”类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围。应该说明的是, 枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。
枚举类型的定义和枚举变量的说明
一、枚举的定义
枚举类型定义的一般形式为:
enum 枚举名
{
枚举值表
};
在枚举值表中应罗列出所有可用值。这些值也称为枚举元素。
例如:
enum weekday
{
sun,mou,tue,wed,thu,fri,sat
};
该枚举名为weekday,枚举值共有7个,即一周中的七天。 凡被说明为weekday类型变量的取值只能是七天中的某一天。
二、枚举变量的说明
如同结构和联合一样,枚举变量也可用不同的方式说明, 即先定义后说明,同时定义说明或直接说明。设有变量a,b,c被说明为上述的weekday,可采用下述任一种方式:
enum weekday
{
......
};
enum weekday a,b,c;或者为: enum weekday
{
......
}a,b,c;或者为: enum
{
......
}a,b,c;
枚举类型变量的赋值和使用
枚举类型在使用中有以下规定:
1. 枚举值是常量,不是变量。不能在程序中用赋值语句再对它赋值。例如对枚举weekday的元素再作以下赋值: sun=5;mon=2;sun=mon; 都是错误的。
2. 枚举元素本身由系统定义了一个表示序号的数值,从0 开始顺序定义为0,1,2…。如在weekday中,sun值为0,mon值为1, …,sat值为6。
main(){
enum weekday
{
sun,mon,tue,wed,thu,fri,sat
} a,b,c;
a=sun;
b=mon;
......
C语言初学者入门讲座 第十三讲 联合(2007-05-04 17:19:00)
摘要:C语言初学者入门讲座 第十三讲 联合
“联合”与“结构”有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间,一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。应该说明的是,这里所谓的共享不是指把多个成员同时装入一个联合变量内, 而是指该联合变量可被赋予任一成员值,但每次只能赋一种值,赋入新值则冲去旧值。如前面介绍的“单位”变量,如定义为一个可装入“班级”或“教研室”的联合后,就允许赋予整型值(班级)或字符串(教研室)。要么赋予整型值,要么赋予字符串,不能把两者同时赋予它。联合类型的定义和联合变量的说明一个联合类型必须经过定义之后, 才能把变量说明为该联合类型。
一、联合的定义
定义一个联合类型的一般形式为:
union 联合名
{
成员表
};
成员表中含有若干成员,成员的一般形式为: 类型说明符 成员名 成员名的命名应符合标识符的规定。
例如:
union perdata
{
int class;
char office[10];
};
定义了一个名为perdata的联合类型,它含有两个成员,一个为整型,成员名为class;另一个为字符数组,数组名为office。联合定义之后,即可进行联合变量说明,被说明为perdata类型的变量,可以存放整型量class或存放字符数组office。
二、联合变量的说明
联合变量的说明和结构变量的说明方式相同, 也有三种形式。即先定义,再说明;定义同时说明和直接说明。以perdata类型为例,说明如下:
union perdata
{
int class;
char officae[10];
};
union perdata a,b; /*说明a,b为perdata类型*/
或者可同时说明为:
union perdata
{
int class;
char office[10];
}
a,b;或直接说明为:
union
{
int class;
char office[10];
}
a,b
经说明后的a,b变量均为perdata类型。 它们的内存分......
C语言初学者入门讲座 第十二讲 结构(3)(2007-05-04 17:18:00)
摘要:C语言初学者入门讲座 第十二讲 结构(3)
结构指针变量作函数参数
在ANSI C标准中允许用结构变量作函数参数进行整体传送。 但是这种传送要将全部成员逐个传送,特别是成员为数组时将会使传送的时间和空间开销很大,严重地降低了程序的效率。 因此最好的办法就是使用指针,即用指针变量作函数参数进行传送。这时由实参传向形参的只是地址,从而减少了时间和空间的开销。
[例7.8]题目与例7.4相同,计算一组学生的平均成绩和不及格人数。
用结构指针变量作函数参数编程。
struct stu
{
int num;
char *name;
char sex;
float score;}boy[5]={
{101,"Li ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"He fang",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58},
};
main()
{
struct stu *ps;
void ave(struct stu *ps);
ps=boy;
ave(ps);
}
void ave(struct stu *ps)
{
int c=0,i;
float ave,s=0;
for(i=0;iscore;
if(ps->scorenum=102;
ps->name="Zhang ping";
ps->sex='M';
ps->score=62.5;
printf("Number=%d/nName=%s/n",ps->num,ps->name);
printf("Sex=%c/nScore=%f/n",ps->sex,ps->score);
free(ps);
}
本例中,定义了结构stu,定义了stu类型指针变量ps。然后分配一块stu大内存区,并把首地址赋予ps,使ps指向该区域。再以ps为指向结构的指针变量对各成员赋值,并用printf 输出各成员值。最后用free函数释放ps指向的内存空间。 整个程序包含了申请内存空间、使用内存空间、释放内存空间三个步骤,实现存储空间的动态......
C语言初学者入门讲座 第十二讲 结构(2)(2007-05-04 17:17:00)
摘要:C语言初学者入门讲座 第十二讲 结构(2)
结构数组
数组的元素也可以是结构类型的。 因此可以构成结构型数组。结构数组的每一个元素都是具有相同结构类型的下标结构变量。 在实际应用中,经常用结构数组来表示具有相同数据结构的一个群体。如一个班的学生档案,一个车间职工的工资表等。
结构数组的定义方法和结构变量相似,只需说明它为数组类型即可。例如:
struct stu
{
int num;
char *name;
char sex;
float score;
}boy[5];
定义了一个结构数组boy1,共有5个元素,boy[0]~boy[4]。每个数组元素都具有struct stu的结构形式。 对外部结构数组或静态结构数组可以作初始化赋值,例如:
struct stu
{
int num;
char *name;
char sex;
float score;
}boy[5]={
{101,"Li ping","M",45},
{102,"Zhang ping","M",62.5},
{103,"He fang","F",92.5},
{104,"Cheng ling","F",87},
{105,"Wang ming","M",58};
}
当对全部元素作初始化赋值时,也可不给出数组长度。
[例7.4]计算学生的平均成绩和不及格的人数。
struct stu
{
int num;
char *name;
char sex;
float score;
}boy[5]={
{101,"Li ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"He fang",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58},
};
main()
{
int i,c=0;
float ave,s=0;
for(i=0;iname);
printf("Sex=%c/nScore=%f/n/n",pstu->sex,pstu->score);
}
本例程序定义了一个结构stu,定义了......
C语言初学者入门讲座 第十二讲 结构(1)(2007-05-04 17:15:00)
摘要:C语言初学者入门讲座 第十二讲 结构(1)
在实际问题中,一组数据往往具有不同的数据类型。例如, 在学生登记表中,姓名应为字符型;学号可为整型或字符型;年龄应为整型;性别应为字符型;成绩可为整型或实型。 显然不能用一个数组来存放这一组数据。因为数组中各元素的类型和长度都必须一致,以便于编译系统处理。为了解决这个问题,C语言中给出了另一种构造数据类型——“结构”。它相当于其它高级语言中的记录。
“结构”是一种构造类型,它是由若干“成员”组成的。 每一个成员可以是一个基本数据类型或者又是一个构造类型。 结构既是一种“构造”而成的数据类型, 那么在说明和使用之前必须先定义它,也就是构造它。如同在说明和调用函数之前要先定义函数一样。
结构的定义
定义一个结构的一般形式为:
struct 结构名
{
成员表列
};
成员表由若干个成员组成, 每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为:
类型说明符 成员名;
成员名的命名应符合标识符的书写规定。例如:
struct stu
{
int num;
char name[20];
char sex;
float score;
};
在这个结构定义中,结构名为stu,该结构由4个成员组成。第一个成员为num,整型变量;第二个成员为name,字符数组;第三个成员为sex,字符变量;第四个成员为score,实型变量。应注意在括号后的分号是不可少的。结构定义之后,即可进行变量说明。 凡说明为结构stu的变量都由上述4个成员组成。由此可见,结构是一种复杂的数据类型,是数目固定,类型不同的若干有序变量的集合。
结构类型变量的说明
说明结构变量有以下三种方法。以上面定义的stu为例来加以说明。
1. 先定义结构,再说明结构变量。如:
struct stu
{
int num;
char name[20];
char sex;
float score;
};
struct stu boy1,boy2;
说明了两个变量boy1和boy2为stu结构类型。也可以用宏定义使一个符号常量来表示一个结构类型,例如:
#def......
C语言初学者入门讲座 第十二讲 多维数组的指针变量(2007-05-04 17:14:00)
摘要:C语言初学者入门讲座 第十二讲 多维数组的指针变量
一、多维数组地址的表示方法
设有整型二维数组a[3][4]如下:
0 1 2 3
4 5 6 7
8 9 10 11
设数组a的首地址为1000,各下标变量的首地址及其值如图所示。
在前面曾经介绍过, C语言允许把一个二维数组分解为多个一维数组来处理。因此数组a可分解为三个一维数组,即a[0],a[1],a[2]。每一个一维数组又含有四个元素。例如a[0]数组,含有a[0][0],a[0][1],a[0][2],a[0][3]四个元素。数组及数组元素的地址表示如下:a是二维数组名,也是二维数组0行的首地址,等于1000。a[0]是第一个一维数组的数组名和首地址,因此也为 1000。*(a+0)或*a是与a[0]等效的, 它表示一维数组a[0]0 号元素的首地址。也为1000。&a[0][0]是二维数组a的0行0列元素首地址,同样是1000。因此,a,a[0],*(a+0),*a,&a [0][0]是相等的。同理,a+1是二维数组1行的首地址,等于1008。a[1]是第二个一维数组的数组名和首地址,因此也为1008。 &a[1][0]是二维数组a的1行0列元素地址,也是1008。因此a+1,a[1],*(a+1),&a[1][0]是等同的。由此可得出:a+i,a[i],*(a+i),&a[i][0]是等同的。此外,&a[i]和a[i]也是等同的。因为在二维数组中不能把&a[i]理解为元素a[i]的地址,不存在元素a[i]。
C语言规定,它是一种地址计算方法,表示数组a第i行首地址。由此,我们得出:a[i],&a[i],*(a+i)和a+i也都是等同的。另外,a[0]也可以看成是a[0]+0是一维数组a[0]的0号元素的首地址,而a[0]+1则是a[0]的1号元素首地址,由此可得出a[i]+j则是一维数组a[i]的j号元素首地址,它等于&a[i][j]。由a [i]=*(a+i)得a[i]+j=*(a+i)+j,由于*(a+i)+j是二维数组a的i行j列元素的首地址。该元素的值等于*(*(a+i)+ j)。
[Explain]
#define PF "%d,%d,%d,%d,%d,/n"
main(){
static int a[3][......
C语言初学者入门讲座 第十一讲 指针的慨念(2)(2007-05-04 17:13:00)
摘要:C语言初学者入门讲座 第十一讲 指针的慨念(2)
(2)加减算术运算
对于指向数组的指针变量,可以加上或减去一个整数n。设pa是指向数组a的指针变量,则pa+n,pa-n,pa++,++pa,pa--,--pa 运算都是合法的。指针变量加或减一个整数n的意义是把指针指向的当前位置(指向某数组元素)向前或向后移动n个位置。应该注意,数组指针变量向前或向后移动一个位置和地址加1或减1 在概念上是不同的。因为数组可以有不同的类型,各种类型的数组元素所占的字节长度是不同的。如指针变量加1,即向后移动1 个位置表示指针变量指向下一个数据元素的首地址。而不是在原地址基础上加1。
例如:
int a[5],*pa;
pa=a; /*pa指向数组a,也是指向a[0]*/
pa=pa+2; /*pa指向a[2],即pa的值为&pa[2]*/ 指针变量的加减运算只能对数组指针变量进行,对指向其它类型变量的指针变量作加减运算是毫无意义的。(3)两个指针变量之间的运算只有指向同一数组的两个指针变量之间才能进行运算,否则运算毫无意义。
①两指针变量相减
两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。实际上是两个指针值(地址) 相减之差再除以该数组元素的长度(字节数)。例如pf1和pf2 是指向同一浮点数组的两个指针变量,设pf1的值为2010H,pf2的值为2000H,而浮点数组每个元素占4个字节,所以pf1-pf2的结果为 (2000H-2010H)/4=4,表示pf1和 pf2之间相差4个元素。两个指针变量不能进行加法运算。 例如, pf1+pf2是什么意思呢?毫无实际意义。
②两指针变量进行关系运算
指向同一数组的两指针变量进行关系运算可表示它们所指数组元素之间的关系。例如:
pf1==pf2表示pf1和pf2指向同一数组元素
pf1>pf2表示pf1处于高地址位置
pf1b){
pmax=&a;
pmin=&b;
}
else{
pmax=&b;
pmin=&a;
}
if(c>*pmax) pmax=&c;
if(c......
C语言初学者入门讲座 第十一讲 指针的慨念(1)(2007-05-04 17:13:00)
摘要:C语言初学者入门讲座 第十一讲 指针的慨念(1)
指针是C语言中广泛使用的一种数据类型。运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; 能很方便地使用数组和字符串;并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C语言的功能。 学习指针是学习C语言中最重要的一环,能否正确理解和使用指针是我们是否掌握C语言的一个标志。同时,指针也是C语言中最为困难的一部分,在学习中除了要正确理解基本概念,还必须要多编程,上机调试。只要作到这些,指针也是不难掌握的。
指针的基本概念 在计算机中,所有的数据都是存放在存储器中的。 一般把存储器中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不等,如整型量占2个单元,字符量占1个单元等, 在第二章中已有详细的介绍。为了正确地访问这些内存单元,必须为每个内存单元编上号。 根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址。既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。 内存单元的指针和内存单元的内容是两个不同的概念。可以用一个通俗的例子来说明它们之间的关系。我们到银行去存取款时, 银行工作人员将根据我们的帐号去找我们的存款单,找到之后在存单上写入存款、取款的金额。在这里,帐号就是存单的指针, 存款数是存单的内容。对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该单元的内容。在C语言中, 允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。图中,设有字符变量C,其内容为“K”(ASCII码为十进制数 75),C占用了011A号单元(地址用十六进数表示)。设有指针变量P,内容为011A,这种情况我们称为P指向变量C,或说P是指向变量C的指针。 严格地说,一个指针是一个地址,是一个常量。而一个指针变量却可以被赋予不同的指针值,是变。 但在常把指针变量简称为指针。为了避免混淆,我们中约定:“指针”是指地址,是常量,“指针变量”是指取值为地址的变量。 定义指针的目的是为了通过指针去访问内存单元。
既然指针变量的值是一个地址, 那么这个地址不仅可以是变量的地址, 也可以是其它数据结构的地址。在一个指针变量中存放一
个数......