资源简介 (共55张PPT)第10章 指 针指针是C语言中的重要概念,也是C语言的重要特色。使用指针,可以使程序更加简洁、紧凑、高效。10.1 指针和指针变量的概念 10.2 指针变量的定义与应用10.3 数组的指针和指向数组的指针变量 10.4 字符串的指针和指向字符串的指针变量10.5 返回指针值的函数 10.6 指针数组与主函数main()的形参 10.7 函数的指针和指向函数的指针变量[Return]10.1 指针和指针变量的概念1.内存地址──内存中存储单元的编号(1)计算机硬件系统的内存储器中,拥有大量的存储单元(容量为1字节)。为了方便管理,必须为每一个存储单元编号,这个编号就是存储单元的“地址”。每个存储单元都有一个惟一的地址。(2)在地址所标识的存储单元中存放数据。注意:内存单元的地址与内存单元中的数据是两个完全不同的概念。2.变量地址──系统分配给变量的内存单元的起始地址假设有这样一个程序:main(){ int num;scanf("%d",&num);printf("num=%d\n", num);}C编译程序编译到该变量定义语句时,将变量num 登录到“符号表”中。符号表的关键属性有两个:一是“标识符名(id)” ,二是该标识符在内存空间中的“地址(addr)” 。为描述方便,假设系统分配给变量num的2字节存储单元为 3000 和3001,则起始地址3000就是变量num在内存中的地址。3.变量值的存取──通过变量在内存中的地址进行系统执行“scanf(”%d“,&num);”和“printf(”num=%d\n“, num);”时,存取变量num值的方式可以有两种:(1)直接访问──直接利用变量的地址进行存取1)上例中scanf(“%d”,&num)的执行过程是这样的:用变量名num作为索引值,检索符号表,找到变量num的起始地址3000;然后将键盘输入的值(假设为3)送到内存单元3000和3001中。此时,变量num在内存中的地址和值,如图9-1所示。2)printf("num=%d\n",num)的执行过程,与scanf()很相似:首先找到变量num的起始地址3000,然后从3000和3001中取出其值,最后将它输出。(2)间接访问──通过另一变量访问该变量的值C语言规定:在程序中可以定义一种特殊的变量(称为指针变量),用来存放其它变量的地址。例如,假设定义了这样一个指针变量num_pointer,它被分配到4000、4001单元,其值可通过赋值语句“num_pointer=&num;”得到。此时,指针变量num_pointer的值就是变量num在内存中的起始地址3000,如图9-1所示。通过指针变量num_pointer存取变量num值的过程如下:首先找到指针变量num_pointer的地址(4000),取出其值3000(正好是变量num 的起始地址); 然后从3000、3001中取出变量num的值(3)。(3)两种访问方式的比较两种访问方式之间的关系,可以用某人甲(系统)要找某人乙(变量)来类比。一种情况是,甲知道乙在何处,直接去找就是(即直接访问)。另一种情况是,甲不知道乙在哪,但丙(指针变量)知道,此时甲可以这么做:先找丙,从丙处获得乙的去向,然后再找乙(即间接访问)。4.指针与指针变量(1)指针──即地址一个变量的地址称为该变量的指针。通过变量的指针能够找到该变量。(2)指针变量──专门用于存储其它变量地址的变量指针变量num_pointer的值就是变量num的地址。指针与指针变量的区别,就是变量值与变量的区别。(3)为表示指针变量和它指向的变量之间的关系,用指针运算符“*”表示。例如,指针变量num_pointer与它所指向的变量num的关系,表示为:*num_pointer,即*num_pointer等价于变量num。因此,下面两个语句的作用相同:num=3; /*将3直接赋给变量num*/num_pointer=# /*使num_pointer指向num */*num_pointer=3; /*将3赋给指针变量num_pointer所指向的变量*/[Return]10.2 指针变量的定义与应用10.2.1 指针变量的定义与相关运算[案例9.1] 指针变量的定义与相关运算示例。 /*案例代码文件名:AL9_1.C*/main() { int num_int=12, *p_int; /*定义一个指向int型数据的指针变量p_int */ float num_f=3.14, *p_f; /*定义一个指向float型数据的指针变量p_f */ char num_ch=’p’, *p_ch; /*定义一个指向char型数据的指针变量p_ch */ p_int=&num_int; /*取变量num_int的地址,赋值给p_int */ p_f=&num_f; /*取变量num_f的地址,赋值给p_f */ p_ch=&num_ch; /*取变量num_ch的地址,赋值给p_ch */ printf(“num_int=%d, *p_int=%d\n”, num_int, *p_int); printf(“num_f=%4.2f, *p_f=%4.2f\n”, num_f, *p_f); printf(“num_ch=%c, *p_ch=%c\n”, num_ch, *p_ch); }程序运行结果:num_int=12, *p_int=12num_f=3.14, *p_f=3.14num_ch=p, *p_ch=p程序说明:(1)头三行的变量定义语句──指针变量的定义与一般变量的定义相比,除变量名前多了一个星号“*” (指针变量的定义标识符)外,其余一样:数据类型 *指针变量[, *指针变量2……];注意:此时的指针变量p_int、p_f、p_ch,并未指向某个具体的变量(称指针是悬空的)。使用悬空指针很容易破坏系统,导致系统瘫痪。(2)中间三行的赋值语句──取地址运算(&)取地址运算的格式: &变量例如,&num_int、&num_f、&num_ch的结果,分别为变量num_int、num_f、num_ch的地址。注意:指针变量只能存放指针(地址),且只能是相同类型变量的地址。例如,指针变量p_int、p_f、p_ch,只能分别接收int型、float型、char型变量的地址,否则出错。(3)后三行的输出语句──指针运算(*)使用直接访问和间接访问两种方式,分别输出变量num_int、num_f、num_ch的值。注意:这三行出现在指针变量前的星号“*”是指针运算符,访问指针变量所指向的变量的值,而非指针运算符。[案例9.2] 使用指针变量求解:输入2个整数,按升序(从小到大排序)输出。/*案例代码文件名:AL9_2.C*//*程序功能:使用指针变量求解2个整数的升序输出*/main(){ int num1,num2;int *num1_p=&num1, *num2_p=&num2, *pointer;printf(“Input the first number: ”); scanf(“%d”,num1_p);printf(“Input the second number: ”); scanf(“%d”,num2_p);printf(“num1=%d, num2=%d\n”, num1, num2);if( *num1_p > *num2_p ) /*如果num1>num2,则交换指针*/pointer= num1_p, num1_p= num2_p, num2_p=pointer;printf(“min=%d, max=%d\n”, *num1_p, *num2_p);}程序运行情况:Input the first number:9←┘Input the second number:6←┘num1=9, num2=6min=6, max=9程序说明:(1)第5行的if语句如果*num1_p>*num2_p (即num1>num2),则交换指针,使num1_p指向变量num2(较小值),num2_p指向变量num1(较大值)。(2)printf(“min=%d, max=%d\n”, *num1_p, *num2_p); 语句:通过指针变量,间接访问变量的值。本案例的处理思路是:交换指针变量num1_p 和num2_p的值,而不是变量num1和num2的值(变量num1和num2并未交换,仍保持原值),最后通过指针变量输出处理结果。10.2.2 指针变量作函数参数1.指针变量,既可以作为函数的形参,也可以作函数的实参。2.指针变量作实参时,与普通变量一样,也是“值传递”,即将指针变量的值(一个地址)传递给被调用函数的形参(必须是一个指针变量)。注意:被调用函数不能改变实参指针变量的值,但可以改变实参指针变量所指向的变量的值。[案例9.3] 使用函数调用方式改写[案例9.2],要求实参为指针变量。/*案例代码文件名:AL9_3.C*/ /******************************************************/ /*exchange()功能:交换2个形参指针变量所指向的变量的值 */ /*形参:2个,均为指向整型数据的指针变量 */ /*返回值:无 */ /******************************************************/ void exchange(int *pointer1, int *pointer2) { int temp; temp=*pointer1, *pointer1=*pointer2, *pointer2=temp; }/*主函数main()*/main(){ int num1,num2;/*定义并初始化指针变量num1_p和 num2_p */int *num1_p=&num1, *num2_p=&num2;printf(“Input the first number: ”); scanf(“%d”, num1_p);printf(“Input the second number: ”);scanf(“%d”, num2_p);printf(“num1=%d, num2=%d\n”, num1, num2);if( *num1_p > *num2_p ) /* 即num1>num2)*/exchange(num1_p, num2_p); /*指针变量作实参*//*输出排序后的num1和num2的值*/printf(“min=%d, max=%d\n”, num1, num2);}程序运行情况:Input the first number:9←┘Input the second number:6←┘num1=9, num2=6min=6, max=9调用函数exchange()之前、之时、结束时和结束后的情况,如图9-5所示。形参指针变量pointer1(指向变量num1)和pointer2(指向变量num2),在函数调用开始时才分配存储空间,函数调用结束后立即被释放。虽然被调用函数不能改变实参指针变量的值,但可以改变它们所指向的变量的值。总结:为了利用被调用函数改变的变量值,应该使用指针(或指针变量)作函数实参。其机制为:在执行被调用函数时,使形参指针变量所指向的变量的值发生变化;函数调用结束后,通过不变的实参指针(或实参指针变量)将变化的值保留下来。[案例9.4] 输入3个整数,按降序(从大到小的顺序)输出。要求使用变量的指针作函数调用的实参来实现。/*案例代码文件名:AL9_4.C*//******************************************************//*exchange()功能:交换2个形参指针变量所指向的变量的值 *//*形参:2个,均为指向整型数据的指针变量 *//*返回值:无 *//******************************************************/void exchange(int *pointer1, int *pointer2){ int temp;temp=*pointer1, *pointer1=*pointer2, *pointer2=temp;}/*主函数main()*/main(){ int num1,num2,num3;/*从键盘上输入3个整数*/printf(“Input the first number: ”); scanf(“%d”, &num1);printf(“Input the second number: ”); scanf(“%d”, &num2);printf(“Input the third number: ”); scanf(“%d”, &num3);printf(“num1=%d, num2=%d, num3=%d\n”, num1, num2, num3);/*排序*/if( num1 < num2 ) /*num1exchange( &num1, &num2 );if( num1 < num3 ) exchange( &num1, &num3 );if( num2 < num3 ) exchange( &num2, &num3 );/*输出排序结果*/printf(“排序结果: %d, %d, %d\n”,num1,num2,num3);}程序运行情况:Input the first number:9←┘Input the second number:6←┘Input the third number:12←┘num1=9, num2=6, num3=12排序结果: 12, 9, 6[Return]10.3 数组的指针和指向数组的指针变量10.3.1 概述1.概念数组的指针──数组在内存中的起始地址,数组元素的指针──数组元素在内存中的起始地址。2.指向数组的指针变量的定义指向数组的指针变量的定义,与指向普通变量的指针变量的定义方法一样。例如,int array[10], *pointer=array(或&array[0]); 或者: int array[10], *pointer;pointer=array;注意:数组名代表数组在内存中的起始地址(与第1个元素的地址相同),所以可以用数组名给指针变量赋值。3.数组元素的引用数组元素的引用,既可用下标法,也可用指针法。使用下标法,直观;而使用指针法,能使目标程序占用内存少、运行速度快。10.3.2 通过指针引用数组元素如果有“int array[10],*pointer=array;” ,则:(1)pointer+i和array+i都是数组元素array[i]的地址,如图9-6所示。(2)*(pointer+i)和*(array+i)就是数组元素array[i]。(3)指向数组的指针变量,也可将其看作是数组名,因而可按下标法来使用。例如,pointer[i]等价于*(pointer+i)。注意:pointer+1指向数组的下一个元素,而不是简单地使指针变量pointer的值+1。其实际变化为pointer+1*size(size为一个元素占用的字节数)。例如,假设指针变量pointer的当前值为3000,则pointer+1为3000+1*2=3002,而不是3001。[案例9.5] 使用指向数组的指针变量来引用数组元素。/*案例代码文件名:AL9_5.C*//*程序功能:使用指向数组的指针变量来引用数组元素*/main(){ int array[10], *pointer=array, i;printf(“Input 10 numbers: ”);for(i=0; i<10; i++)scanf(“%d”, pointer+i); /*使用指针变量来输入数组元素的值*/printf(“array[10]: ”);for(i=0; i<10; i++)printf(“%d ”, *(pointer+i)); /*使用指向数组的指针变量输出数组*/printf(“\n”);}程序运行情况:Input 10 numbers: 0 1 2 3 4 5 6 7 8 9←┘array[10]: 0 1 2 3 4 5 6 7 8 9程序说明:程序中第3行和第6行的2个for语句,等价于下面的程序段:for(i=0; i<10; i++,pointer++)scanf(“%d”,pointer);printf(“array[10]: ”);pointer=array; /*使pointer重新指向数组的第一个元素*/for(i=0; i<10; i++,pointer++)printf(“%d”,*pointer);思考题:(1)如果去掉“pointer=array;”行,程序运行结果会如何?请上机验证。(2)在本案例中,也可以不使用i来作循环控制变量,程序怎么修改?提示:指针可以参与关系运算。说明:(1)指针变量的值是可以改变的,所以必须注意其当前值,否则容易出错。(2)指向数组的指针变量,可以指向数组以后的内存单元,虽然没有实际意义。(3)对指向数组的指针变量(px和py)进行算术运算和关系运算的含义1)可以进行的算术运算,只有以下几种:px±n, px++/++px, px--/--px, px-py·px±n:将指针从当前位置向前(+n)或回退(-n)n个数据单位,而不是n个字节。显然,px++/++px和px--/--px是px±n的特例(n=1)。·px-py:两指针之间的数据个数,而不是指针的地址之差。2)关系运算表示两个指针所指地址之间、位置的前后关系:前者为小,后者为大。例如,如果指针px所指地址在指针py所指地址之前,则px〈py的值为1。10.3.3 再论数组作函数参数数组名作形参时,接收实参数组的起始地址;作实参时,将数组的起始地址传递给形参数组。引入指向数组的指针变量后,数组及指向数组的指针变量作函数参数时,可有4种等价形式(本质上是一种,即指针数据作函数参数):(1)形参、实参都用数组名(2)形参、实参都用指针变量(3)形参用指针变量、实参用数组名(4)形参用数组名、实参用指针变量10.3.4 2维数组的指针及其指针变量1. 2维数组的指针 假设有如下数组定义语句: int array[3][4]; (1)从2维数组角度看,数组名array代表数组的起始地址, 是一个以行为单位进行控制的行指针: ·array+i:行指针值,指向2维数组的第i行。 ·*(array+i):(列)指针值,指向第i行第0列(控制由行转为列,但仍为指针)。 ·*(*(array+i)):数组元素array[i][0]的值。 用array作指针访问数组元素array[i][j]的格式:*(*(array+i)+j)注意:行指针是一个2级指针,如图9-7所示。(2)从1维数组角度看,数组名array和第1维下标的每一个值, 共同构成一组新的1维数组名array[0]、array[1]、array[2],它们均由4个元素组成。C语言规定:数组名代表数组的地址,所以array[i]是第i行1维数组的地址, 它指向该行的第0列元素,是一个以数组元素为单位进行控制的列指针:·array[i]+j:(列)指针值,指向数组元素array[i][j]。·*(array[i]+j):数组元素array[i][j]的值。如果有“int array[3][4],*p=array[0];”,则p+1指向下一个元素,如图9-8所示。用p作指针访问数组元素array[i][j]的格式:*(p+(i*每行列数+j) )2.行指针变量──指向由n个元素组成的一维数组的指针变量(1)定义格式数据类型 (*指针变量)[n];注意:“*指针变量”外的括号不能缺,否则成了指针数组——数组的每个元素都是一个指针──指针数组(本章第6节介绍)。(2)赋值行指针变量 = 2维数组名 | 行指针变量;[案例9.6] 使用行指针和列指针两种方式输出2维数组的任一元素。(1) 使用行指针/*案例代码文件名:AL9_6_1.C*//*程序功能:使用行指针输出2维数组的任一元素*/main(){ int array[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};int (*pointer)[4], row, col;pointer=array;printf(“Input row = ”); scanf(“%d”, &row);printf(“Input col = ”); scanf(“%d”, &col);printf(“array[%1d][%1d] = %d\n”, row, col, *(*(pointer+row)+col));} [程序演示]程序运行情况:Input row = 1←┘Input col = 2←┘array[1][2] = 7思考题:本题也可以直接使用数组名array作指针,应如何修改?(2)使用列指针/*案例代码文件名:AL9_6_2.C*//*程序功能:使用列指针输出2维数组的任一元素*/main(){ int array[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};int *pointer, row, col; /*定义一个(列)指针变量pointer*/pointer=array[0]; /*给(列)指针变量pointer赋值*/printf(“Input row = ”); scanf(“%d”,&row);printf(“Input col = ”); scanf(“%d”,&col);printf(“array[%1d][%1d] = %d\n”, row, col, *(pointer+(row*4+col)));}3. 2维数组指针作函数参数2维数组的指针作函数实参时,有列指针和行指针两种形式。相应的,用来接受实参数组指针的形参,必须使用相应形式的指针变量,如下所示:实参: 列指针 行指针↓ ↓形参: (列)指针变量 行指针变量10.3.5 动态数组的实现在程序运行过程中,数组的大小是不能改变的。这种数组称为静态数组。静态数组的缺点是:对于事先无法准确估计数据量的情况,无法做到既满足处理需要,又不浪费内存空间。所谓动态数组是指,在程序运行过程中,根据实际需要指定数组的大小。在C语言中,可利用内存的申请和释放库函数,以及指向数组的指针变量可当数组名使用的特点,来实现动态数组。动态数组的本质是:一个指向数组的指针变量。[案例9.7] 动态数组的实现。/*案例代码文件名:AL9_7.C*//*程序功能:实现动态数组*/#include “alloc.h”#include “stdlib.h”main(){ int *array=NULL, num, i;printf(“Input the number of element: ”); scanf(“%d”, &num);/*申请动态数组使用的内存块*/array=(int *)malloc( sizeof(int) * num );if ( array==NULL ) /*内存申请失败:提示,退出*/{ printf(“out of memory, press any key to quit……”);exit(0); /*exit():终止程序运行,返回操作系统*/}/*提示输入num个数据*/printf(“Input %d elements: ”, num);for (i=0; i/*输出刚输入的num个数据*/printf(“%d elements are: ”, num);for (i=0; iprintf(“\b ”); /*删除最后一个数据后的分隔符“,”*/free(array); /*释放由malloc()函数申请的内存块*/}程序运行情况:Input the number of element: 3←┘Input 3 elements: 1 2 3←┘3 elements are: 1,2,3程序说明:(1) array=(int *)malloc( sizeof(int) * num );语句──malloc()函数和sizeof运算符1)库函数malloc()·用法:void *malloc(unsigned size)·功能:在内存的动态存储区分配1个长度为size的连续空间。·返回值:申请成功,则返回新分配内存块的起始地址;否则,返回NULL。·函数原型:alloc.h,stdlib.h。malloc()函数的返回值是一个无类型指针,其特点是可以指向任何类型的数据。但在实际使用malloc()函数时,必须将其返回值强制转换成被赋值指针变量的数据类型,以免出错。2)运算符sizeof·格式:sizeof(变量名/类型名)·功能:求变量/类型占用的内存字节数(正整数)。例如,在IBM-PC机上,sizeof(int)=2。思考题:在该语句中,使用sizeof(int)求出1个int型数据占用的内存字节数,而不是使用常量“2”,为什么?(2) scanf(“%d”, &array[i]);语句和printf(“%d,”, array[i]);语句将指向数组的指针变量当作数组名使用,所以就必须按引用数组元素的语法规则来使用。(3) printf(“\b ”);语句“\b” 在该语句中的作用是,使光标定位到最后一个数据后的分隔符“,”上,然后再输出一个空格,以达到删除之目的。(4) free(array);语句──库函数free()·用法:void free(void *ptr)·功能:释放由ptr指向的内存块(ptr是调用malloc() 函数的返回值)。·返回值:无。·函数原型:stdlib.h,alloc.h。原则上,使用malloc()函数申请的内存块,操作结束后,应及时使用free()函数予以释放。尤其是循环使用malloc()函数时,如果不及时释放不再使用的内存块,很可能很快就耗尽系统的内存资源,从而导致程序无法继续运行。[Return]10.4 字符串的指针和指向字符串的指针变量字符串在内存中的起始地址称为字符串的指针,可以定义一个字符指针变量指向一个字符串。10.4.1 字符串的表示与引用在C语言中,既可以用字符数组表示字符串,也可用字符指针变量来表示;引用时,既可以逐个字符引用,也可以整体引用。1.逐个引用[案例9.8] 使用字符指针变量表示和引用字符串。/*案例代码文件名:AL9_8.C*/main(){ char *string=”I love Beijing.”;for(; *string!=’\0’; string++) printf(“%c”, *string);printf(“\n”);}程序运行结果:I love Beijing.程序说明:char *string="I love Beijing.";语句定义并初始化字符指针变量string:用串常量“I love Beijing.”的地址(由系统自动开辟、存储串常量的内存块的首地址)给string赋初值。该语句也可分成如下所示的两条语句:char *string;string="I love Beijing.";注意:字符指针变量string中,仅存储串常量的地址,而串常量的内容(即字符串本身),是存储在由系统自动开辟的内存块中,并在串尾添加一个结束标志’\0’。2.整体引用[案例9.9] 采取整体引用的办法,改写[案例9.8]。/*案例代码文件名:AL9_9.C*//*程序功能:使用字符指针变量表示和引用字符串*/main(){ char *string=”I love Beijing.”;printf(“%s\n”,string);}程序说明:printf("%s\n",string);语句通过指向字符串的指针变量string,整体引用它所指向的字符串的原理:系统首先输出string指向的第一个字符,然后使string自动加1,使之指向下一个字符;重复上述过程,直至遇到字符串结束标志。注意:其它类型的数组,是不能用数组名来一次性输出它的全部元素的,只能逐个元素输出。例如:int array[10]={……};......printf("%d\n",array); /*这种用法是非法的*/......3.字符指针变量与字符数组之比较虽然用字符指针变量和字符数组都能实现字符串的存储和处理,但二者是有区别的,不能混为一谈。(1)存储内容不同。字符指针变量中存储的是字符串的首地址,而字符数组中存储的是字符串本身(数组的每个元素存放一个字符)。(2)赋值方式不同。对字符指针变量,可采用下面的赋值语句赋值:char *pointer;pointer="This is a example.";而字符数组,虽然可以在定义时初始化,但不能用赋值语句整体赋值。下面的用法是非法的:char char_array[20];char_array="This is a example."; /*非法用法*/(3)指针变量的值是可以改变的,字符指针变量也不例外;而数组名代表数组的起始地址,是一个常量,而常量是不能被改变的。10.4.2 字符串指针作函数参数[案例9.10] 用函数调用方式,实现字符串的复制。/*案例代码文件名:AL9_10.C*//**********************************************************//*string_copy()函数:复制一个字符串 *//*形参:字符指针str_from接收源串,字符指针 str_to存储目标串 *//*返回值:无 *//**********************************************************/void string_copy(char *str_from, char *str_to){ int i=0;for(; (*(str_to+i)=*(str_from+i))!=’\0’; i++) ; /*循环体为空语句*/}main(){char array_str1[20]=”I am a teacher.”;char array_str2[20];string_copy(array_str1, array_str2); /*数组名作实参*/printf(“array_str2=%s\n”, array_str2);}程序运行结果:I am a teacher.程序说明:for(; (*(str_to+i)=*(str_from+i))!=’\0’; i++) ;语句的执行过程为:首先将源串中的当前字符,复制到目标串中;然后判断该字符(即赋值表达式的值)是否是结束标志。如果不是,则相对位置变量i的值增1,以便复制下一个字符;如果是结束标志,则结束循环。其特点是:先复制、后判断,循环结束前,结束标志已经复制。在C语言中,用赋值运算符、而不是赋值语句来实现赋值操作,能给某些处理带来很大的灵活性,该语句(实现字符串的复制)的用法就是最好的例证。[Return]10.5 返回指针值的函数一个函数可以返回一个int型、float型、char型的数据,也可以返回一个指针类型的数据。 返回指针值的函数(简称指针函数)的定义格式如下:函数类型 *函数名([形参表])[案例9.11] 某数理化三项竞赛训练组有3个人,找出其中至少有一项成绩不合格者。要求使用指针函数实现。 /*案例代码文件名:AL9_11.C*/ /*************************************************************/ /*seek()函数:判断是否有不合格成绩 */ /*形参:指向由3个int型元素组成的1维数组的行指针变量 */ /*返回值:(1)有不合格成绩,则返回指向本行首列的一个(列)指针; */ /* (2)没有有不合格成绩,返回值为指向下一行的一个(列)指针 */ /*************************************************************/int *seek( int (*pnt_row)[3] ){ int i=0, *pnt_col; /*定义一个(列)指针变量pnt_col */pnt_col=*(pnt_row+1); /*使pnt_col指向下一行之首(作标志用)*/for(; i<3; i++)if(*(*pnt_row+i)<60) /*某项成绩不合格*/{ pnt_col=*pnt_row; /*使pnt_col指向本行之首*/break; /*退出循环*/}return(pnt_col);}/*主函数main()*/main(){ int grade[3][3]={{55,65,75},{65,75,85},{75,80,90}};int i,j,*pointer; /*定义一个(列)指针变量pointer */for(i=0; i<3; i++) /*控制每个学生*/{ pointer=seek(grade+i); /*用行指针作实参,调用seek()函数*/if(pointer==*(grade+i)) /*该学生至少有一项成绩不合格*/{ /*输出该学生的序号和各项成绩*/printf(“No.%d grade list: ”, i+1);for(j=0; j<3; j++) printf(“%d ”,*(pointer+j));printf(“\n”);}}}程序运行结果:No.1 grade list: 55 65 75程序说明:(1) 主函数中的pointer=seek(grade+i);语句调用seek()函数时,将实参grade+i(行指针)的值,复制到形参pnt_row(行指针变量)中,使形参pnt_row指向grade数组的第i行。(2)在指针函数seek()中:1) pnt_col=*(pnt_row+1);语句*(pnt_row+1)将行指针转换为列指针,指向grade数组的第i+1行第0列,并赋值给(列)指针变量pnt_col。2) if(*(*pnt_row+i)<60)行pnt_row是一个行指针,指向数组grade的第i行;*pnt_row使指针由行转换为列,指向数组grade的第i行0列;*pnt_row+j的值还是一个指针,指向数组的第i行第j列;*(*pnt_row+j)是一个数据(数组元素grade[i][j]的值)。[Return]10.6 指针数组与主函数main()的形参10.6.1 指针数组 1.概念 数组的每个元素都是一个指针数据。指针数组比较适合用于指向多个字符串,使字符串处理更加方便、灵活。 2.定义格式数据类型 *数组名[元素个数] 注意:与行指针变量定义格式“<数据类型>(*行指针变量)[<元素个数>]”的差别。[案例9.12] 有若干计算机图书,请按字母顺序,从小到大输出书名。解题要求:使用排序函数完成排序,在主函数中进行输入输出。 /*案例代码文件名:AL9_12.C*/ /*程序功能:指针数组应用示例*//***********************************************//* sort()函数:对字符指针数组进行排序 *//*形参:name——字符指针数组,count——元素个数*//*返回值:无 *//***********************************************/void sort(char *name[], int count){ char *temp_p;int i,j,min;/*使用选择法排序*/for(i=0; i{ min=i; /*预置本次最小串的位置*/for(j=i+1; jif(strcmp(name[min],name[j])>0) /*存在更小的串*/min=j; /*保存之*/if(min!=i) /*存在更小的串,交换位置*/temp_p=name[i],name[i]=name[min],name[min]=temp_p;}}/*主函数main()*/main(){ char *name[5]={“BASIC”,”FORTRAN”,”PASCAL”,”C”,”FoxBASE”};int i=0;sort(name,5); /*使用字符指针数组名作实参,调用排序函数sort()*//*输出排序结果*/for(; i<5; i++) printf(“%s\n”,name[i]);}程序运行结果:BASICCFORTRANFoxBASEPASCAL程序说明:(1)实参对形参的值传递:sort( name , 5 );↓ ↓void sort(char *name[], int count)(2)字符串的比较只能使用strcmp()函数。形参字符指针数组name的每个元素,都是一个指向字符串的指针,所以有strcmp(name[min],name[j])。10.6.2 主函数main()的形参在以往的程序中,主函数main()都使用其无参形式。实际上,主函数main()也是可以指定形参的。[案例9.13] 用同一程序实现文件的加密和解密。约定:程序的可执行文件名为lock.exe, 其用法为:lock +|- <被处理的文件名>,其中“+”为加密,“-”为解密。/*案例代码文件名:AL9_13.C*//*程序功能:带参主函数的应用示例*/main(int argc, char *argv[]){ char c;if (argc != 3) printf("参数个数不对!\n");else{ c=*argv[1]; /*截取第二个实参字符串的第一个字符*/switch(c){ case '+': /*执行加密*/{ /*加密程序段*/printf("执行加密程序段。\n");}break;case '-': /*执行解密*/{ /*解密程序段*/printf("执行解密程序段。\n");}break;default: printf("第二个参数错误!\n");}}}1.主函数main()的有参形式main(int argc, char *argv[]){ … …}2.实参的来源运行带形参的主函数,必须在操作系统状态下,输入主函数所在的可执行文件名,以及所需的实参,然后回车即可。命令行的一般格式为:可执行文件名?实参[ 实参2……]例如,本案例程序的用法:lock +|- <被处理的文件名>←┘●在TC的集成环境下,也可直接利用Options | Arguments 项,输入主函数所需要的实参:只须输入各参数(相邻参数间用空格分开),可执行文件名可省略。就本案例而言,输入“+|- <被处理的文件名>”即可。3.形参说明(1)形参argc是命令行中参数的个数(可执行文件名本身也算一个)。在本案例中,形参argc的值为3(lock、+|-、文件名)。(2)形参argv是一个字符指针数组,即形参argv首先是一个数组(元素个数为形参argc的值),其元素值都是指向实参字符串的指针。在本案例中,元素argv[0]指向第1个实参字符串“lock”,元素argv[1] 指向第2个实参字符串“+|-”,元素argv[2]指向第3个实参字符串“被处理的文件名”。10.6.3 指向指针的指针变量简介在[案例9.12]的主函数main()中,数组name是一个字符指针数组,即数组的每一个元素都是一个指向字符串的指针。既然name是一个数组,则它的每一个元素也同样有相应的地址,因此可以设置一个指针变量pointer,使其指向指针数组的元素(元素的值还是一个指针),称pointer为指向指针的指针变量。显然,指向指针的指针变量是一个两级的指针变量。1.指向指针的指针变量的定义数据类型 **指针变量[, **指针变量2……];2.指向指针的指针变量的赋值指针变量 = 指针数组名 + i[Return]10.7 函数的指针和指向函数的指针变量简介1.函数指针的概念 一个函数在编译时,被分配了一个入口地址,这个地址就称为该函数的指针。可以用一个指针变量指向一个函数,然后通过该指针变量调用此函数。 2.指向函数的指针变量 (1)定义格式函数类型 (*指针变量)( ); 注意:“*指针变量”外的括号不能缺,否则成了返回指针值的函数。 例如,int (*fp)(); /* fp为指向int函数的指针变量*/ (2)赋值 函数名代表该函数的入口地址。因此,可用函数名给指向函数的指针变量赋值。 指向函数的指针变量=[&]函数名; 注意:函数名后不能带括号和参数;函数名前的“&”符号是可选的。(3)调用格式(*函数指针变量)([实参表])3.指向函数的指针变量作函数参数指向函数的指针变量的常用用途之一,就是将函数指针作参数,传递到其它函数。函数名作实参时,因为要缺省括号和参数,造成编译器无法判断它是一个变量还是一个函数,所以必须加以说明。函数说明的格式,与第7章中介绍的一样。注意:对指向函数的指针变量,诸如p+i、p++/p--等运算是没有意义的。[Return](共101张PPT)if + goto 语句构成循环while 循环控制语句do … while 循环控制语句for 循环控制语句循环嵌套break 和 continue 语句第六章 循环结构程序设计1 概 述循环概述重复执行某部分代码,就构成循环结构。有限循环:在某个条件满足时重复执行,不满足时结束无限循环:无条件的重复执行或循环结束条件永远不满足循环概述循环结构的组成要素:循环初始化部分循环变量或循环计数器循环继续条件(循环终止条件)循环体(重复执行的代码)循环概述C语言中有4种循环实现方式:if + goto 语句while 语句do … while 语句for 语句注意:后3种是专门的循环控制语句重点:掌握每种循环语句的执行过程。2 goto语句及用goto语句构成循环goto 语句if+goto 语句构成循环goto语句goto语句:无条件跳转语句。用法:goto 语句标号;功能:程序流程直接转到语句标号所在的地方执行语句标号:是一个标识符,命名的规则同变量的命名规则。Here :……goto Here;……goto There;……There :……goto语句goto可以往上跳转。goto可以往下跳转。语句标号出现的方式是:在其后面必须跟上一个冒号。goto语句注意:goto 语句应该慎用,因为它不判断条件而直接跳转,容易破坏程序的结构化。主要用于:① 和if语句构成循环;② 从循环体内跳到循环体外。算法和思路:为什么可以用循环?因为数据有规律的递增且都是乘法运算。循环的是什么?每次产生一个新数据并把它乘到上次的结果中。什么时候结束循环?当新产生的数据大于10的时候就停止上述循环过程。用if 语句和goto语句构成循环:例6-1例:编程计算1×2×3×…×n ,n=10算法和思路:怎样实现循环控制?goto 语句可以无条件跳转,用于跳到循环开始的地方;if 语句是条件分支语句,用于判断循环结束条件是否满足。如果满足,则循环结束并输出计算结果;如果不满足,则利用goto 语句返回继续循环。用if 语句和goto语句构成循环:例6-1例:编程计算1×2×3×…×n ,n=10#include void main( ){long int prod=1; int n=1;Loop :if(n<=10){prod=prod*n; n++;goto Loop;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}用if 语句和goto语句构成循环:例6-1循环变量n 初值=1变化步长=1终值=10#include void main( ){long int prod=1; int n=1;Loop :if(n<=10){prod=prod*n; n++;goto Loop;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}用if 语句和goto语句构成循环:例6-1循环初始化#include void main( ){long int prod=1; int n=1;Loop :if(n<=10){prod=prod*n; n++;goto Loop;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}用if 语句和goto语句构成循环:例6-1循环继续条件:n<=10即循环终止条件: n>10#include void main( ){long int prod=1; int n=1;Loop :if(n<=10){prod=prod*n; n++;goto Loop;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}用if 语句和goto语句构成循环:例6-1循环体:大括弧中的语句。其中的 n++使循环终止条件越来越接近满足,也就是说循环最终会结束。#include void main( ){long int prod=1; int n=1;Loop :if(n<=10){prod=prod*n; n++;goto Loop;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}用if 语句和goto语句构成循环:例6-1由if判断条件,goto直接跳转,使得一部分指定的操作被重复执行了。这就是循环。#include void main( ){long int prod=1; int n=1;Loop :if(n<=10){prod=prod*n; n++;goto Loop;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}用if 语句和goto语句构成循环:例6-1循环结束后:循环变量的值是多少?用if 语句和goto语句构成循环:例6-1prod=prod*nTFn<=10prod=1; n=1n++循环初始化循环继续条件循环体循环控制变量:自增无条件跳转#include void main( ){long int prod=1; int n=1;Loop :if(n>10)printf(“1*2*…*%d=%ld\n”, n–1, prod);else{prod=prod*n; n++; goto Loop;}return;}用if 语句和goto语句构成循环:例6-1也可以用循环终止条件来控制。3 while 语句while 语句(当型循环)语法: while (表达式) 循环体语句;执行过程:首先求解while后面表达式的逻辑值。若值为“T”,则执行内嵌的循环体语句,然后返回再次求解表达式;若值为“F”,则while语句结束,接着执行后面的语句。语句TF表达式while 语句(当型循环)①当循环体包含多于一个语句时,必须用大括号把它们括起来构成复合语句。②while(表达式) 后面没有分号。③如果第一次求解表达式,其值就为“F”,则循环体不被执行。由while语句的执行过程可见,在while循环控制语句中就隐含了用if 判断条件,用 goto直接跳转的循环流程。While语句的注意事项:while 循环控制语句:例例:编程计算1×2×3×…×n ,n=10刚才是用 if+goto 语句实现的,现在使用专门的循环控制语句while来实现。既然是专门的循环控制语句,就不需要使用if或者goto语句,因为它们内部已经包含了这样的控制过程。while 循环控制语句:例#include void main( ){long int prod=1; int n=1;while(n<=10){prod=prod*n; n++;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}用while语句 构成的循环while 循环控制语句:例#include void main( ){long int prod=1; int n=1;while(n<=10){prod=prod*n; n++;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}注意:这后面没有分号while 循环控制语句:例#include void main( ){long int prod=1; int n=1;while(n<=10){prod=prod*n; n++;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}注意:将内嵌的循环体语句用大括号括起来形成一个复合语句。◆ 1. 下面程序执行的结果是( )。void main( ){int n = 1;while(n = 1) n --;printf(“n=%d\n”, n);}(A) n = 0 (B) while构成无限循环(C) n = 1 (D) n = -1课堂练习:while 语句答案: (B)◆ 2. 下面程序执行的结果是( )。void main( ){int n = 1;while(n == 1) n --;printf(“n=%d\n”, n);}(A) n = 0 (B) while构成无限循环(C) n = 1 (D) n = -1课堂练习:while 语句答案: (A)◆ 3. 下面程序执行的结果是( )。void main( ){int n = 1;while(n == 1) ;printf(“n=%d\n”, n);}(A) n = 0 (B) while构成无限循环(C) n = 1 (D) n = -1课堂练习:while 语句答案: (B)◆ 4. 下面程序执行的结果是( )。void main( ){int n = 1;while(n = n - 1) n --;printf(“n=%d\n”, n);}(A) n = 0 (B) while构成无限循环(C) n = 1 (D) n = -1课堂练习:while 语句答案: (A)4 do…while 语句do…while 语句语法: do { 循环体语句 }while(表达式) ;执行过程:首先执行do后的循环体语句,然后求解while后面表达式的逻辑值。若值为“T”,则返回继续执行内嵌的循环体语句;若值为“F”,则do…while语句结束,接着执行其后的语句。语句TF表达式do…while 语句①当循环体语句多于一个时,必须用大括号把它们括起来构成复合语句。②while(表达式) 后面有分号。③不论第一次求解表达式的值是什么,循环体语句至少被执行一次。由do…while语句的执行过程可见,在do…while循环控制语句中也隐含了用if判断条件,用goto直接跳转的循环流程。do…While语句的注意事项:do… while 循环控制语句:例例:编程计算1×2×3×…×n ,n=10现在使用专门的循环控制语句do….while来实现。既然是专门的循环控制语句,就不需要使用if或者goto语句,因为它们内部已经包含了这样的控制过程。do… while 循环控制语句:例#include void main( ){long int prod=1; int n=1;do{prod=prod*n; n++;}while(n<=10);printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}用do…while语句构成的循环do… while 循环控制语句:例#include void main( ){long int prod=1; int n=1;do{prod=prod*n; n++;}while(n<=10) ;printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}注意:这个while后面有分号do… while 循环控制语句:例#include void main( ){long int prod=1; int n=1;do{prod=prod*n; n++;}while(n<=10);printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}注意:将内嵌的循环体语句用大括号括起来形成一个复合语句。while 语句和do…while 语句的对比do … while 语句的流程图while 语句的流程图不论第一次求解表达式的值是什么,循环体语句至少被执行一次。如果第一次求解表达式,其值就为“F”,则循环体一次都不被执行。语句TF表达式语句TF表达式while 语句和do…while 语句的对比一般情况下,同一个问题用while语句和do…while语句实现,其循环体都是一样的。但是,有可能循环体被执行的次数是不同的。while和do…while循环控制语句比较适用于循环次数不太明确,但是循环的终止条件(或循环继续条件)比较明确的问题中。5 for 语句for 语句语法: for(表达式1; 表达式2; 表达式3){ 循环体语句 }通常:表达式1 用于循环初始化;表达式2 用于表示循环继续条件;表达式3 用于循环变量的自增或自减,从而使循环继续条件越来越不满足。for 语句执行过程:① 首先求解表达式1的具体值(不是求逻辑值);② 再求解表达式2的逻辑值。若值为“T”,则执行内嵌的循环体语句;若值为“F”,则循环结束,执行for语句下面的语句;③ 求解表达式3的具体值;④ 返回第②步循环体语句TF表达式2求解表达式1求解表达式3for 循环控制语句:例例:编程计算1×2×3×…×n ,n=10#include void main(){long int prod=1; int n;for(n=1; n<=10; n++){prod=prod*n;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}注意:将内嵌的循环体语句用大括号括起来形成一个复合语句。for 语句的灵活使用由于for 语句语法组成部分多语法: for(表达式1; 表达式2; 表达式3){ 循环体语句 }而C语言运算符丰富,所以C语言中可以构成的表达式也就可能很丰富和很复杂,比如赋值表达式、逗号表达式、条件表达式等等。只要是合法的C表达式,就可以出现在for语句的任一部分中,所以for语句使用上非常灵活,功能也最强大。for 语句的灵活使用#include void main(){long int prod=1;int n;for(n=1; n<=10; n++){prod=prod*n;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}3个部分的表达式都在for 语句的灵活使用#include void main(){long int prod=1;int n;for(n=1; n<=10; n++){prod=prod*n;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}表达式1可以省略,把循环初始化的工作放在for语句之前。long int prod=1;int n=1;for( ; n<=10; n++){prod=prod*n;}for 语句的灵活使用#include void main(){long int prod=1;int n;for(n=1; n<=10; n++){prod=prod*n;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}表达式2也可以省略。当然,如果表达式2也省略,则成了无限循环。显然这是不允许的。long int prod=1;int n;for(n=1; ; n++){prod=prod*n;}for 语句的灵活使用#include void main(){long int prod=1;int n;for(n=1; n<=10; n++){prod=prod*n;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}表达式3也可以省略,把它放回到循环体中,作最后一个语句中。long int prod=1;int n;for(n=1; n<=10; ){prod=prod*n; n++;}for 语句的灵活使用#include void main(){long int prod=1;int n;for(n=1; n<=10; n++){prod=prod*n;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}循环体中的部分表达式语句甚至也可以放到表达式3中。只要保证流程正确即可。for(n=1; n<=10; prod=prod*n, n++;){}逗号表达式for 语句for 循环语句常用于循环次数(循环变量的初值和终值)比较明确的问题中。while 和 do…while 循环控制语句比较适用于循环次数不太明确,但是循环终止条件(或循环继续条件)比较明确的问题中。实际上,四种循环实现方式是相通的,即同一个问题,可以用任何一种方式去实现。只不过,对不同的问题用某一种方式实现起来算法比较有条理,代码比较简洁紧凑而已。for 语句:例6-4例:课本p120 习题6.3编程求 Sn=a + aa + aaa +…+ a…a(n个a)a是一个0~9的整数,n是一个整数由键盘输入。思路:肯定是用循环来解决。为什么?因为序列中每一个数据的产生看上去都很有规律,且都是加法运算(即累加)。如何寻找每一个数据产生的规律?①考察某个数据和前一个数据的关系;②或者考察某个数据与它在整个数据序列中所处位置的关系。for 语句:例6-4编程求 Sn=a + aa + aaa +…+ a…a(n个a)a是一个0~9的整数,n是一个整数由键盘输入。考察数据产生的规律:①考察某个数据和前一个数据的关系;假设求Sn=2 + 22 + 222 +…+ 2…2(n个2)那么有如下规律:第一个数:t=2(就是变量a)第二个数:t=22=2×10+2=t×10+a第三个数:t=222=22×10+2=t×10+a第四个数:t=2222=222×10+2=t×10+a……for 语句:例6-4#include void main(){int a = 2, n = 0, i;long int sum = 0, temp = 0;printf(“Please input a integer: ”);scanf(“%d”, &n);for (i = 1; i <= n; i ++){temp = temp * 10 + a;sum = sum + temp;}printf(“Sn = %ld\n”, sum);}每个数据 产生的规律累加6 循环嵌套循环嵌套在一个循环控制语句的循环体内包含另一个循环控制语句称为“循环嵌套”。嵌套的层数不同的系统规定不同。一般来说,实际中最多就用到4层嵌套。最常用的是双重循环。while(){…while(){ … }…}while(){…do{ … }while();…}while(){…for( ; ; ){ … }…}循环嵌套循环嵌套do{…do{ … }while();…} while();do{…while(){ … }…} while();do{…for ( ; ; ){ … }…} while();循环嵌套for( ; ; ){…do{ … }while();…}for( ; ; ){…while(){ … }…}for( ; ; ){…for ( ; ; ){ … }…}循环嵌套:例6-5例:编程求∑n! = 1! + 2! + 3! + … + n!其中,n是一个整数,由键盘输入。思路:∑n! = 1! + 2! + 3! + … + n!= 1 += 1×2 += 1×2×3 + …= 1×2×3×…×n用两层循环内层循环求累乘,外层循环求累加#include void main( ){int n=10, i, j;long int sum=0, prod=1;for(i=1; i<=n; i++){prod=1;for(j=1; j<=i; j++)prod=prod*j;sum=sum+prod;}printf(“1!+2!+…+%d!=%ld\n”, n, sum);}循环嵌套:例6-5外层循环变量把每次由内层循环计算出的阶乘值累加到变量sum中。内层循环用于求外层循环变量 i当前值的阶乘。#include void main( ){int n=10, i, j;long int sum=0, prod=1;for(i=1; i<=n; i++){prod=1;for(j=1; j<=i; j++)prod=prod*j;sum=sum+prod;}printf(“1!+2!+…+%d!=%ld\n”, n, sum);}循环嵌套:例6-5实际上,这个问题用一层循环就可以解决。就是利用关系式:n! = (n-1)! * n#include void main( ){int n = 10, i;long int sum = 0, prod = 1;for(i = 1; i <= n; i ++){prod = prod * i;sum = sum + prod;}printf(“1!+2!+…+%d!=%ld\n”, n, sum);}循环嵌套:例6-5用一层循环解决:利用关系式 n! = (n-1)! * n上一个阶乘值再乘上当前的循环变量就等于新的阶乘值8 break语句和continue语句break 语句语法:break;用法:用在一个循环控制语句的循环体内。作用:结束整个循环语句,接着执行该语句后面的语句。break 语句break语句只能用在switch语句和三种专门的循环控制即while、do…while和for语句中。用在switch语句中作用是结束它所在的switch语句的执行,接着执行switch后面的语句。用在循环体中作用是结束它所在的循环语句的执行,接着执行该循环语句后面的语句。break语句的注意事项:continue 语句语法:continue;用法:用在一个循环控制语句的循环体内。作用:结束本次循环体的执行,即当遇到continue 语句时,就跳过该循环体中位于continue 后面的语句,接着进行下一次是否继续循环的判断。continue 语句也只能用在三种专门的循环控制语句即while、do…while和for语句中。continue语句的注意事项:break 语句和continue 语句的区别break语句: 结束整个循环语句while(表达式1){…if(表达式2)break;…}表达式1…TF表达式2…while后的语句TFbreakbreak 语句和continue 语句的区别continue语句: 结束本次循环的执行while(表达式1){…if(表达式2)continue;…}F表达式1…TF表达式2…while后的语句Tcontinuebreak 语句和continue 语句的区别F表达式1…TF表达式2…while后的语句Tcontinue表达式1…TF表达式2…while后的语句TFbreakbreak 语句:例6-6例:编程计算1×2×3×…×n ,n=10#include void main(){long int prod=1;int n=1;while (1){prod=prod*n; n++;if (n>10) break;}printf(“1*2*…*%d=%ld\n”, n–1, prod);return;}用常量1作判断表达式,好像形成了一个无限循环。当循环终止条件满足时,用break语句结束循环。continue 语句:例6-7例:编程计算1~100之间所有偶数的和分析:实际上就是计算 2+4+6+…+100 的和。这和计算 1×2×3×…×n 的算法是一样的。即要用循环来解决。让循环控制变量初值为2,终值为100,自增步长为2,就可以产生序列中的每一个数,然后再用一个变量来保存累加的结果即可。#include void main(){int sum=0, n=2;while(n<=100){sum=sum+n;n=n+2;}printf(“2+4+…+100=%d\n”, sum);return;}continue 语句:例6-7用while循环控制语句实现:循环继续条件循环控制变量变化步长=2#include void main(){int sum=0, n=2;do{sum=sum+n;n=n+2;} while(n<=100);printf(“2+4+…+100=%d\n”, sum);return;}continue 语句:例6-7用do…while循环控制语句实现:循环继续条件循环变量变化步长=2#include void main(){int sum=0, n=2;for( ; n<=100; ){sum=sum+n;n=n+2;}printf(“2+4+…+100=%d\n”, sum);return;}continue 语句:例6-7用for循环控制语句实现:循环继续条件循环变量变化步长=2continue 语句:例6-7例:编程计算1~100之间所有偶数的和分析:现在用continue语句来解决这个问题。此时,让循环控制变量初值为1,终值为100,自增步长为+1,这样实际产生的序列是1、2、3、…、100,而我们只需要其中的偶数,所以在循环体中用if语句判断当前所产生的数是否是偶数,如果是,就累加;如果不是,就不累加并开始产生下一个数据(实际上就开始下一次循环)。#include void main(){int sum=0, n=2;for( ; n<=100; n++){if(n%2 != 0) continue;sum=sum+n;}printf(“2+4+…+100=%d\n”, sum);return;}continue 语句:例6-7用for循环控制语句和continue语句实现:如果不是偶数,就开始下一次循环循环变量变化步长=19 循环结构程序举例程序举例6-8 :题目及分析例:课本p116 例6.6用π/4 = 1 – 1/3 + 1/5 – 1/7 + …公式求π的近似值,直到最后一项的绝对值小于1e-6为止。分析:题目需要计算累加,应该用到循环语句。用那种循环语句来控制呢?根据题目的要求,要累计到最后一项数据的绝对值小于某个值的时候结束。也就是说,我们无法知道要累计多少个数据,只知道什么时候结束累计,因此应该用 while 或do…while 比较合适。程序举例6-8 :题目及分析例:课本p116 例6.6用π/4 = 1 – 1/3 + 1/5 – 1/7 + …公式求π的近似值,直到最后一项的绝对值小于1e-6为止。分析:如何解决累加的数据符号在交替变化的问题?可以设置一个变量,专门用于保存数据的符号,根据循环次数的不同,依次在正负之间转换。如何求一个数据的绝对值?可以调用数学库函数,或自己编写代码求绝对值。程序举例6-8 :实现代码#include #include void main(){float t=1, pi=0.0; int n=1, sign=1;while(fabs(t)>1e-6){pi=pi+t; n=n+2;sign = - sign;t=sign/n;}pi= pi*4;printf("pi=%10.6f\n", pi);}因为用到数学库函数,所以要包含相应的头文件用于保存 符号的变量fabs是用于求实型数据绝对值的函数程序举例6-8 :实现代码#include #include void main(){float t=1, pi=0.0; int n=1, sign=1;while(fabs(t)>1e-6){pi=pi+t; n=n+2;sign = - sign;t=sign/n;}pi= pi*4;printf("pi=%10.6f\n", pi);}每循环一次,符号就变化一次求出下一个要累计的数据例:课本p116 例6.7求 Fibonacci 数列的前40个数。该数列满足递推公式f1= 1 , f2 = 1 ; fn = fn-1 + fn-2程序举例6-9 :题目及分析分析:题目需要计算累加,应该用到循环语句。用那种循环语句来控制呢?根据题目考虑用for语句比较合适。程序举例6-9 :题目及分析分析:怎么循环?我们把前40个数的递推过程写一下:f1=1f2=1f3=f2+f1f4=f3+f2f5=f4+f3……可见,在已知f1和f2的时候,从第3个数据开始,每个数据都只与前面相邻的两个数据有关。比如:当根据 f1+f2=f3 求出 f3 后,再求 f4 时,原来 f1 变量中的数据不再有用,所以如果把 f2 的值放到 f1 中,把 f3 的值放到 f2 中,那么计算 f4 的表达式 f3+f2 就变成了 f2+f1 ,结果又可以放到 f3 中,依此类推就形成了循环。程序举例6-9 :算法f3 = f2 + f1f3 =f2f1(f3)(f2)+f4 = f3 + f2(f4)f3 =f2f1+f5 = f4 + f3(f4)(f3)(f5)第1次循环:第2次循环:第3次循环:算法:每循环一次求出一个新的数据程序举例6-9 :实现代码/* 算法:循环一次输出一个数据 */#include void main(){long int f1 = 1, f2 = 1, f3; int n;printf(“%12d%12d”, f1, f2);for (n = 3; n <= 40; n++){f3 = f1 + f2 ; printf(“%12d”, f3);if (n % 5 == 0) printf(“\n”);f1 = f2; f2 = f3;}}每次循环求出一个新的数据并输出程序举例6-9 :实现代码/* 算法:循环一次输出一个数据 */#include void main(){long int f1 = 1, f2 = 1, f3; int n;printf(“%12d%12d”, f1, f2);for (n = 3; n <= 40; n++){f3 = f1 + f2 ; printf(“%12d”, f3);if (n % 5 == 0) printf(“\n”);f1 = f2; f2 = f3;}}使每行只输出显示5个数据程序举例6-9 :实现代码/* 算法:循环一次输出一个数据 */#include void main(){long int f1 = 1, f2 = 1, f3; int n;printf(“%12d%12d”, f1, f2);for (n = 3; n <= 40; n++){f3 = f1 + f2 ; printf(“%12d”, f3);if (n % 5 == 0) printf(“\n”);f1 = f2; f2 = f3;}}实现循环递推算法的关键程序举例6-10 :题目及分析例:课本p116 例6.8判断一个整数是否是素数。分析:素数是指除了1和它本身外,不能被任何数整除的自然数。如何判断呢?根据素数的性质,对一个给定的整数 num,用它去除以从2到 num-1 之间的所有整数。在这个检验过程中,只要有一个能整除,就说明num不是素数;只有到最后检验结束,都不能整除,才说明num是素数。程序举例6-10 :题目及分析例:课本p116 例6.8判断一个整数是否是素数。分析:根据上述分析可见,需要使用循环控制语句产生 2~num-1 之间的整数,依次去除 num。循环体中判断能否整除,并在发现第一个能整除的情况下提前结束循环(即是说,没有必要继续往下检验能否整除了),否则循环会正常结束。如何判断一个循环是提前结束还是正常结束的呢?检查循环变量在循环结束的当前值。#include void main(){int num , n;printf(“Please input a integer: ”);scanf(“%d”, &num);for (n = 2; n <= num - 1; n ++)if (num % n == 0) break;if ( n >= num )printf(“%d 是素数.\n” , num);elseprintf(“%d 不是素数.\n” , num);}程序举例6-10 :实现方案一循环变量n终值是num-1,步长是1,因此如果循环正常结束,那么n的值应该等于num 。#include void main(){int num , n;printf(“Please input a integer: ”);scanf(“%d”, &num);for (n = 2; n <= num - 1; n ++)if (num % n == 0) break;if ( n >= num )printf(“%d 是素数.\n” , num);elseprintf(“%d 不是素数.\n” , num);}程序举例6-10 :实现方案一只要找到第一个能被整除的,就结束循环。#include void main(){int num , n;printf(“Please input a integer: ”);scanf(“%d”, &num);for (n = 2; n <= num - 1; n ++)if (num % n == 0) break;if ( n >= num )printf(“%d 是素数.\n” , num);elseprintf(“%d 不是素数.\n” , num);}程序举例6-10 :实现方案一根据循环结束后,循环变量的当前值判断循环是否是正常结束的#include #define TRUE 1#define FALSE 0void main(){int num, n, bIsPrime = TRUE;printf(“Please input a integer: ”);scanf(“%d”, &num);for (n = 2; n <= num - 1; n ++)if (num % n == 0){ bIsPrime = FALSE; break; }if (bIsPrime) printf(“%d 是素数.\n” , num);else printf(“%d 不是素数.\n” , num);}程序举例6-10 :实现方案二定义两个含义清楚的符号变量,代表逻辑值“T”和“F”。#include #define TRUE 1#define FALSE 0void main(){int num, n, bIsPrime = TRUE;printf(“Please input a integer: ”);scanf(“%d”, &num);for (n = 2; n <= num - 1; n ++)if (num % n == 0){ bIsPrime = FALSE; break; }if (bIsPrime) printf(“%d 是素数.\n” , num);else printf(“%d 不是素数.\n” , num);}程序举例6-10 :实现方案二定义一个标志变量保存是否素数的判断结果。此处初始化为“T”。#include #define TRUE 1#define FALSE 0void main(){int num, n, bIsPrime = TRUE;printf(“Please input a integer: ”);scanf(“%d”, &num);for (n = 2; n <= num - 1; n ++)if (num % n == 0){ bIsPrime = FALSE; break; }if (bIsPrime) printf(“%d 是素数.\n” , num);else printf(“%d 不是素数.\n” , num);}程序举例6-10 :实现方案二若发现第一个能被整除的,即将标志变量设为“F”,同时结束循环。#include #define TRUE 1#define FALSE 0void main(){int num, n, bIsPrime = TRUE;printf(“Please input a integer: ”);scanf(“%d”, &num);for (n = 2; n <= num - 1; n ++)if (num % n == 0){ bIsPrime = FALSE; break; }if (bIsPrime) printf(“%d 是素数.\n” , num);else printf(“%d 不是素数.\n” , num);}程序举例6-10 :实现方案二最后,根据标志变量的值,很容易得出判断的结果。程序举例6-11 :题目及分析例:一次执行程序,可以判断多个整数是否是素数。分析:当用户根据提示信息输入一个整数后,程序接收它并判断给出结果,一个数据的判断过程就结束了。此时,若再次给出提示信息,要求用户继续输入数据,就可以实现判断若干个数据的目的。因此需要把判断素数的全部代码在放到一个循环体中。该循环的结束是由用户的输入决定的。即当用户输入一个整数0时表示他想结束程序的运行,输入其它数据时表示他想判断是否是素数。程序举例6-11 :实现方案#include #define TRUE 1#define FALSE 0void main(){int num , n, bIsPrime = TRUE;while(TRUE){printf(“Please input a integer”);printf(“(0 to quit) : ”);scanf(“%d”, &num);if (num == 0) break;…… /* 判断素数的代码放在这儿*/}}这个break是结束while循环的◆ 1. 下面程序执行的结果是( )。循环For(i=1,j=6;++i!=j--;)s=i+j;将被执行( )。(A) 2次 (B) 3次(C) 4次 (D) 无限循环课堂练习:答案: (A)◆2. 小强今年12岁,父亲大他20岁。问过多少年父亲年龄是小强年龄的2倍,那时两人分别是多少岁?#include void main( ){int son = 12, dad = son + 20 , year = 0 ;while ( dad != son * 2 ){dad ++ ; son ++ ; year ++ ;}printf(“%d years past\n”, year);printf(“son\’s age = %d\n”, son);printf(“dad\’s age = %d\n”, dad);}课堂练习:循环次数不太明确而循环终止条件明确的情况。课后及上机作业1. 课本p119习题 4.4,4.9,4.13,4.16,4.18,4.21,4.37,4.41编程求出1~1000之间的所有同构数并输出。要求每行输出4个数据。(共23张PPT)第13章 文件13.1 C 文 件 概 述文件:存储在外部介质上数据的集合。操作系统以文件为单位对数据进行管理。ASCII文件(文本文件):每一字节放一ASCII代码。二进制文件:按数据在内存中的形式原样输出到磁盘。文件C文件是一个字节流或二进制流,对文件的存取是以字符(字节)为单位。数据流的开始和结束仅受程序控制而不受物理符号控制。即C文件不是由记录组成的。程序数据区输出文件缓冲区输入文件缓冲区磁盘输出输入输出输入非缓冲文件系统:由程序为每个文件设定缓冲区。缓冲文件系统:系统自动为文件开辟一个缓冲区。13.2 文 件 类 型 指 针在stdio.h文件中/或由系统定义一个结构体类型(名为 FILE )的变量来存放文件信息(文件名、文件状态、文件当前位置等。typedef struct{ int -fd; /* 文件号 */int -cleft; /* 缓冲区中剩下的字符 */int -mode; /* 文件操作模式 */char * -nextc; /* 下一个字符位置 */char * -buff; /* 文件缓冲区位置 */}FILE;因此,可以定义若干FILE类型的变量来存放文件信息。如:FILE -efile [-MAXFILE]; /* 定义一个结构体数组 */FILE *fp; /* 定义文件型指针变量 */13.3 文件的打开与关闭13.3.1 文件的打开(fopen函数)调用方式为: FILE *fp;fp=fopen(文件名,使用文件的方式);13.3.1 文件的关闭(fclose函数)调用方式为: fclose(文件指针);可用error函数来测试fclose函数的返回值:返回值=0,顺利关闭;返回值=非0,关闭时出错。13.4 文 件 的 读 写13.4.1 fputc 函数和 fgetc 函数1. fputc 函数 将一个字符写到磁盘文件上去。一般形式为:fputc(ch,fp);ch:要输出的字符,可以是一个字符常量或字符变量fp:文件指针变量,从fopen函数得到返回值。输出成功,fputc函数返回值为输出的字符;输出失败,返回一个EOF(stdio.h中定义的符号常量,值为-1)。2. fgetc 函数 从一个以读或读写方式打开的文件读入一个字符。一般形式为: ch=fgetc(fp);ch: 字符变量fp:文件指针变量若读字符时遇到文件结束符,函数返回一个结束标志EOF。EOF不是可输出字符,因此不能在屏幕上显示。从一个磁盘文件顺序读入字符并显示:ch=fgetc(fp);while(ch!=EOF){ putchar(ch);ch=fgetc(fp);}顺序读入一个二进制文件中的数据:while(!feof(fp)){ c=fgetc(fp);}3. fputc 和 fgetc 函数使用举例[例1]:将键盘输入的字符送到磁盘#include "stdio.h"main(){ FILE *fp;char ch,filename[10];scanf("%s",filename);if ((fp=fopen(filename,"w"))==NULL){ printf("cannot open file\n");exit(0);}ch=getchar();while (ch!='#'){ fputc(ch,fp);putchar(ch);ch=getchar();}fclose(fp);printf("\n");}从键盘输入一些字符,逐个将其送到磁盘上,直到输入一个“#”为止。[例2]:文件内容复制#include "stdio.h"main(){ FILE *in,*out;char ch,infile[10],outfile[10];printf("Enter the infile name:\n");scanf("%s",infile);printf("Enter the outfile name:\n");scanf("%s",outfile);if ((in=fopen(infile,"r"))==NULL){printf("cannot open infile\n"); exit(0); }if ((out=fopen(outfile,"w"))==NULL){printf("cannot open outfile\n"); exit(0); }while (!feof(in))fputc(fgetc(in),out);fclose(in);fclose(out);}将一个磁盘文件中的信息复制到另一个磁盘文件中。使用带参数的main函数进行文件内容复制#include "stdio.h"main(argc,argv)int argc;char *argv[];{ FILE *in,*out;char ch;if (argc!=3){printf("You forgot to enter a filename\n"); exit(0); }if((in=fopen(argv[1],"r"))==NULL){printf("cannot open infile\n"); exit(0); }if((out=fopen(argv[2],"w"))==NULL){printf("cannot open outfile\n"); exit(0); }while(!feof(in)) fputc(fgetc(in),out);fclose(in);fclose(out);}在输入命令行时把两个文件名一起输入。这时要用到main函数的参数。13.4.2 fread 函数 和 fwrite 函数fread和fwrite函数用于读写一个数据块(如一个实数,一个结构体变量的值等)。一般调用形式为:fread(buffer,size,count,fp);fwrite(buffer,size,count,fp);buffer:是一个指针,指向读入/输出数据的起始地址;size:要读写的字节数;count:要进行读写多少个size字节的数据项;fp:文件型指针。如:fread(f,4,2,fp);表示从fp所指向的文件读入2次(每次4个字节)数据,存储到数组f中。[例3]:键盘输入转存到磁盘文件main(){int i;for (i=0;iscanf("%s%d%d%s",stud[i].name,&stud[i].num,&stud[i].age,stud[i].addr);save();}从键盘输入4个学生的有关数据,然后把它们转存到磁盘文件上。#include "stdio.h"#define SIZE 4struct student_type{ char name[10];int num;int age;char addr[15];}stud[SIZE];void save(){ FILE *fp;int i;if ((fp=fopen("stu_list","wb"))==NULL){printf("cannot open file\n"); return; }for (i=0;iif (fwrite(&stud[i],sizeof(struct student_type),1,fp)!=1)printf("file write error\n");}[例3a]:验证[例13.3]的正确性main(){ int i;FILE *fp;fp=fopen("stu_list","rb");for (i=0;i{fread(&stud[i],sizeof(struct student_type),1,fp);printf("%-10s%4d%4d %- 15s\n",stud[i].name,stud[i].num,stud[i].age,stud[i].addr);}}#include "stdio.h"#define SIZE 4struct student_type{ char name[10];int num;int age;char addr[15];}stud[SIZE];下列程序从“stu_list”文件读入数据然后输出到屏幕。[例3b]:读入二进制文件转存#include "stdio.h"#define SIZE 4struct student_type{ char name[10];int num;int age;char addr[15];}stud[SIZE];若上题不是从键盘输入数据,而是数据已经以二进制形式存储在一个磁盘文件“stu_dat”中,要求从其中读入数据并输出到“stu_list”文件中,可以编写一个load函数,从磁盘文件中读二进制数据。void save(){ FILE *fp;int i;if ((fp=fopen("stu_list","wb"))==NULL){ printf("cannot open file\n");return;}for (i=0;iif (fwrite(&stud[i],sizeof(struct student_type),1,fp)!=1)printf("file write error\n");}void load(){ FILE *fp;int i;if ((fp=fopen("stu_dat","rb"))==NULL){printf("cannot open file\n"); return; }for (i=0;iif (fread(&stud[i],sizeof(struct student_type),1,fp)!=1){ if (feof(fp)) return;printf("file read error\n");}}main(){load();save();}13.4.3 fprintf 函数和 fscanf 函数fprintf、fscanf的读写对象是磁盘文件而不是终端。调用方式为:fprintf(文件指针,格式字符串,输出表列);fscanf(文件指针,格式字符串,输出表列);如:fprintf(fp,“%d,%6.2f”,i,t);表示将整型变量i和实型变量t的值分别按格式%d和%6.2f输出到fp指向的文件。13.4.4 其它读写函数1. putw 和 getw 函数:用于读写磁盘文件的一个字(整数)。如:putw(10,fp); /* 将整数10输出到fp指向的文件 */i=getw(fp); /* 从磁盘文件读入一个整数到内存,赋给i。若系统未提供上述函数,其自定义方法详见教材P280~281。2. fgets 和 fputs 函数:用于读写一个字符串。fgets(str,n,fp);/* 从指向的文件输入n-1 个字符放到字符数组str中,函数返回值为str的首地址。*/fputs(“China”,fp);/* 将字符串输出到fp指向的文件。输出成功,函数值为0;输出失败,函数值非0。第一个参数可以是字符串常量、字符数组名、字符型指针。*/13.5 文 件 的 定 位13.5.1 rewind 函数:使位置指针返回文件开头。无返回值。[例4]:rewind函数的应用#include "stdio.h"main(){ FILE *fp1,*fp2;fp1=fopen("file1.c","r");fp2=fopen("file2.c","w");while (!feof(fp1)) putchar(getc(fp1));rewind(fp1);while(!feof(fp1)) putc(getc(fp1),fp2);fclose(fp1);fclose(fp2);}有一个磁盘文件,第一次将它显示在屏幕上,第二次将它复制到另一个文件。13.5.2 fseek 函数和随机读写对文件进行顺序读写和随机读写靠位置指针的移动来实现。fseek函数改变文件位置指针的调用形式为:fseek(文件类型指针,位移量,起始点)位移量是以起始点为基准的字节数,采用long型,在数字后加“L”实现。为避免文本文件在字符转换时发生混乱,fseek 函数一般仅用于二进制文件。[例5]:fseek函数的应用#include "stdio.h"struct student_type{ char name[10];int num;int age;char addr[15];}stud[10];磁盘文件上有10个学生的数据,将第1、3、5、7、9号学生数据输入计算机,并在屏幕上显示出来。main(){ int i;FILE *fp;if ((fp=fopen("stu_list","rb"))==NULL){ printf("cannot open file\n");exit(0);}for (i=0;i<10;i+=2){ fseek(fp,i*sizeof(struct student_type),0);fread(&stud[i],sizeof(struct student_type),1,fp);printf("%-10s %4d %4d %-15s\n",stud[i].name,stud[i].num,stud[i].age,stud[i].addr);}fclose(fp);}13.5.3 ftell 函数用于得到文件的当前位置。出错时,函数的返回值为“-1L”。ftell 的使用方法为:i=ftell(fp); /* 当前位置存放到变量i中 */if(i==-1L)printf(“error\n”);(共35张PPT)第11章 结构与链表为将不同数据类型、但相互关联的一组数据,组合成一个有机整体使用,C语言提供一种称为“结构”的数据结构。11.1 结构类型与结构变量的定义 11.2 结构变量的引用与初始化 11.3 结构数组11.4 指向结构类型数据的指针11.5 链表处理──结构指针的应用11.6 共用型和枚举型11.7 定义已有类型的别名[Return]11.1 结构类型与结构变量的定义C语言中的结构类型,相当于其它高级语言中的“记录”类型。11.1.1 结构类型定义struct 结构类型名 /* struct是结构类型关键字*/ {数据类型 数据项1; 数据类型 数据项2; …… …… 数据类型 数据项n; }; /* 此行分号不能少!*/[案例10.1] 定义一个反映学生基本情况的结构类型,用以存储学生的相关信息。 /*案例代码文件名:AL10_1.C。*/ /*功能:定义一个反映学生基本情况的结构类型*/struct date /*日期结构类型:由年、月、日三项组成*/{int year;int month;int day;};struct std_info /*学生信息结构类型:由学号、姓名、性别和生日共4项组成*/{char no[7];char name[9];char sex[3];struct date birthday;};struct score /*成绩结构类型:由学号和三门成绩共4项组成*/{char no[7];int score1;int score2;int score3;};(1)“结构类型名”和“数据项”的命名规则,与变量名相同。(2)数据类型相同的数据项,既可逐个、逐行分别定义,也可合并成一行定义。例如,本案例代码中的日期结构类型,也可改为如下形式:struct date{int year, month, day;};(3)结构类型中的数据项,既可以是基本数据类型,也允许是另一个已经定义的结构类型。例如,本案例代码中的结构类型std_info,其数据项“birthday”就是一个已经定义的日期结构类型date。(4)本书将1个数据项称为结构类型的1个成员(或分量)。11.1.2 结构变量定义用户自己定义的结构类型,与系统定义的标准类型(int、char等)一样,可用来定义结构变量的类型。1.定义结构变量的方法,可概括为两种:(1)间接定义法──先定义结构类型、再定义结构变量例如,利用[案例10.1]中定义的学生信息结构类型std_info,定义了一个相应的结构变量student:struct std_info student;结构变量student:拥有结构类型的全部成员,其中birthday成员是一个日期结构类型,它又由3个成员构成。注意:使用间接定义法定义结构变量时,必须同时指定结构类型名。(2)直接定义法──在定义结构类型的同时,定义结构变量例如,结构变量student的定义可以改为如下形式:struct std_info{……} student;同时定义结构类型及其结构变量的一般格式如下:struct [结构类型名]{ ……} 结构变量表;2.说明(1)结构类型与结构变量是两个不同的概念,其区别如同int类型与int型变量的区别一样。(2)结构类型中的成员名,可以与程序中的变量同名,它们代表不同的对象,互不干扰。[Return]11.2 结构变量的引用与初始化[案例10.2] 利用[案例10.1]中定义的结构类型struct std_info,定义一个结构变量student,用于存储和显示一个学生的基本情况。/*案例代码文件名:AL10_2.C*/ #include"struct.h" /*定义并初始化一个外部结构变量student */ struct std_info student={"000102","张三","男",{1980,9,20}}; main() { printf("No: %s\n",student.no); printf("Name: %s\n",student.name); printf("Sex: %s\n",student.sex); printf("Birthday: %d-%d-%d\n",student.birthday.year,student.birthday.month, student.birthday.day);} [程序演示]程序运行结果:No: 000102Name: 张三Sex: 男Birthday:1980-9-201.结构变量的引用规则对于结构变量,要通过成员运算符“.”,逐个访问其成员,且访问的格式为:结构变量.成员 /*其中的“.”是成员运算符*/例如,案例中的student.no,引用结构变量student中的no成员;student.name引用结构变量student中的name成员,等等。如果某成员本身又是一个结构类型,则只能通过多级的分量运算,对最低一级的成员进行引用。此时的引用格式扩展为:结构变量.成员.子成员.….最低1级子成员例如,引用结构变量student中的birthday成员的格式分别为:student.birthday.yearstudent.birthday.monthstudent.birthday.day(1)对最低一级成员,可像同类型的普通变量一样,进行相应的各种运算。(2)既可引用结构变量成员的地址,也可引用结构变量的地址。例如,&student.name ,&student 。2.结构变量的初始化结构变量初始化的格式,与一维数组相似:结构变量={初值表}不同的是:如果某成员本身又是结构类型,则该成员的初值为一个初值表。例如,[案例10.2]中的student={"000102", "张三", "男", {1980,9,20}}。注意:初值的数据类型,应与结构变量中相应成员所要求的一致,否则会出错。[Return]11.3 结构数组结构数组的每一个元素,都是结构类型数据,均包含结构类型的所有成员。[案例10.3] 利用[案例10.1]中定义的结构类型struct std_info,定义一个结构数组student,用于存储和显示三个学生的基本情况。/*案例代码文件名:AL10_3.C*/ #include"struct.h" /*定义并初始化一个外部结构数组student[3] */ struct std_info student[3]={{“000102”,“张三”,“男”,{1980,9,20}}, {“000105”,“李四”,“男”,{1980,8,15}}, {“000112”,“王五”,“女”,{1980,3,10}} };/*主函数main()*/main(){ int i;/*打印表头: " □"表示1个空格字符*/printf("No.□□□□Name□□□□□Sex□Birthday\n");/*输出三个学生的基本情况*/for(i=0; i<3; i++){ printf("%-7s",student[i].no);printf("%-9s",student[i].name);printf("%-4s",student[i].sex);printf("%d-%d-%d\n",student[i].birthday.year,student[i].birthday.month, student[i].birthday.day);}}[程序演示]程序运行结果:No. Name Sex Birthday000102 张三 男 1980-9-20000105 李四 男 1980-8-15000112 王五 女 1980-3-10与结构变量的定义相似,结构数组的定义也分直接定义和间接定义两种方法,只需说明为数组即可。与普通数组一样,结构数组也可在定义时进行初始化。初始化的格式为:结构数组[n]={{初值表1},{初值表2},...,{初值表n}}例如,本案例中的结构数组student[3]。[Return]11.4 指向结构类型数据的指针结构变量在内存中的起始地址称为结构变量的指针。11.4.1 指向结构变量的指针 [案例10.4] 使用指向结构变量的指针来访问结构变量的各个成员。 /*案例代码文件名:AL10_4.C*/ #include“struct.h” struct std_info student={“000102”,“张三”,“男”,{1980,9,20}}; main(){ struct std_info *p_std=&student;printf("No: %s\n", p_std->no);printf("Name: %s\n", p_std->name);printf("Sex: %s\n", p_std->sex);printf("Birthday: %d-%d-%d\n", p_std->birthday.year,p_std->birthday.month, p_std->birthday.day);} [程序演示]通过指向结构变量的指针来访问结构变量的成员,与直接使用结构变量的效果一样。一般地说,如果指针变量pointer已指向结构变量var,则以下三种形式等价:(1)var.成员(2)pointer->成员(3)(*pointer).成员 /* “*pointer”外面的括号不能省!*/注意:在格式(1)中,分量运算符左侧的运算对象,只能是结构变量,;而在格式(2)中,指向运算符左侧的运算对象,只能是指向结构变量(或结构数组)的指针变量,否则都出错。思考题:如果要求从键盘上输入结构变量student的各成员数据,如何修改程序?11.4.2 指向结构数组的指针[案例10.5] 使用指向结构数组的指针来访问结构数组。/*案例代码文件名:AL10_5.C*/#include"struct.h"/*定义并初始化一个外部结构数组student */struct std_info student[3]={{"000102","张三","男",{1980,5,20}},{"000105","李四","男",{1980,8,15}},{“000112”,“王五”,“女”,{1980,3,10}} };main(){ struct std_info *p_std=student;int i=0;/*打印表头*/printf("No.□□□□Name□□□□□Sex□Birthday\n");/*输出结构数组内容*/for( ; i<3; i++, p_std++){ printf("%-7s%-9s%-4s", p_std->no, p_std->name, p_std->sex);printf("%4d-%2d-%2d\n", p_std->birthday.year,p_std->birthday.month, p_std->birthday.day);}} [程序演示]如果指针变量p已指向某结构数组,则p+1指向结构数组的下一个元素,而不是当前元素的下一个成员。另外,如果指针变量p已经指向一个结构变量(或结构数组),就不能再使之指向结构变量(或结构数组元素)的某一成员。11.4.3 指向结构数据的指针作函数参数[案例10.6] 用函数调用方式,改写[案例10.5]:编写一个专门的显示函数display(),通过主函数调用来实现显示。/*案例代码文件名:AL10_6.C*/#include"struct.h"/*定义并初始化一个外部结构数组student */struct std_info student[3]={{"000102","张三","男",{1980,5,20}},{"000105","李四","男",{1980,8,15}},{“000112”,“王五”,“女”,{1980,3,10}} };/*主函数main()*/main(){ void display(); /*函数说明*/int i=0;/*打印表头*/printf("No.□□□□Name□□□□□Sex□Birthday\n");/*打印内容*/for( ; i<3; i++){ display( student + i );printf("\n");}}void display(struct std_info *p_std){ printf("%-7s%-9s%-4s", p_std->no,p_std->name, p_std->sex);printf("%4d-%2d-%2d\n", p_std->birthday.year,p_std->birthday.month, p_std->birthday.day);}[程序演示][Return]11.5 链表处理──结构指针的应用11.5.1 概述1.链表结构链表作为一种常用的、能够实现动态存储分配的数据结构,在《数据结构》课程中有详细介绍。为方便没有学过数据结构的读者,本书从应用角度,对链表作一简单介绍。图10-1所示为单链表。(1)头指针变量head──指向链表的首结点。(2)每个结点由2个域组成:1)数据域──存储结点本身的信息。2)指针域──指向后继结点的指针。(3)尾结点的指针域置为“NULL(空)”,作为链表结束的标志。2.对链表的基本操作对链表的基本操作有:创建、检索(查找)、插入、删除和修改等。(1)创建链表是指,从无到有地建立起一个链表,即往空链表中依次插入若干结点,并保持结点之间的前驱和后继关系。(2)检索操作是指,按给定的结点索引号或检索条件,查找某个结点。如果找到指定的结点,则称为检索成功;否则,称为检索失败。(3)插入操作是指,在结点ki-1与ki之间插入一个新的结点k’,使线性表的长度增1,且ki-1与ki的逻辑关系发生如下变化:插入前,ki-1是ki的前驱,ki是ki-1的后继;插入后,新插入的结点k’成为ki-1的后继、ki的前驱,如图10-2所示。(4)删除操作是指,删除结点ki,使线性表的长度减1,且ki-1、ki和ki+1之间的逻辑关系发生如下变化:删除前,ki是ki+1的前驱、ki-1的后继;删除后,ki-1成为ki+1的前驱,ki+1成为ki-1的后继,如图10-3所示。3.C语言对链表结点的结构描述 在C语言中,用结构类型来描述结点结构。例如: struct grade{ char no[7]; /*学号*/int score; /*成绩*/struct grade *next; /*指针域*/};11.5.2 创建一个新链表[案例10.7] 编写一个create()函数,按照规定的结点结构,创建一个单链表(链表中的结点个数不限)。基本思路:首先向系统申请一个结点的空间,然后输入结点数据域的(2个)数据项,并将指针域置为空(链尾标志),最后将新结点插入到链表尾。对于链表的第一个结点,还要设置头指针变量。另外,案例代码中的3个指针变量head、new和tail的说明如下:(1)head──头指针变量,指向链表的第一个结点,用作函数返回值。(2)new──指向新申请的结点。(3)tail──指向链表的尾结点,用tail->next=new,实现将新申请的结点,插入到链表尾,使之成为新的尾结点。/*案例代码文件名:AL10_7.C*/#define NULL 0#define LEN sizeof(struct grade) /*定义结点长度*//*定义结点结构*/struct grade{ char no[7]; /*学号*/int score; /*成绩*/struct grade *next; /*指针域*/};/*create()函数: 创建一个具有头结点的单链表*//*形参:无*//*返回值:返回单链表的头指针*/struct grade *create( void ){ struct grade *head=NULL, *new, *tail;int count=0; /*链表中的结点个数(初值为0)*/for( ; ; ) /*缺省3个表达式的for语句*/{ new=(struct grade *)malloc(LEN); /*申请一个新结点的空间*//*1、输入结点数据域的各数据项*/printf("Input the number of student No.%d(6 bytes): ", count+1);scanf("%6s", new->no);if(strcmp(new->no,"000000")==0) /*如果学号为6个0,则退出*/{ free(new); /*释放最后申请的结点空间*/break; /*结束for语句*/}printf("Input the score of the student No.%d: ", count+1);scanf("%d", &new->score);count++; /*结点个数加1*//*2、置新结点的指针域为空*/new->next=NULL;/*3、将新结点插入到链表尾,并设置新的尾指针*/if(count==1) head=new; /*是第一个结点, 置头指针*/else tail->next=new; /*非首结点, 将新结点插入到链表尾*/tail=new; /*设置新的尾结点*/}return(head);} [程序演示]思考题:在设计存储学号数据的字符数组时,其元素个数应为学号长度+1。为什么?11.5.3 对链表的插入操作[案例10.8] 编写一个insert()函数,完成在单链表的第i个结点后插入1个新结点的操作。当i=0时,表示新结点插入到第一个结点之前,成为链表新的首结点。基本思路:通过单链表的头指针,首先找到链表的第一个结点;然后顺着结点的指针域找到第i个结点,最后将新结点插入到第i个结点之后。/*案例代码文件名:AL10_8.C*//*函数功能:在单链表的第i个结点后插入1个新结点*//*函数参数:head为单链表的头指针,new指向要插入的新结点,i为结点索引号*//*函数返回值:单链表的头指针*/struct grade *insert(struct grade *head, struct grade *new, int i){ struct grade *pointer;/*将新结点插入到链表中*/if(head==NULL) head=new, new->next=NULL; /*将新结点插入到1个空链表中*/else /*非空链表*/if(i==0) new->next=head, head=new; /*使新结点成为 链表新的首结点*/else /*其他位置*/{ pointer=head;/*查找单链表的第i个结点(pointer指向它)*/for(; pointer!=NULL && i>1; pointer=pointer->next, i--) ;if(pointer==NULL) /*越界错*/printf("Out of the range, can’t insert new node!\n");else /*一般情况:pointer指向第i个结点 */new->next=pointer->next, pointer->next=new;}return(head);} [程序演示][Return]11.6 共用型和枚举型简介11.6.1 共用型 1.概念 使几个不同的变量占用同一段内存空间的结构称为共用型。 2.共用类型的定义──与结构类型的定义类似 union 共用类型名 {成员列表;}; 3.共用变量的定义──与结构变量的定义类似(1)间接定义──先定义类型、再定义变量例如,定义data共用类型变量un1,un2,un3的语句如下: union data un1,un2,un3;(2)直接定义──定义类型的同时定义变量例如,union [data]{ int i;char ch;float f;} un1, un2, un3;共用变量占用的内存空间,等于最长成员的长度,而不是各成员长度之和。例如,共用变量un1、un2和un3,在16位操作系统中,占用的内存空间均为4字节(不是2+1+4=7字节)。4.共用变量的引用──与结构变量一样,也只能逐个引用共用变量的成员例如,访问共用变量un1各成员的格式为:un1.i、un1.ch、un1.f。5.特点(1)系统采用覆盖技术,实现共用变量各成员的内存共享,所以在某一时刻,存放的和起作用的是最后一次存入的成员值。例如,执行un1.i=1, un1.ch='c', un1.f=3.14后,un1.f才是有效的成员。(2)由于所有成员共享同一内存空间,故共用变量与其各成员的地址相同。例如,&un1=&un1.i=&un1.ch=&un1.f。(3)不能对共用变量进行初始化(注意:结构变量可以);也不能将共用变量作为函数参数,以及使函数返回一个共用数据,但可以使用指向共用变量的指针。(4)共用类型可以出现在结构类型定义中,反之亦然。11.6.2 枚举型1.枚举类型的定义enum 枚举类型名 {取值表};例如,enum weekdays {Sun,Mon,Tue,Wed,Thu,Fri,Sat};2.枚举变量的定义──与结构变量类似(1)间接定义例如,enum weekdays workday;(2)直接定义例如,enum [weekdays]{Sun,Mon,Tue,Wed,Thu,Fri,Sat } workday;3.说明(1)枚举型仅适应于取值有限的数据。例如,根据现行的历法规定,1周7天,1年12个月。(2)取值表中的值称为枚举元素,其含义由程序解释。例如,不是因为写成“Sun”就自动代表“星期天”。事实上, 枚举元素用什么表示都可以。(3)枚举元素作为常量是有值的──定义时的顺序号(从0开始),所以枚举元素可以进行比较,比较规则是:序号大者为大!例如,上例中的Sun=0、Mon=1、……、Sat=6,所以Mon>Sun、Sat最大。(4)枚举元素的值也是可以人为改变的:在定义时由程序指定。例如,如果enum weekdays {Sun=7, Mon=1 ,Tue, Wed, Thu, Fri, Sat};则Sun=7,Mon=1,从Tue=2开始,依次增1。[Return]11.7 定义已有类型的别名除可直接使用C提供的标准类型和自定义的类型(结构、共用、枚举)外,也可使用typedef定义已有类型的别名。该别名与标准类型名一样,可用来定义相应的变量。 定义已有类型别名的方法如下: (1)按定义变量的方法,写出定义体; (2)将变量名换成别名; (3)在定义体最前面加上typedef。[案例10.9] 给实型float定义1个别名REAL。 (1)按定义实型变量的方法,写出定义体:float f; (2)将变量名换成别名: float REAL; (3)在定义体最前面加上typedef:typedef float REAL;[案例10.10] 给如下所示的结构类型struct date定义1个别名DATE。struct date{ int year, month, day;};(1)按定义结构变量的方法,写出定义体:struct date {……} d;(2)将变量名换成别名: struct date {……} DATE;(3)在定义体最前面加上typedef: typedef struct date {……} DATE;说明:(1)用typedef只是给已有类型增加1个别名,并不能创造1个新的类型。就如同人一样,除学名外,可以再取一个小名(或雅号),但并不能创造出另一个人来。(2)typedef与#define有相似之处,但二者是不同的:前者是由编译器在编译时处理的;后者是由编译预处理器在编译预处理时处理的,而且只能作简单的字符串替换。[Return](共27张PPT)第11章 结构体与共用体11.1 结构体结构体是一种构造数据类型用途:把不同类型的数据组合成一个整体-------自定义数据类型结构体类型定义struct [结构体名]{类型标识符 成员名;类型标识符 成员名;…………….};成员类型可以是基本型或构造型struct是关键字,不能省略合法标识符可省:无名结构体例 struct student{ int num;char name[20];char sex;int age;float score;char addr[30];};namenumsexagescoreaddr2字节2字节20字节1字节4字节30字节……..结构体类型定义描述结构的组织形式,不分配内存结构体类型定义的作用域例 struct student{ int num;char name[20];char sex;int age;float score;char addr[30];};struct student stu1,stu2;11.2 结构体变量的定义先定义结构体类型,再定义结构体变量一般形式:struct 结构体名{类型标识符 成员名;类型标识符 成员名;…………….};struct 结构体名 变量名表列;例 #define STUDENT struct studentSTUDENT{ int num;char name[20];char sex;int age;float score;char addr[30];};STUDENT stu1,stu2;定义结构体类型的同时定义结构体变量一般形式:struct 结构体名{类型标识符 成员名;类型标识符 成员名;…………….}变量名表列;例 struct student{ int num;char name[20];char sex;int age;float score;char addr[30];}stu1,stu2;直接定义结构体变量一般形式:struct{类型标识符 成员名;类型标识符 成员名;…………….}变量名表列;例 struct{ int num;char name[20];char sex;int age;float score;char addr[30];}stu1,stu2;用无名结构体直接定义变量只能一次说明结构体类型与结构体变量概念不同类型:不分配内存; 变量:分配内存类型:不能赋值、存取、运算; 变量:可以结构体可嵌套结构体成员名与程序中变量名可相同,不会混淆结构体类型及变量的作用域与生存期例 struct date{ int month;int day;int year;};struct student{ int num;char name[20];struct date birthday;}stu;numnamebirthdaymonthdayyear例 struct student{ int num;char name[20];struct date{ int month;int day;int year;}birthday;}stu;numnamebirthdaymonthdayyear11.3 结构体变量的引用引用规则结构体变量不能整体引用,只能引用变量成员可以将一个结构体变量赋值给另一个结构体变量结构体嵌套时逐级引用成员(分量)运算符优先级: 1结合性:从左向右引用方式: 结构体变量名.成员名例 struct student{ int num;char name[20];char sex;int age;float score;char addr[30];}stu1,stu2;stu1.num=10;stu1.score=85.5;stu1.score+=stu2.score;stu1.age++;例 struct student{ int num;char name[20];char sex;int age;float score;char addr[30];}stu1,stu2;printf(“%d,%s,%c,%d,%f,%s\n”,stu1); ( )stu1={101,“Wan Lin”,‘M’,19,87.5,“DaLian”}; ( )例 struct student{ int num;char name[20];char sex;int age;float score;char addr[30];}stu1,stu2;stu2=stu1; ( )例 struct student{ int num;char name[20];struct date{ int month;int day;int year;}birthday;}stu1,stu2;numnamebirthdaymonthdayyearstu1.birthday.month=12;例 struct student{ int num;char name[20];char sex;int age;float score;char addr[30];}stu1,stu2;if(stu1==stu2)…….. ( )11.4 结构体变量的初始化形式一:struct 结构体名{类型标识符 成员名;类型标识符 成员名;…………….};struct 结构体名 结构体变量={初始数据};例 struct student{ int num;char name[20];char sex;int age;char addr[30];};struct student stu1={112,“Wang Lin”,‘M’,19, “200 Beijing Road”};形式二:struct 结构体名{类型标识符 成员名;类型标识符 成员名;…………….}结构体变量={初始数据};例 struct student{ int num;char name[20];char sex;int age;char addr[30];}stu1={112,“Wang Lin”,‘M’,19, “200 Beijing Road”};形式三:struct{类型标识符 成员名;类型标识符 成员名;…………….}结构体变量={初始数据};例 struct{ int num;char name[20];char sex;int age;char addr[30];}stu1={112,“Wang Lin”,‘M’,19, “200 Beijing Road”};11.5 结构体数组结构体数组的定义三种形式:形式一:struct student{ int num;char name[20];char sex;int age;};struct student stu[2];形式二:struct student{ int num;char name[20];char sex;int age;}stu[2];形式三:struct{ int num;char name[20];char sex;int age;}stu[2];numnamesexagenumnamesexagestu[0]stu[1]25B结构体数组初始化例 struct{ int num;char name[20];char sex;int age;}stu[ ]={{……},{……},{……}};顺序初始化:struct student{ int num;char name[20];char sex;int age;};struct student stu[ ]={100,“Wang Lin”,‘M’,20,101,“Li Gang”,‘M’,19,110,“Liu Yan”,‘F’,19};例 struct student{ int num;char name[20];char sex;int age;}stu[ ]={{……},{……},{……}};分行初始化:struct student{ int num;char name[20];char sex;int age;};struct student stu[ ]={{100,“Wang Lin”,‘M’,20},{101,“Li Gang”,‘M’,19},{110,“Liu Yan”,‘F’,19}};全部初始化时维数可省结构体数组引用引用方式: 结构体数组名[下标].成员名struct student{ int num;char name[20];char sex;int age;}str[3];stu[1].age++;strcpy(stu[0].name,”ZhaoDa”);例 统计候选人选票struct person{ char name[20];int count;}leader[3]={“Li”,0,“Zhang”,0,”Wang“,0};main(){ int i,j; char leader_name[20];for(i=1;i<=10;i++){ scanf("%s",leader_name);for(j=0;j<3;j++)if(strcmp(leader_name,leader[j].name)==0)leader[j].count++;}for(i=0;i<3;i++)printf("%5s:%d\n",leader[i].name,leader[i].count);}namecountLiZhangWang00011.6 结构体和指针指向结构体变量的指针定义形式:struct 结构体名 *结构体指针名;例 struct student *p;使用结构体指针变量引用成员形式存放结构体变量在内存的起始地址numnamesexagestupstruct student{ int num;char name[20];char sex;int age;}stu;struct student *p=&stu;(*结构体指针名).成员名结构体指针名->成员名结构体变量名.成员名指向运算符优先级: 1结合方向:从左向右例 指向结构体的指针变量main(){ struct student{ long int num;char name[20];char sex;float score;}stu_1,*p;p=&stu_1;stu_1.num=89101;strcpy(stu_1.name,"Li Lin");p->sex='M';p->score=89.5;printf("\nNo:%ld\nname:%s\nsex:%c\nscore:%f\n",(*p).num,p->name,stu_1.sex,p->score);}例 int n;int *p=&n;*p=10; n=10struct student stu1;struct student *p=&stu1;stu1.num=101; (*p).num=101指向结构体数组的指针例 指向结构体数组的指针struct student{ int num;char name[20];char sex;int age;}stu[3]={{10101,"Li Lin",'M',18},{10102,"Zhang Fun",'M',19},{10104,"Wang Min",'F',20}};main(){ struct student *p;for(p=stu;pprintf("%d%s%c%d\n",p->num,p->name,p->sex,p->age);}numnamesexagestu[0]pstu[1]stu[2]p+1用指向结构体的指针作函数参数用结构体变量的成员作参数----值传递用指向结构体变量或数组的指针作参数----地址传递用结构体变量作参数----多值传递,效率低struct data{ int a, b, c; };main(){ void func(struct data);struct data arg;arg.a=27; arg.b=3; arg.c=arg.a+arg.b;printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);printf("Call Func()....\n");func(arg);printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);}void func(struct data parm){ printf("parm.a=%d parm.b=%d parm.c=%d\n",parm.a,parm.b,parm.c);printf("Process...\n");parm.a=18; parm.b=5; parm.c=parm.a*parm.b;printf("parm.a=%d parm.b=%d parm.c=%d\n",parm.a,parm.b,parm.c);printf("Return...\n");}arga :27b: 3c :30(main)(func)parma :27b: 3c :30copyarga :27b: 3c :30(main)(func)parma :18b: 5c :90arga :27b: 3c :30(main)arga :27b: 3c :30(main)例 用结构体变量作函数参数struct data{ int a, b, c; };main(){ void func(struct data *parm);struct data arg;arg.a=27; arg.b=3; arg.c=arg.a+arg.b;printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);printf("Call Func()....\n");func(&arg);printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);}void func(struct data *parm){ printf("parm->a=%d parm->b=%d parm->c=%d\n",parm->a,parm->b,parm->c);printf("Process...\n");parm->a=18; parm->b=5; parm->c=parm->a*parm->b;printf("parm->a=%d parm->b=%d parm->c=%d\n",parm->a,parm->b,parm->c);printf("Return...\n");}arga :18b: 5c :90(main)arga :27b: 3c :30(main)例 用结构体指针变量作函数参数arga :27b: 3c :30(main)(func)parm****arga :18b: 5c :90(main)(func)parm****11.8 共用体构造数据类型,也叫联合体用途:使几个不同类型的变量共占一段内存(相互覆盖)共用体类型定义定义形式:union 共用体名{类型标识符 成员名;类型标识符 成员名;…………….};例 union data{ int i;char ch;float f;};fchi类型定义不分配内存形式一:union data{ int i;char ch;float f;}a,b;形式二:union data{ int i;char ch;float f;};union data a,b,c,*p,d[3];形式三:union{ int i;char ch;float f;}a,b,c;共用体变量的定义fchifchiab共用体变量定义分配内存,长度=最长成员所占字节数共用体变量任何时刻只有一个成员存在共用体变量引用引用方式:例 a.i=1;a.ch=‘a’;a.f=1.5;printf(“%d”,a.i); ( 编译通过,运行结果不对)引用规则不能引用共用体变量,只能引用其成员共用体指针名->成员名共用体变量名.成员名(*共用体指针名).成员名union data{ int i;char ch;float f;};union data a,b,c,*p,d[3];a.i a.ch a.fp->i p->ch p->f(*p).i (*p).ch (*p).fd[0].i d[0].ch d[0].f共用体变量中起作用的成员是最后一次存放的成员例 union{ int i;char ch;float f;}a;a=1; ( )不能在定义共用体变量时初始化例 union{ int i;char ch;float f;}a={1,’a’,1.5}; ( )可以用一个共用体变量为另一个变量赋值例 float x;union{ int i; char ch; float f;}a,b;a.i=1; a.ch=‘a’; a.f=1.5;b=a; ( )x=a.f; ( )例 将一个整数按字节输出01100001 01000001低字节高字节0100000101100001ch[0]ch[1]运行结果:i=60501ch0=101,ch1=141ch0=A,ch1=amain(){ union int_char{ int i;char ch[2];}x;x.i=24897;printf("i=%o\n",x.i);printf("ch0=%o,ch1=%o\nch0=%c,ch1=%c\n",x.ch[0],x.ch[1],x.ch[0],x.ch[1]);}结构体与共用体区别: 存储方式不同struct node{ char ch[2];int k;}a;union node{ char ch[2];int k;}b;achkbchk变量的各成员同时存在任一时刻只有一个成员存在联系: 两者可相互嵌套例 结构体中嵌套共用体namenumsexjobclasspositionLiWang10112086FMST501prof循环n次读入姓名、号码、性别、职务job==‘s’真真假假读入class读入position输出“输入错”循环n次job==‘s’真假输出:姓名,号码,性别,职业,班级输出:姓名,号码,性别,职业,职务job==‘t’struct{ int num;char name[10];char sex;char job;union{ int class;char position[10];}category;}person[2];例:共用体中嵌套结构体,机器字数据与字节数据的处理00010010 00110100低字节高字节0011010000010010lowhigh0x123400010010 11111111低字节高字节1111111100010010lowhigh0x12ffstruct w_tag{ char low;char high;};union u_tag{ struct w_tag byte_acc;int word_acc;}u_acc;word_accbyte_acc.lowbyte_acc.highu_acc11.10 用typedef定义类型功能:用自定义名字为已有数据类型命名类型定义简单形式: typedef type name;例 typedef int INTEGER;类型定义语句关键字已有数据类型名用户定义的类型名例 typedef float REAL;类型定义后,与已有类型一样使用例 INTEGER a,b,c;REAL f1,f2;int a,b,c;float f1,f2;说明:1.typedef 没有创造新数据类型2.typedef 是定义类型,不能定义变量3.typedef 与 define 不同define typedef预编译时处理 编译时处理简单字符置换 为已有类型命名typedef定义类型步骤按定义变量方法先写出定义体 如 int i;将变量名换成新类型名 如 int INTEGER;最前面加typedef 如 typedef int INTEGER;用新类型名定义变量 如 INTEGER i,j;例 定义数组类型int a[100];int ARRAY[100];typedef int ARRAY[100];ARRAY a,b,c;例 定义指针类型char *str;char *STRING;typedef char *STRING;STRING p,s[10];例 定义函数指针类型int (*p)();int (*POWER)();typedef int (*POWER)();POWER p1,p2;例 定义结构体类型struct date{ int month;int day;int year;}d;例 定义结构体类型struct date{ int month;int day;int year;}DATE;例 定义结构体类型typedef struct date{ int month;int day;int year;}DATE;例 定义结构体类型DATE birthday, *p;类型定义可嵌套例 typedef struct club{ char name[20];int size;int year;}GROUP;typedef GROUP *PG;PG pclub;GROUP为结构体类型PG为指向GROUP的指针类型(共35张PPT)为了解决比较复杂的问题,本章介绍C语言提供的一种最简单的构造类型──数组。7.1 1维数组的定义和引用7.2 2维数组的定义和引用7.3 字符数组与字符串[Return]第7章 数 组7.1 1维数组的定义和引用7.1.1 1维数组的定义7.1.2 1维数组元素的引用7.1.3 1维数组元素的初始化7.1.4 1维数组应用举例[Return]7.1.1 1维数组的定义[案例6.1] 从键盘上任意输入10个整数,要求按从小到大的顺序在屏幕上显示出来。排序的方法有很多,本题采用冒泡法。冒泡法的基本思想:通过相邻两个数之间的比较和交换,使排序码(数值)较小的数逐渐从底部移向顶部,排序码较大的数逐渐从顶部移向底部。就像水底的气泡一样逐渐向上冒,故而得名。由A[n]~A[1]组成的n个数据,进行冒泡排序的过程可以描述为:(1)首先将相邻的A[n]与A[n-1]进行比较,如果A[n]的值小于A[n-1]的值,则交换两者的位置,使较小的上浮,较大的下沉;接着比较A[n-1]与A[n-2],同样使小的上浮,大的下沉。依此类推,直到比较完A[2]和A[1]后,A[1]为具有最小排序码(数值)的元素,称第一趟排序结束。(2)然后在A[n]~A[2]区间内,进行第二趟排序,使剩余元素中排序码最小的元素上浮到A[2];重复进行n-1趟后,整个排序过程结束。/*案例代码文件名:AL6_1.C*//*功能:从键盘上任意输入n个整数,用冒泡法按从小到大地排序,并在屏幕上显示出来。*/#include "stdio.h"#define NUM 10 /*定义符号常量(数据个数N)*/main(){ int data[NUM]; /*定义1个1维整型数组data*/int i,j,temp; /*定义循环变量和临时变量*/clrscr(); /*库函数clrscr():清屏*/printf("Please input 10 numbers:\n");for(i=0; iscanf("%d", &data[i]);/*冒泡法排序*/for(i=0; ifor(j=NUM-1; j>i; j--) /*内循环:进行每趟比较*/if(data[j]{temp=data[j];data[j]=data[j-1];data[j-1]=temp;};/*输出排序后的数据*/printf("\nthe result of sort:\n");for(i=0; iprintf("%d ",data[i]);getch(); /*等待键盘输入任一字符,目的使程序暂停*/}数组同变量一样,也必须先定义、后使用。1维数组是只有1个下标的数组,定义形式如下:数据类型 数组名[常量表达式][, 数组名2[常量表达式2]……];(1)“数据类型”是指数组元素的数据类型。(2)数组名,与变量名一样,必须遵循标识符命名规则。(3)“常量表达式”必须用方括号括起来,指的是数组的元素个数(又称数组长度),它是一个整型值,其中可以包含常数和符号常量,但不能包含变量。注意:C语言中不允许动态定义数组。特别说明:在数组定义时,“常量表达式”外的方括号;以及元素引用时,“下标表达式”外的方括号,都是C语言语法规则所要求的,不是本书所约定的可选项的描述符号!(4)数组元素的下标,是元素相对于数组起始地址的偏移量,所以从0开始顺序编号。(5)数组名中存放的是一个地址常量,它代表整个数组的首地址。同一数组中的所有元素,按其下标的顺序占用一段连续的存储单元。[Return]7.1.2 数组元素的引用引用数组中的任意一个元素的形式:数组名[下标表达式]1.“下标表达式”可以是任何非负整型数据,取值范围是0~(元素个数-1)。特别强调:在运行C语言程序过程中,系统并不自动检验数组元素的下标是否越界。因此在编写程序时,保证数组下标不越界是十分重要的。2.1个数组元素,实质上就是1个变量,它具有和相同类型单个变量一样的属性,可以对它进行赋值和参与各种运算。3.在C语言中,数组作为1个整体,不能参加数据运算,只能对单个的元素进行处理。[Return]7.1.3 1维数组元素的初始化初始化格式:数据类型 数组名[常量表达式]={初值表}(1)如果对数组的全部元素赋以初值,定义时可以不指定数组长度(系统根据初值个数自动确定)。如果被定义数组的长度,与初值个数不同,则数组长度不能省略。(2)“初值表”中的初值个数,可以少于元素个数,即允许只给部分元素赋初值。(3)根据存储类型的不同,数组有静态数组(static)和动态数组(auto)之分;根据定义的位置不同,数组有内部数组(在函数内部定义的数组)和外部数组(在函数外部定义的数组)之分。[Return]7.1.4 1维数组应用举例[案例6.2] 已知某课程的平时、实习、测验和期末成绩,求该课程的总评成绩。其中平时、实习、测验和期末分别占10%、20%、20%、50%。 /*案例代码文件名:AL6_2.C*/ /*功能:从键盘上循环输入某课程的平时、实习、测验和期末成绩,按10%,20%,20%,50%的比例计算总评成绩,并在屏幕上显示出来。按空格键继续循环,其他键终止循环。*/ #include “stdio.h” main() { int i=1,j; char con_key=‘\x20’; /* ‘\x20’ 空格键的ASCII码*/ float score[5],ratio[4]={0.1,0.2,0.2,0.5}; /*定义成绩、比例系数数组*/ while(con_key=='\x20')while(con_key=='\x20'){clrscr();printf("输入第%2d个学生的成绩\n", i++);printf("平时 实习 测验 期末成绩\n");score[4]=0; /* score[4]:存储总评成绩*/for(j=0; j<4; j++){scanf("%f",&score[j]);score[4] += score[j] * ratio[j];}printf("总评成绩为:%6.1f\n", score[4]);printf("\n按空格键继续,其它键退出");con_key=getch(); /*getch()函数等待从键盘上输入一个字符*/}}[Return]7.2 2维数组的定义和引用7.2.1 2维数组的定义7.2.2 2维数组元素的引用7.2.3 2维数组元素的初始化7.2.4 2维数组应用举例[Return][案例6.3] 给一个2*3的2维数组各元素赋值,并输出全部元素的值。/*案例代码文件名:AL6_3.C*//*功能:从键盘上给2*3数组赋值,并在屏幕上显示出来。*/#define Row 2#define Col 3#include "stdio.h"main(){ int i, j, array[Row][Col]; /*定义1个2行3列的2维数组array*/for(i=0; ifor(j=0; j{printf("please input array[%2d][%2d]:",i,j);scanf("%d",&array[i][j]); /*从键盘输入a[i][j]的值*/}printf("\n");/*输出2维数组array*/for(i=0;i7.2.1 2维数组的定义{ for(j=0;jprintf("%d\t",array[i][j]); /*将a[i][j]的值显示在屏幕上*/printf("\n");}getch();}2维数组的定义方式如下:数据类型 数组名[行常量表达式][列常量表达式][, 数组名2[行常量表达式2][列常量表达式2]……];1.数组元素在内存中的排列顺序为“按行存放”,即先顺序存放第一行的元素,再存放第二行,以此类推。2. 设有一个m*n的数组x,则第i行第j列的元素x[i][j]在数组中的位置为:i*n+j(注意:行号、列号均从0开始计数)。3.可以把2维数组看作是一种特殊的1维数组:它的元素又是一个1维数组。例如,对x[3][2],可以把x看作是一个1维数组,它有3个元素:x[0]、x[1]、x[2],每个元素又是一个包含2个元素的1维数组,如图6-4所示。即把x[0]、x[1]、x[2]看作是3个1维数组的名字。[Return]7.2.2 2维数组元素的引用引用2维数组元素的形式为:数组名[行下标表达式][列下标表达式]1.“行下标表达式”和“列下标表达式”,都应是整型表达式或符号常量。2.“行下标表达式”和“列下标表达式”的值,都应在已定义数组大小的范围内。假设有数组x[3][4],则可用的行下标范围为0~2,列下标范围为0~3。3.对基本数据类型的变量所能进行的操作,也都适合于相同数据类型的2维数组元素。[Return]7.2.3 2维数组元素的初始化1.按行赋初值数据类型 数组名[行常量表达式][列常量表达式]={{第0行初值表},{第1行初值表},……,{最后1行初值表}};赋值规则:将“第0行初值表”中的数据,依次赋给第0行中各元素;将“第1行初值表”中的数据,依次赋给第1行各元素;以此类推。2.按2维数组在内存中的排列顺序给各元素赋初值数据类型 数组名[行常量表达式][列常量表达式]={初值表};赋值规则:按2维数组在内存中的排列顺序,将初值表中的数据,依次赋给各元素。如果对全部元素都赋初值,则“行数”可以省略。注意:只能省略“行数”。[Return]7.2.4 2维数组应用举例[案例6.4] 有M个学生,学习N门课程,已知所有学生的各科成绩,编程:分别求每个学生的平均成绩和每门课程的平均成绩。/*案例代码文件名:AL6_4.C*//*功能:计算个人平均成绩与各科平均成绩,并在屏幕上显示出来。*/#define NUM_std 5 /*定义符号常量人数为5*/#define NUM_course 4 /*定义符号常量课程为4*/#include "stdio.h"main(){ int i,j;static float score[NUM_std+1][NUM_course+1]={{78,85,83,65},{88,91,89,93}, {72,65,54,75},{86,88,75,60}, {69,60,50,72}};for(i=0;i{for(j=0;j{ score[i][NUM_course] += score[i][j];/*求第i个人的总成绩*/score[NUM_std][j] += score[i][j]; /*求第j门课的总成绩*/}score[i][NUM_course] /= NUM_course;/*求第i个人的平均成绩*/}for(j=0;jscore[NUM_std][j] /= NUM_std; /*求第j门课的平均成绩*/clrscr();/*输出表头*/printf("学生编号 课程1 课程2 课程3 课程4 个人平均\n");/*输出每个学生的各科成绩和平均成绩*/for(i=0;i{ printf("学生%d\t",i+1);for(j=0;jprintf("%6.1f\t",score[i][j]);printf("\n");}/*输出1条短划线*/for(j=0;j<8*(NUM_course+2);j++)printf("-");printf("\n课程平均");/*输出每门课程的平均成绩*/for(j=0;jprintf("%6.1f\t",score[NUM_std][j]);printf("\n");getch();}[Return]7.3 字符数组与字符串7.3.1 字符数组的逐个字符操作7.3.2 字符数组的整体操作7.3.3 常用的字符串处理函数[Return]7.3.1 字符数组的逐个字符操作[案例6.5]从键盘输入一个字符串,回车键结束,并将字符串在屏幕上输出。/*案例代码文件名:AL6_5.C*/main(){int i;static char str[80];clrscr();for(i=0;i<80;i++){ str[i]=getch(); /*逐次给数组元素str[i]赋值,但不回显在屏幕上*/printf("*"); /*以星号代替输入字符的个数*/if(str[i]=='\x0d') break;/*若输入回车则终止循环*/}i=0;while(str[i]!='\x0d')printf("%c",str[i++]); /*逐次输出字符数组的各个元素*/printf("\n");getch(); /*程序暂停*/}1.字符数组的定义1维字符数组,用于存储和处理1个字符串,其定义格式与1维数值数组一样。2维字符数组,用于同时存储和处理多个字符串,其定义格式与2维数值数组一样。2.字符数组的初始化字符数组的初始化,可以通过为每个数组元素指定初值字符来实现。3.字符数组的引用字符数组的逐个字符引用,与引用数值数组元素类似。(1)字符数组的输入除了可以通过初始化使字符数组各元素得到初值外,也可以使用getchar()或scanf()函数输入字符。例如:char str[10];……for(i=0; i<10; i++){ scanf("%c", &str[i]);fflush(stdin); /*清除键盘输入缓冲区*/}……(2)字符数组的输出字符数组的输出,可以用putchar()或printf()函数。例如:char str[10]="c language";……for(i=0; i<10; i++) printf("%c", str[i]);printf("\n");……注意:逐个字符输入、输出时,要指出元素的下标,而且使用“%c”格式符。另外,从键盘上输入字符时,无需输入字符的定界符──单引号;输出时,系统也不输出字符的定界符。[Return]7.3.2 字符数组的整体操作[案例6.6] 字符数组的整体输入与输出。/*案例代码文件名:AL6_6.C*//*功能:将2维字符数组进行初始化,并在屏幕上输出*/main(){ int i;char name[5][9]={"张三山", "李四季", "王五魁", "刘六顺", "赵七巧"};for(i=0;i<5;i++)printf("\n%s\t",name[i]); /*name[i]代表该行数组元素的首地址*/getch();}1.字符串及其结束标志所谓字符串,是指若干有效字符的序列。C语言中的字符串,可以包括字母、数字、专用字符、转义字符等。C语言规定:以‘\0’作为字符串结束标志(‘\0’代表ASCII码为0的字符,表示一个“空操作”,只起一个标志作用)。因此可以对字符数组采用另一种方式进行操作了──字符数组的整体操作。注意:由于系统在存储字符串常量时,会在串尾自动加上1个结束标志,所以无需人为地再加1个。另外,由于结束标志也要在字符数组中占用一个元素的存储空间,因此在说明字符数组长度时,至少为字符串所需长度加1。2.字符数组的整体初始化字符串设置了结束标志以后,对字符数组的初始化,就可以用字符串常量来初始化字符数组。3.字符数组的整体引用(1)字符串的输入除了可以通过初始化使字符数组各元素得到初值外,也可以使用scanf()函数输入字符串。(2)字符串的输出printf()函数,不仅可以逐个输出字符数组元素,还可以整体输出存放在字符数组中的字符串。[Return]7.3.3 常用的字符串处理函数字符串标准函数的原型在头文件string.h中。1.输入字符串──gets()函数(1)调用方式:gets(字符数组)(2)函数功能:从标准输入设备(stdin)──键盘上,读取1个字符串(可以包含空格),并将其存储到字符数组中去。(3)使用说明1)gets()读取的字符串,其长度没有限制,编程者要保证字符数组有足够大的空间,存放输入的字符串。2)该函数输入的字符串中允许包含空格,而scanf()函数不允许。2.输出字符串──puts()函数(1)调用方式:puts(字符数组)(2)函数功能:把字符数组中所存放的字符串,输出到标准输出设备中去,并用‘\n’取代字符串的结束标志‘\0’。所以用puts()函数输出字符串时,不要求另加换行符。( 3)使用说明1)字符串中允许包含转义字符,输出时产生一个控制操作。2)该函数一次只能输出一个字符串,而printf()函数也能用来输出字符串,且一次能输出多个。3.字符串比较──strcmp()函数(1)调用方式:strcmp(字符串1 ,字符串2)其中“字符串”可以是串常量,也可以是1维字符数组。(2)函数功能:比较两个字符串的大小。如果:字符串1=字符串2,函数返回值等于0;字符串1<字符串2,函数返回值负整数;字符串1>字符串2,函数返回值正整数。(3)使用说明1)如果一个字符串是另一个字符串从头开始的子串,则母串为大。2)不能使用关系运算符“==”来比较两个字符串,只能用strcmp() 函数来处理。[案例6.7] gets函数和strcmp函数的应用。/*案例代码文件名:AL6_7.C*//*功能:简单密码检测程序*/#include "stdio.h"main(){char pass_str[80]; /*定义字符数组passstr*/int i=0;/*检验密码*/while(1){clrscr();printf("请输入密码\n");gets(pass_str); /*输入密码*/if(strcmp(pass_str,“password”)!=0) /*口令错*/printf("口令错误,按任意键继续");elsebreak; /*输入正确的密码,中止循环*/getch();i++;if(i==3) exit(0); /*输入三次错误的密码,退出程序*/}/*输入正确密码所进入的程序段*/}4.拷贝字符串──strcpy()函数(1)调用方式:strcpy(字符数组, 字符串)其中“字符串”可以是串常量,也可以是字符数组。(2)函数功能:将“字符串”完整地复制到“字符数组”中,字符数组中原有内容被覆盖。(3)使用说明1)字符数组必须定义得足够大,以便容纳复制过来的字符串。复制时,连同结束标志'\0'一起复制。2)不能用赋值运算符“=”将一个字符串直接赋值给一个字符数组,只能用strcpy()函数来处理。5.连接字符串──strcat()函数(1)调用方式:strcat(字符数组, 字符串)(2)函数功能:把“字符串”连接到“字符数组”中的字符串尾端,并存储于“字符数组”中。“字符数组”中原来的结束标志,被“字符串”的第一个字符覆盖,而“字符串”在操作中未被修改。(3)使用说明1)由于没有边界检查,编程者要注意保证“字符数组”定义得足够大,以便容纳连接后的目标字符串;否则,会因长度不够而产生问题。2)连接前两个字符串都有结束标志'\0',连接后“字符数组”中存储的字符串的结束标志'\0'被舍弃,只在目标串的最后保留一个'\0'。6.求字符串长度──strlen()函数(len是length的缩写)(1)调用方式:strlen(字符串)(2)函数功能:求字符串(常量或字符数组)的实际长度(不包含结束标志)。7.将字符串中大写字母转换成小写──strlwr()函数(1)调用方式:strlwr(字符串)(2)函数功能:将字符串中的大写字母转换成小写,其它字符(包括小写字母和非字母字符)不转换。8.将字符串中小写字母转换成大写──strupr()函数(1)调用方式:strupr(字符串)(2)函数功能:将字符串中小写字母转换成大写,其它字符(包括大写字母和非字母字符)不转换。[Return](共13张PPT)第12章 位 运 算12.1 概 述位运算是指对二进制位进行的运算。1. 字节和位位(bit):字节(byte):由若干二进制位(通常8位)组成,每个字节有一个地址。字(word):由若干个字节组成。2. 原码:最高位为符号位,其余各位代表数值本身的绝对值。3. 反码:正数的反码与原码相同;负数的反码是除符号位外,其余各位的原码取反。4. 补码:正数的补码与原码相同;负数的补码是其反码加1(加1时符号位也参与运算)。计算机中的数是以补码形式存放的。原码:0000 0000反码:0000 0000补码:0000 0000+0原码:1000 0000反码:1111 1111补码:0000 0000-012.2 位运算符1)除“~”外,均为二目运算符。2)运算量只能是整型和字符型。1. 按位与0&0=0 0&1=0 1&0=0 1&1=12. 按位或0|0=0 0|1=1 1|0=1 1|1=13. 按位异或(XOR)0^0=0 0^1=1 1^0=1 1^1=04. 按位取反用于对一个二进制数按位取反。它的优先级别高于算术运算符、关系运算符、逻辑运算符和其它位运算符。5. 左移用于将一个数的二进制位全部左移若干位。低位补0。a=a<<2 (左移两位)左移n位相当于乘以2n。6. 右移用于将一个数的二进制位全部右移若干位。无符号数,高位补0;有符号数,根据系统而定,Turbo C 采用“算术右移”,即移入最高位的是1。a=a>>2 (右移两位)右移n位相当于除以2n。7. 扩展的赋值运算符:位运算符与赋值运算符的结合。&= |= >>= <<= ^=a&=b 相当于:a=a&ba<<=2 相当于:a=a<<28. 不同长度的数据进行位运算按右端对齐,正数和无符号数补0,负数补满1。12.3 位运算举例步骤分析:1. 使a右移4位,即 a>>42. 设置一个低4位全1,其余为0的数,即 ~(~0<<4)(见下图)0:0000…00 0000~0:1111…11 1111~0<<4:1111… 11 0000~(~0<<4):0000 …00 11113. 将上面两数相与,即a>>4& ~(~0<<4)[例3]:取一个整数的4~7位main(){ unsigned a,b,c,d;scanf("%o",&a);b=a>>4;c=~(~0<<4);d=b&c;printf("%o\n%o\n",a,d);}运行步骤分析:1. 将a右端n位放在b的高n位中,b=a<<(16-n);2. 将a右移n位,高n位补0,即:c=a>>n;3. 将c与b进行按位或运算。即:c=c|b;[例4]:将a右循环移n位假设一个整数占2个字节。main(){ unsigned a,b,c;int n;scanf("a=%o,n=%d",&a,&n);b=a<<(16-n);c=a>>n;c=c|b;printf("%o\n%o\n",a,c);}运行12.4 位 段位段是以位为单位定义长度的结构体类型中的成员,用于在一个字节中存放多个信息。struct packed-data{ unsigned a:2;unsigned b:6;unsigned c:4;unsigned d:4;int i;}data;各个位段不一定非要占满一个字节,未占的位为空闲。位段的空间分配方向因机器而异,PDP系列为从右向左分配,即:位段中的数据引用方法为:data.a=2;data.b=7;data.c=9;注意位段允许的数值范围(依据位数决定)。关于位段定义和引用的说明1. 若某一位段要从另一字开始存放,可以使用下列定义:unsigned a:2;unsigned b:6;unsigned :0;使下一位段从下一单元开始。unsigned c:4;2. 一个位段必须存储在同一存储单元中,不能跨单元。3. 可以定义无名位段。如:unsigned a:1;unsigned :2; (这两位空间不用)unsigned b:3;unsigned c:4;一个存储单元另一个存储单元4. 位段的长度不能大于存储单元的长度,也不能定义位段数组。5. 位段可以用以下格式符输出:%d、%u、%o、%x等。如:printf(“%d,%d,%d”,data.a,data.b,data.c);6. 位段可以在数值表达式中引用,它会被系统自动转换成整型数。如:data.a+5/data.b(共109张PPT)《C程序设计》信息工程学院 黄涛第七章 数组结构*数组的存储和访问方式一维数组的定义和引用二维数组的定义和引用字符数组数组在程序设计中的重要性本章重点:数组是若干同一类型数据的有序集合。集合中的每一个元素都相当于一个与数组同类型的变量。集合中的每一个元素用同一个名字和不同下标来区分引用。数组的基本概念数组属于一种构造数据类型。构造数据类型是按一定规则组成的数据类型。数组的基本概念重点掌握内容:◆ 数组的定义、初始化和引用◆ 数组在内存中的存储方式和访问方式◆ 数组和循环语句的结合使用◆ 字符数组及字符串变量的实现7.1 一维数组的定义和引用一维数组的定义定义方法:类型说明符 数组名[常量表达式]说明:① 数组名的命名规则与变量名的命名规则相同。如: int fnum[10];float score[40];char name[10];一维数组的定义② 一对方括号表示一维数组;方括号中只能是常量表达式,不能是变量。C语言不允许定义动态数组。常量表达式是指:直接常量或符号常量或由它们组成的表达式。int fnum[10]; 或:#define ArrayLen 10int fnum[ArrayLen];int n;scanf(“%d”, &n);int fnum[n];说明:一维数组的定义③ 数组元素的个数即数组的长度由方括号中的常量确定。对于一维数组array[n],数组元素的个数是n,有效的元素下标范围是 [0] ~ [n–1]。说明:比如 int a[10]; 则该数组有a[0]~a[9]共10个元素。一维数组元素的引用C语言规定:同变量一样,数组也必须 “先定义,后使用”。C语言规定:只能单个的引用某一个数组元素,而不能一次引用整个数组。数组中的每一个元素都相当于一个与数组同类型的普通变量。比如: int a[10];则该数组有 a[0]~a[9] 共10个元素,就相当于10个普通的整型变量。一维数组元素的引用一维数组元素的引用方法: 数组名[下标]其中,下标可以是常量、变量或表达式。定义数组:int fnum[10], n = 1;引用数组元素:fnum[0] = 1; /* 下标是常量 */fnum[n] = 1; /* 下标是变量 */fnum[n+1] = fnum[0]+fnum[fnum[n]];/* 下标是表达式或数组元素 */一维数组元素的引用在引用数组元素时,编程者一定要确保下标在有效范围内。 C语言编译器本身并不进行下标越限的检查。如果一维数组定义为array[n],则有效的元素下标范围是[0]~[n–1]。注意:比如: int a[10];则该数组合法的10个元素是:a[0]~a[9]不存在a[-1]或a[10]这样的元素。引用非法的数组元素:fnum[-1] = 1; /* 下标越下限 */fnum[10] = 1; /* 下标越上限 */一维数组元素的引用定义数组:int fnum[10], n = 1;引用合法的数组元素:fnum[0] = 1;fnum[1] = 1;fnum[9] = fnum[0] + fnum[1];虽然越限了,但是编译时并不给出出错信息。引用数组元素时,确保下标在有效范围的任务由编程者完成。一维数组的存储方式数组在内存中占据连续的字节单元;整个数组占据的字节单元数等于数组元素个数乘以该数组所属数据类型的数据占据的字节单元数。数组元素在内存中按顺序连续存放。如: int a[10];则该数组在内存中共占据连续的2×10=20个字节单元,依次保存 a[0]、a[1]、 …、a[9] 共10个元素。一维数组的存储方式…20个字节a[0]a[1]a[2]a[9]可见,如果a[0]的地址是0x1000,则:a[1]的地址是 0x1000+1×2=0x1002;a[2]的地址是 0x1000+2×2=0x1004;… 。如: int a[10];该数组在内存中共占据连续的20个字节单元。如图:一维数组元素的访问方式首先回忆一下变量的访问方式:变量的访问方式:直接使用变量名(变量名相当于符号地址)如:int num; num = 0;访问过程:从由系统维护的变量名和其所占内存单元地址的对应信息表中找到要访问的变量的地址,然后从(或向)该地址所指向的内存单元取数据或存数据。一维数组元素的访问方式再来看数组元素的访问方式:数组元素的访问方式:数组名[要访问的元素的下标]如:int fnum[10]; fnum[0] = 0;别忘了:每一个数组元素都相当于一个与数组同类型的普通变量。所以,访问一个数组元素就如同访问一个变量,关键就在于要能找到该数组元素的地址。一维数组元素的访问方式因为:数组名包含了数组在内存中的首地址信息,即数组名就表示数组中第一个元素的地址。 为什么用数组名和下标就能访问数组元素数组的首地址,即是指数组中第一个元素在内存中所占单元的地址。一维数组元素的访问方式…a[0]a[1]a[2]a[9]数组名包含数组的首地址信息0x10000x10020x10040x1012=&a[0]=&a[1]=&a[2]=&a[9]如: int a[10];数组名 a&a[0]0x1000数组名指向数组的首地址(或第一个元素)a=a+0=a+1=a+2=a+9一维数组元素的访问方式…a[0]a[1]a[2]a[9]0x10000x10020x10040x1012=&a[0]=&a[1]=&a[2]=&a[9]如: int a[10];数组名 a&a[0]0x1000a=a+0=a+1=a+2=a+9现在来看:系统如何根据数组名和下标找到指定元素的地址?一维数组元素的访问方式…a[0]a[1]a[2]a[9]0x10000x10020x10040x1012=&a[0]=&a[1]=&a[2]=&a[9]如: int a[10];数组名 a&a[0]0x1000a=a+0=a+1=a+2=a+9对于 int 型数组a :元素a[i]的地址 = 数组名 + i ×2欲访问元素的下标数组a是int型,int型占2个字节一维数组元素的访问方式访问过程:系统在访问数组元素时,首先根据数组名和下标计算出该元素的地址,然后从该地址所指向的内存单元取数据或向该地址所指向的内存单元存数据。数组元素的访问方式:数组名[要访问的元素的下标]如:int fnum[10]; fnum[0] = 0;一维数组元素的访问方式sizeof 运算符:单目运算符用法:sizeof(数据或数据类型)作用:返回以字节为单位的指定的数据或类型的长度如: sizeof(int) 返回2sizeof(float) 返回4int a[10]; sizeof(a) 返回20一维数组中,下标为 i 的元素的地址 =数组名+i ×sizeof(ArrayType)根据数组名和下标计算元素地址的公式是:关于数组名数组名包含数组的首地址信息。数组首地址即数组中第一个元素的地址。数组名本身并不是变量(虽然也可以把它理解为一个地址常量),所以它并不占据内存单元,是不可寻址的。就是说:在程序中,数组名可以出现在部分类型的表达式中,也可以利用数组名手工计算数组元素的地址。但是,由于上述原因,数组名不可能被改变,即数组名不可能被赋值。说明:关于数组名准确的说:C语言中,数组名就是一个地址常量(指针常量)。int fnum[5];fnum = {0, 1, 2, 3, 4};fnum = 0x1000;fnum = &fnum[0];fnum ++; -- fnum;因此,以下的用法是错误的:关于数组名准确的说:C语言中,数组名就是一个地址常量(指针常量)。int fnum[5];fnum; /* 等于&a[0] */fnum + 0; /* 等于&a[0] */fnum + 1; /* 等于&a[1] */fnum + 2; /* 等于&a[2] */而以下的用法是正确的:一维数组的初始化定义同时进行初始化:将初值放在一对大括号中对所有元素赋初值。如 int a[5] = {0, 1, 2, 3, 4};对部分元素赋初值。如 int a[10] = {0, 1, 2, 3, 4}; 前5个元素有初值若定义数组时省略数组长度,则系统根据初值的个数自动确定数组元素个数。如 int a[ ] = {0, 1, 2, 3, 4};相当于 int a[5] = {0, 1, 2, 3, 4};int a[5]; /* 正确的访问方法 */a[0] = 0; a[1] = 1;a[2] = 2; a[3] = 3;a[4] = 4;一维数组的初始化2. 定义之后进行初始化。注意:只能访问单个的数组元素。int a[5]; /* 错误的访问方法 */a = {0, 1, 2, 3, 4};a[5] = {0, 1, 2, 3, 4};int a[5], i;for (i = 0; i < 5; i ++)a[i] = 0;for (i = 0; i < 5; i ++)a[i] = i;一维数组的初始化数组的初始化和数组元素的访问通常都利用循环完成一维数组的初始化和元素的引用:例例:一维数组的初始化和元素的引用void main(){int i, a[10];for(i = 0; i < 10; i ++)a[i] = i;for(i = 9; i >= 0; i - -)printf(“%d\n”, a[i]);return;}运行结果:9876543210一维数组例7-1 :题目及分析例:求 Fibonacci 数列的前40个数。该数列满足递推公式:f1= 1, f2 = 1, fn = fn-1 + fn-2分析:在已知f1和f2的时候,从第3个数据开始,每个数据都只与前面相邻的两个数据有关。如果定义一个具有40个元素的数组,则利用递推公式和循环很容易得到数列的前40个数,即:f[1]=1, f[2]=1, f[n]=f[n-2]+f[n-1]一维数组例7-1 :实现代码#include void main(){int n; int f[40] = {1, 1};for(n = 2; n < 40; n ++)f[n] = f[n-2] + f[n-1];for(n = 0; n < 40; n ++){printf(“12d”, f[n]);if(n % 5 == 0) printf(“\n”);}}定义一个有40个元素的数组,前2个元素的初值为1。一维数组例7-1 :实现代码#include void main(){int n; int f[40] = {1, 1};for(n = 2; n < 40; n ++)f[n] = f[n-2] + f[n-1];for(n = 0; n < 40; n ++){printf(“12d”, f[n]);if(n % 5 == 0) printf(“\n”);}}利用循环和递推公式求出余下的38个数。用变化的循环变量做下标,可以访问每一个数组元素。一维数组例7-1 :实现代码#include void main(){int n; int f[40] = {1, 1};for(n = 2; n < 40; n ++)f[n] = f[n-2] + f[n-1];for(n = 0; n < 40; n ++){printf(“12d”, f[n]);if(n % 5 == 0) printf(“\n”);}}再利用循环输出这40个数,每行输出5个。例:将一个数组中的值按逆序重新存放。例如,原来顺序为8,6,5,4,1,要求改为1,4,5,6,8。一维数组例7-2:题目及分析分析:8a[0]a[1]a[2]a[4]a[3]6541算法:数组从首尾向中间,对应位置上的元素交换值。直到交换到中间的元素为止。1a[0]a[1]a[2]a[4]a[3]4568#include void main(){int i, j, n=5, t; int a[5]={8, 6, 5, 4, 1};printf(“Array before inverted is: ”);for(i=0; ifor(i=0; i{j=n–i–1;t=a[i]; a[i]=a[j]; a[j]=t;}printf(“Array after inverted is: ”);for(i=0; i}一维数组例7-2 :实现代码输出逆序存放前的数组元素。#include void main(){int i, j, n=5, t; int a[5]={8, 6, 5, 4, 1};printf(“Array before inverted is: ”);for(i=0; ifor(i=0; i{j=n–i–1;t=a[i]; a[i]=a[j]; a[j]=t;}printf(“Array after inverted is: ”);for(i=0; i}一维数组例7-2 :实现代码循环终值控制交换停止的地方。#include void main(){int i, j, n=5, t; int a[5]={8, 6, 5, 4, 1};printf(“Array before inverted is: ”);for(i=0; ifor(i=0; i{j=n–i–1;t=a[i]; a[i]=a[j]; a[j]=t;}printf(“Array after inverted is: ”);for(i=0; i}一维数组例7-2 :实现代码求出要和a[i]交换的元素的下标。#include void main(){int i, j, n=5, t; int a[5]={8, 6, 5, 4, 1};printf(“Array before inverted is: ”);for(i=0; ifor(i=0; i{j=n–i–1;t=a[i]; a[i]=a[j]; a[j]=t;}printf(“Array after inverted is: ”);for(i=0; i}一维数组例7-2 :实现代码交换a[i]和a[j]的值。#include void main(){int i, j, n=5, t; int a[5]={8, 6, 5, 4, 1};printf(“Array before inverted is: ”);for(i=0; ifor(i=0; i{j=n–i–1;t=a[i]; a[i]=a[j]; a[j]=t;}printf(“Array after inverted is: ”);for(i=0; i}一维数组例7-2 :实现代码输出逆序存放后的数组元素。◆ 在定义int a[10];之后,对a的引用正确的是( ) 。(A) a[10] (B) a[6.3](C) a(6) (D) a[10-10]课堂练习:关于数组的定义答案: (D)7.2 二维数组的定义和引用二维数组的定义定义方法:类型符 数组名[常量表达式][常量表达式]说明:① 数组名的命名规则与变量名的命名规则相同。如: float score[40][9];char name[40][10];二维数组的定义② 两对方括号表示二维数组;方括号中只能是常量表达式,不能是变量。C语言不允许定义动态数组。常量表达式是指:直接常量或符号常量或由它们组成的表达式。int n1, n2;scanf(“%d,%d”, &n1, &n2);int fnum[n1][n2];说明:二维数组的定义③ 数组元素的个数即数组的长度由两个方括号中的常量确定。对于二维数组array[n1][n2],数组元素的个数是n1×n2,有效的元素下标范围是:[0][0]~[n1–1][n2-1]。说明:比如 float score[40][9];则该数组有score[0][0]~score[39][8]共40×9=360个元素。二维数组元素的引用C语言规定:同变量一样,数组也必须 “先定义,后使用”。C语言规定:只能单个的引用某一个数组元素,而不能一次引用整个数组。数组中的每一个元素都相当于一个与数组同类型的普通变量。比如: float score[40][9];则该数组有score[0][0]~score[39][8]共360个元素,就相当于360个普通的整型变量。二维数组元素的引用二维数组元素的引用方法:数组名[下标1][下标2]其中,下标1、2可以是常量、变量或表达式。定义数组:float score[40][9], n = 1;引用数组元素:score[0][0] = 1; /* 下标是常量 */score[n][n] = 1; /* 下标是变量 */score[0][n+1] = score[score[0][0]][1];/* 下标是表达式或数组元素 */二维数组元素的引用在引用数组元素时,编程者一定要确保下标在有效范围内。 C语言编译器本身并不进行下标越限的检查。如果二维数组定义为 array[n1][n2],则有效的元素下标范围是[0][0]~[n1–1][n2-1]。注意:比如: float score[40][9];则该数组中不存在像 score[-1][-1] 或score[40][9] 这样的非法元素。引用非法的数组元素:score[-1][-1] = 70; /* 下标越下限 */score[40][9] = 69.5; /* 下标越上限 */二维数组元素的引用定义数组:float score[40][9], n = 1;引用合法的数组元素:score[0][0] = 60;score[1][1] = 90;score[39][8] = 70.5;虽然越限了,但是编译时并不给出出错信息。引用数组元素时,确保下标在有效范围的任务由编程者完成。二维数组的存储方式数组在内存中占据连续的字节单元;整个数组占据的字节单元数等于数组元素个数乘以该数组所属数据类型的数据占据的字节单元数。数组元素在内存中按顺序连续存放。C语言把二维数组理解为一个特殊的一维数组。这个一维数组的每一个元素又是一个一维数组。二维数组的存储方式比如:float a[3][3];aa[0]a[1]a[2]a[0][0] a[0][1] a[0][2]a[1][0] a[1][1] a[1][2]a[2][0] a[2][1] a[2][2]C语言把二维数组a理解为一个一维数组a[3],它有3个元素,分别是a[0]、a[1]、a[2]。而这三个元素不是直接的等价于同类型变量的元素,而又是一个一维数组,每个又都包含4个元素。二维数组的存储方式比如:float a[3][3];aa[0]a[1]a[2]a[0][0] a[0][1] a[0][2]a[1][0] a[1][1] a[1][2]a[2][0] a[2][1] a[2][2]数组名包含数组的首地址信息,即数组名指向数组的第一个元素。因此,对于上述二维数组a,数组名 a 表示数组的首地址,即数组第一个元素的地址。而a[0]、a[1]、a[2]由于分别代表三个一维数组,所以它们也是数组名,也包含地址信息。二维数组的存储方式a[0]a[1]a[2]0x10000x10060x100C=a[0] =&a[0][0]=a[1] =&a[1][0]=a[2] =&a[2][0]比如:int a[3][3];a数组名 aa[0]&a[0][0]0x1000a[0][0]a[0][1]a[0][2]a[1][0]a[1][1]a[1][2]a[2][0]a[2][1]a[2][2]元素 ArrayName[i][j] 的地址=ArrayName +i × n1 × sizeof(ArrayType) +j × sizeof (ArrayType)二维数组元素的访问方式现在来看:对二维数组,系统如何根据数组名和给定的两个下标找到指定元素的地址?对于已定义的二维数组:ArrayType ArrayName[n1][n2];二维数组元素的访问方式例如:定义二维数组 int a[3][3];若:&a[0][0] = 0x1000,则:&a[0][1] = 0x1000 + 0×3×2 + 1×2= 0x1002&a[2][1] = 0x1000 + 2×3×2 + 1×2= 0x100E元素 ArrayName[i][j] 的地址=ArrayName +i × n1 × sizeof(ArrayType) +j × sizeof (ArrayType)二维数组的初始化定义同时进行初始化:将初值放在一对大括号中对所有元素赋初值。如:int a[2][3]={{0,1,2}, {3,4,5}};或:int a[2][3]={0,1,2,3,4,5};2. 定义之后进行初始化。注意:只能访问单个的数组元素。如:int a[3][2];a[0][0]=a[0][1]=a[1][0]=0;a[1][1]=a[2][0]=a[2][1]=0;二维数组的初始化int a[3][2], i, j;for (i = 0; i < 3; i ++)for (j = 0; j < 2; j ++)a[i][j] = 0; /*初始化*/for (i = 0; i < 3; i ++)for (j = 0; j < 2; j ++)a[i][j] = i*j; /*访问元素*/二维数组的初始化和数组元素的访问通常都利用循环完成。7.3 字符数组字符数组的定义和引用字符数组的定义方法:一维字符数组:char 数组名[常量表达式]二维字符数组:char 数组名[常量表达式][常量表达式]如:char OneStuName[10];char AllStuName[40][10];字符数组是用来存放字符型数据的数组。字符数组的每一个元素都相当于一个字符型的变量,可以保存一个字符。字符数组的定义和引用字符数组元素的引用一维字符数组: 数组名[下标]二维字符数组: 数组名[下标1][下标2]如:OneStuName[0] = ‘L’;AllStuName[0][0] = ‘L’;注意:字符数组的每个元素只能保存一个字符型的数据。字符数组的初始化定义同时进行初始化:将初值放在一对大括号中如:char str[10] = {‘h’, ‘e’, ‘l’, ‘l’, ‘o’};如:char name[10] = {‘L’, ‘C’, ‘S’};若定义数组时省略数组长度,则系统自动根据给出的初值的个数确定数组长度。如:char name[ ] = {‘L’, ‘C’, ‘S’};等价于: char name[3] = {‘L’, ‘C’, ‘S’};字符数组的初始化2. 定义之后进行初始化。注意:只能访问单个的数组元素。如:char name[10];name[0] = ‘L’;name[1] = ‘C’;name[2] = ‘S’;注意:字符数组的每个元素只能保存一个字符型的数据。所以:char name[10];name[0] = “LCS”; 是错误的。用字符数组处理字符串C语言没有专门的字符串变量,是用字符数组来处理字符串变量的。字符串结束标志字符:‘\0’对于字符串常量:系统自动加 ‘\0’对于字符数组:如果要保存字符串,必须手工加‘\0’;因为系统根据该字符来判断字符串是否结束,而不是根据字符数组的长度来决定的。就是说,有可能字符数组的长度大于实际字符串的长度。用字符数组处理字符串注意:一个字符数组并不是非要保存一个字符串(即并不是一定要加上字符串结束标志字符 ‘\0’)。但是,只要加了‘\0’字符,我们就可以把字符数组按字符串的方式处理(即可以对字符数组进行整体的输入和输出操作)。用字符数组处理字符串 如何将一个字符串保存在一个字符数组中方法一:使用代码手工在对字符数组初始化时或对数组元素赋值时加上字符串结束标志字符 ‘\0’;如:char name[ ]={‘L’, ‘C’, ‘S’, ‘\0’};等价于:char name[4]={‘L’, ‘C’, ‘S’, ‘\0’};或:char name[10] = {‘L’, ‘C’, ‘S’, ‘\0’};用字符数组处理字符串 如何将一个字符串保存在一个字符数组中方法一:使用代码手工在对字符数组初始化时或对数组元素赋值时加上字符串结束标志字符 ‘\0’;如:char name[10];name[0] = ‘L’; name[1] = ‘C’;name[2] = ‘S’; name[3] = ‘\0’;用字符数组处理字符串 如何将一个字符串保存在一个字符数组中方法二:更常用的方式是直接用字符串常量来初始化字符数组。但是要注意:这种方法 只能在定义数组时使用。正确:char name[10] = “LCS”;正确:char password[10] = “swufe”;错误:char name[10];name = “LCS”;char name[10] = “”;name[0] = getchar();或者:scanf(“%c” , &name[1]);字符数组及字符串的单个输入1. 单个输入:分别输入每个元素。(不常用)%c 表示要输入一个字符所以此处给出指定的一个数组元素的地址char name[10] = “”;scanf(“%s” , name);字符数组及字符串的整体输入2. 整体输入:一次输入整个字符串。(常用)%s 表示要输入一个字符串所以此处给出指定的整个数组的首地址而像下面的用法是错误的:scanf(“%s” , &name);字符数组及字符串的单个输出1. 单个输出:在已知字符数组长度或确知保存的是字符串的情况下,利用不同循环控制方式依次处理每个数组元素。char str[10]={‘h’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’};int i=0;/* 利用字符数组长度控制 */for (i = 0; i < 10; i ++)printf(“%c”, str[i]);用数组长度来控制循环结束显然存在一个问题:可能会输出一些位于结束标志字符‘\0’ 之后多余的无用字符。 char str[10]={‘h’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’};int i=0;/* 用‘\0’ 控制循环结束 */while (str[i] != ‘\0’){ printf(“%c”, str[i]); i ++; }字符数组及字符串的单个输出由于每个字符串最后必然有一个结束标志字符‘\0’,所以可以用它来控制循环结束,而不管字符数组有多长。这种循环控制方式只能用在确定字符数组中保存的是字符串的情况下%c要输出一个字符要输出的元素char greeting[81]=‘‘hello, Welcome”;printf(“%s”, greeting);字符数组及字符串的整体输出2. 整体输出:在确定字符数组中保存的是字符串的情况下,利用%s格式字符一次输出整个字符串。%s 表示要输出一个字符串此处给出的是数组的名字为什么在%s格式字符控制下,给出数组名就能实现字符串整体输出呢?实际的内部输出过程是怎样的呢? 字符数组及字符串的整体输出别忘了:数组名中包含数组的首地址信息,即数组名是一个指针常量,它指向数组的第一个元素。因此,当用%s格式字符同时给出数组名的情况下,其内部执行的输出过程是:实际上是一个循环输出过程,即:从给定的地址(数组名中包含)开始,每次取出一个字符,将其和字符串结束标志字符‘\0’进行对比。如果不是结束标志字符,就输出它,然后取下一个字符继续对比;如果是结束标志字符,就结束输出。int i = 0;while (greeting[i] != ‘\0’){printf(“%c”, greeting[i]);i ++;}字符数组及字符串的整体输出printf(“%s”, greeting);等 价检查当前字符是否是结束标志字符‘\0’:不是,就执行循环体;是,就结束循环输出当前字符准备取出下个字符◆ 下面的语句段的输出结果是( ) 。char str[81] = “hello,world”;printf(“%s”, str + 6);(A) hello,world (B) w(C) world (D) ,world课堂练习:字符串的输出答案: (C)字符串处理函数系统库函数,对应的头文件是 string.h 。凡是使用了这些字符串处理系统库函数,必须在源程序开头使用如下的预处理命令:#include 对字符串处理函数要求:掌握这些函数的功能及用法(函数原型、调用参数、返回值等)能自己编写代码,实现这些函数的功能(关键在于 ‘\0’ )常用字符串处理函数常用字符串处理系统库函数功能 函数名 功能 函数名字符串 输出 puts 字符串 输入 gets字符串 拷贝 strcpy 字符串 连接 strcat字符串 比较 strcmp 字符串 长度 strlen字符串 大写 strupr 字符串 小写 strlwr字符串输出函数 puts1. 原型:int puts(const char *str);2. 参数:常指针类型的数据,可以是字符串常量,字符数组名或字符指针变量名。3. 功能:从给定的地址开始,输出一个以 ‘\0’字符结尾的字符串str到显示器上,最后再输出一个回车换行字符。4. 用法:puts(字符串常量或字符数组名或字符指针变量名);5. 返回值:一个非负整数。绝大多数情况下不使用该返回值。字符串输出函数 putschar greeting[81]=‘‘hello,Welcome”;printf(“%s”, greeting);/*字符数组名作参数*/puts(greeting);/*字符串常量作参数*/puts(“Hello, Welcome”);/*字符数组名+地址偏移量作参数*/puts(greeting+6);字符串输出函数 puts自己用代码编写 puts 函数的功能:int i = 0;while (greeting[i] != ‘\0’){printf(“%c”, greeting[i]); i ++;}printf(“\n”);字符串输入函数 gets1. 原型:char *gets(char *str);2. 参数:指针类型的数据,可以是字符数组名或指针变量名。3. 功能:从键盘接收用户键入的以回车键结束的字符序列,把它存放到由指定的地址(由 str指定)开始的内存单元去。4. 用法:gets(字符数组名或指针变量名);5. 返回值:指向字符串str的指针。字符串输入函数 getschar name[80] = “”;printf("Input your name:");/*字符数组名作参数*/gets(name);printf(“Your name is : ”);puts(string);注意:用gets接收的字符串中可以包含空格,但是用scanf加%s格式字符接收的字符串中却不能包含空格。字符串输入函数 gets自己用代码编写 gets 函数的功能:char name[81] = “”;int i = 0; char ch;ch = getchar();while (ch != ‘\n’){name[i] = ch; i ++;ch = getchar();}字符串输入函数 gets自己用代码编写 gets 函数的功能:char name[81] = “”;int i = 0; char ch;while ((ch = getchar()) != ‘\n’){name[i] = ch; i ++;}字符串拷贝函数 strcpy1. 原型:char *strcpy(char *dest, const char *src);2. 参数:两个。第一个:目标字符串,指针类型的数据,可以是字符数组名或指针变量名。第二个:源字符串,常指针类型的数据,可以是字符串常量或字符数组名或指针变量名。字符串拷贝函数 strcpy1. 原型:char *strcpy(char *dest, const char *src);3. 功能:将源字符串src的全部字符拷贝到目标字符串dest中。目标字符串中的原有内容被覆盖。4. 用法:strcpy(字符数组名或字符指针变量名, 字符串常量或字符数组名或指针变量名);5. 返回值:指向目标字符串dest的指针。字符串拷贝函数 strcpychar OldName[80] = “”;char NewName[80] = “”;/* 把字符串常量拷贝到字符数组中*/strcpy(NewName, “WPSOffice”);strcpy(OldName, “WPS”);/* 把字符数组拷贝到另一个字符数组中*/strcpy(OldName, NewName);目标字符数组的长度应该足够大字符串拷贝函数 strcpy自己用代码编写 strcpy 函数的功能:int i = 0;while (NewName[i] != ‘\0’){OldName[i] = NewName[i]; i ++;}NewName[i] = ‘\0’;…dest…src…字符串拷贝的实际过程:0char OldName[80] = “”;char NewName[80] = “”;strcpy(NewName, “WPSOffice”);strcpy(OldName, “WPS”);strcpy(OldName, NewName);字符串拷贝函数 strcpy注意:当用字符数组保存和处理字符串时,如果想在字符串之间赋值,只能使用系统库函数strcpy,而不能直接使用赋值运算符。char OldName[80] = “”;char NewName[80] = “”;NewName = “WPSOffice”;OldName = “WPS”;OldName = NewName;字符串拷贝函数 strcpy注意:当用字符数组保存和处理字符串时,如果想在字符串之间赋值,只能使用系统库函数strcpy,而不能直接使用赋值运算符。因为数组名是指针常量,而常量不可能被赋值,所以是错误的。字符串连接函数 strcat1. 原型:char *strcat(char *dest, const char *src);2. 参数:两个。第一个:目标字符串,指针类型的数据,可以是字符数组名或指针变量名。第二个:源字符串,常指针类型的数据,可以是字符串常量或字符数组名或指针变量名。3. 功能:将源字符串src中的全部字符连接到目标字符串dest的末尾。目标字符串原有内容仍存在。字符串连接函数 strcat原型:char *strcat(char *dest, const char *src);4. 用法:strcat(字符数组名或字符指针变量名, 字符串常量或字符数组名或指针变量名);5. 返回值:指向目标字符串dest的指针。字符串连接函数 strcatchar OldName[80] = “WPS”;char NewName[80] = “”;/* 把字符串常量连接到字符数组中*/strcat(NewName, “Office”);/* 把字符数组连接到另一个字符数组中*/strcat(NewName, OldName);目标字符数组的长度应该足够大字符串连接函数 strcat自己用代码编写 strcat 函数的功能:…0…dest…src…字符串连接的实际过程:0字符串连接函数 strcat自己用代码编写 strcat 函数的功能:int i = 0, j = 0;while (NewName[i] != ‘\0’) i ++;while (OldName[j] != ‘\0’){NewName[i] = OldName[j];i ++; j ++;}NewName[i] = ‘\0’;strcat(NewName, OldName); 等价于字符串比较函数 strcmp1. 原型:char *strcmp(const char *s1,const char *s2);2. 参数:两个。常指针类型的数据,可以是字符串常量或字符数组名或指针变量名。3. 功能:将字符串s1与字符串s2进行比较。根据函数返回值可以判断s1和s2的大小。字符串比较函数 strcmp原型:char *strcmp(const char *s1,const char *s2);4. 用法:strcmp(字符串常量或字符数组名或字符指针变量名, 字符串常量或字符数组名或字符指针变量名);5. 返回值:若 s1 > s2,返回值 > 0 若 s1 = s2,返回值 = 0 若 s1 < s2,返回值 < 0字符串比较函数 strcmpchar str1[81] = “hello”;char str2[81] = “world”;int result;result = strcmp(str1, str2);if (result > 0)printf(“str1 > str2”);else if (result == 0)printf(“str1 = str2”);else if (result < 0)printf(“str1 < str2”);保存字符串比较函数的返回值。根据该返回值判断两个字符串的大小。字符串比较函数 strcmp…s1…s2…字符串比较的实际过程:直到比较出结果为止提示:字符之间的大小比较实际上是在比较它们的ASCII码的大小。所谓字符串的大小比较,实际上是两个字符串对应位置上的字符进行大小比较。字符串比较函数 strcmp注意:如果想比较字符串之间的大小,只能使用strcmp系统库函数,而不能直接使用关系运算符。char str1[81] = “hello”;char str2[81] = “world”;if (strcmp (str1, str2) > 0)printf (“str1 > str2”);if (strcmp (str1, str2) == 0)printf (“str1 equals to str2”);字符串比较函数 strcmp注意:如果想比较字符串之间的大小,只能使用strcmp系统库函数,而不能直接使用关系运算符。char str1[81] = “hello”;char str2[81] = “world”;if (str1 > str2)printf (“str1 > str2”);if (str1 == str2)printf (“str1 equals to str2”);字符串长度函数 strlen1. 原型:unsigned int strlen(const char *str);2. 参数:一个常指针类型的数据,可以是字符串常量或字符数组名或字符指针变量名。3. 功能:求指定字符串str的长度(str 中不包括结束标志字符在内的字符的个数)。4. 用法:strlen(字符串常量或字符数组名或字符指针变量名);5. 返回值:str中不包括结束标志字符在内的字符的个数。字符串长度函数 strlenchar NewName[80] = “WPS2000”;unsigned int slen = 0;slen = strlen(NewName);/* slen 等于 7 */slen = strlen(“Office 2001”);/* slen 等于 11 */字符串长度函数 strlen自己用代码编写 strlen 函数的功能:unsigned int slen = 0;while (NewName[slen] != ‘\0’)slen ++;/*slen保存的就是字符串NewName的长度*/printf(“The length is %u”, slen);字符串小写函数 strlwr1. 原型:char *strlwr(char *str);2. 参数:一个指针类型的数据,可以是字符数组名或指针变量名。3. 功能:将指定字符串str中的大写字母(A-Z)转换成对应的小写字母(a-z),其它字符保持不变。4. 用法:strlwr(字符数组名或指针变量名);5. 返回值:指向字符串str的指针。字符串小写函数 strlwrchar NewName[80] = “WPSOffice”;strlwr(NewName);puts(NewName); /* 输出 wpsoffice */strcpy(NewName, “OFFICE2001”);strlwr(NewName);puts(NewName); /* 输出 office2001 */自己用代码编写 strlwr 函数的功能:字符串大写函数 strupr1. 原型:char *strupr(char *str);2. 参数:一个指针类型的数据,可以是字符数组名或指针变量名。3. 功能:将指定字符串str中的小写字母(a-z)转换成对应的大写字母(A-Z),其它字符保持不变。4. 用法:strupr(字符数组名或指针变量名);5. 返回值:指向字符串str的指针。字符串大写函数 strupr自己用代码编写 strupr 函数的功能:char NewName[80] = “Office”;strupr(NewName);puts(NewName); /* 输出 OFFICE */strcpy(NewName, “Word2001”);strupr(NewName);puts(NewName); /* 输出 WORD2001 */课后及上机作业1. 课本p186 习题7.1,7.2,7.6,7.14,7.152. 自己编写代码实现函数 strlwr 的功能。3. 自己编写代码实现函数 strupr 的功能。结束页今天就讲到这里再见!(共37张PPT)第3章 数据类型、运算符与表达式数据类型常量与变量不同类型数据间的转换运算符和表达式3.1 程序设计概述一个程序应包括对数据的描述和对数据处理的描述。 1.对数据的描述,即数据结构。数据结构是计算机学科的核心课程之一,有许多专门著作论述,本课程就不再赘述。 在C语言中,系统提供的数据结构,是以数据类型的形式出现的。 2.对数据处理的描述,即计算机算法。算法是为解决一个问题而采取的方法和步骤,是程序的灵魂。为此,著名计算机科学家沃思(Nikiklaus Wirth)提出一个公式: 数据结构 + 算法 = 程序 实际上,一个程序除了数据结构和算法外,还必须使用一种计算机语言,并采用结构化方法来表示。[Return]3.2 C语言的数据类型类型是计算机科学的核心概念之一。C基本数据类型包括字符/整数/实数类型等。同一类型的所有数据对象性质相同,采用统一书写形式,同样编码方式,能做同样操作。程序里的数据都属于特定类型。硬件也把数据分成类型:整数、浮点数类型等,对每个类型有一组指令。各种基本类型用定长二进制编码表示。这确定了该类型数据的可能表示范围。数据类型总表C数据类型基本类型构造类型指针类型空类型void定义类型typedef字符类型char枚举类型enum整 型实型单精度型float双精度型double数组结构体struct共用体union短整型short长整型long整型int数据类型决定:1. 数据占内存字节数2. 数据取值范围3. 其上可进行的操作基本数据类型类型符号关键字数的表示范围所占位数整型字符型实型有无(signed)int16-32768~32767(signed)short16-32768~32767(signed)long32-2147483648~214748364716unsigned int0~65535320~4294967295unsigned longunsigned short160~65535有float323.4e-38~3.4e38有double641.7e-308~1.7e308有char8-128~127无unsigned char80~255说明:数据类型所占字节数随机器硬件不同而不同,上表以IBM PC机为例:3.3 常量与变量标识符定义:用来标识变量、常量、函数等的字符序列组成:只能由字母、数字、下划线组成,且第一个字母必须是字母或下划线大小写敏感不能使用关键字长度:最长32个字符命名原则:见名知意不宜混淆 如l与I, o与0例:判断下列标识符号合法性sum Sum M.D.John day Date 3daysstudent_name #33 lotus_1_2_3char a>b _above $123M.D.John3days#33char$123a>b一般用大写字母是宏定义预处理命令,不是C语句直接常量:整型常量实型常量字符常量字符串常量如 #define PRICE 30常量定义:程序运行时其值不能改变的量(即常数)分类:符号常量:用标识符代表常量定义格式: #define 符号常量 常量例 符号常量举例(ch2_1.c)#define PRICE 30main(){int num,total;num=10;total=num*PRICE;printf("total=%d",total);}运行结果:total=300整型常量(整常数)三种形式:十进制整数:由数字0~9和正负号表示.如 123,-456,0八进制整数:由数字0开头,后跟数字0~7表示.如0123,011十六进制整数:由0x开头,后跟0~9,a~f,A~F表示. 如0x123,0Xff例 12 与 12L例 30000 为int型65536 为long int 型整型常量的类型根据其值所在范围确定其数据类型在整常量后加字母l或L,认为它是long int 型常量问题:0123 = ( )100x123 = ( )100Xff = ( )1083291255问题:0123 = ( )100x123 = ( )100Xff = ( )10实型常量(实数或浮点数)表示形式:十进制数形式:(必须有小数点) 如0.123, .123, 123.0, 0.0, 123.指数形式:(e或E之前必须有数字;指数必须为整数)如12.3e3 ,123E2, 1.23e4 , e-5 , 1.2E-3.5实型常量的类型默认double型在实型常量后加字母f或F,认为它是float 型字符常量定义:用单引号括起来的单个普通字符或转义字符.字符常量的值:该字符的ASCII码值<转义字符及其含义:转义字符含义\n\v\r\a\‘\ddd\t\b\f\\\“\xhh转义字符含义换行垂直制表回车响铃单引号3位8进制数代表的字符水平制表退格换页反斜线双引号2位16进制数代表的字符如 ‘\101’ -----------‘A’ ‘\012’ -----------’\n’‘\376’ -----------’ ’ ‘\x61’ -----------’a’‘\60’ -----------’0’ ‘\483’ ----------( )例:‘A’-------’\101’-------’\x41’--------65如 ‘A’——65, ‘a’——97,‘0’——48 , ‘\n’——10如 ‘a’ ‘A’ ‘ ’ ‘\n’ ‘\101’转义字符:反斜线后面跟一个字符或一个代码值表示例 转义字符举例(ch2_001.c,ch2_004.c)main(){printf("\101 \x42 C\n");printf("I say:\"How are you \"\n");printf("\\C Program\\\n");printf("Turbo \'C\'");}运行结果:(屏幕显示)A B CIsay:”How are you ”\C Program\Turbo ‘C’字符常量与字符串常量不同字符串常量定义:用双引号(“”)括起来的字符序列存储:每个字符串尾自动加一个 ‘\0’ 作为字符串结束标志h e l l o \0例 字符串“hello”在内存中aa \0例 ‘a’“a”例 空串 “”\0例: char ch;ch=“A”;例: char ch;ch=‘A’;编译程序根据变量定义为其分配指定字节的内存单元…...地址int a=1, b=-3,c;abc2字节2字节2字节地址地址…...内存1-3 随机数变量概念:其值可以改变的量变量名与变量值变量定义的一般格式:数据类型 变量1[,变量2,…,变量n];变量初始化:定义时赋初值例:int a,b,c;float data;决定分配字节数和数的表示范围合法标识符例:int a=2,b,c=4;float data=3.67;char ch=‘A’;int x=1,y=1,z=1;int x=y=z=1;变量的使用:先定义,后使用例1int student;stadent=19; //Undefined symbol ‘statent’ in function main例2float a,b,c;c=a%b; //Illegal use of floating point in function main变量定义位置:一般放在函数开头main(){ int a,b=2;float data;a=1;data=(a+b)*1.2;printf(“data=%f\n”,data);}变量定义可执行语句main(){ int a,b=2;a=1;float data;data=(a+b)*1.2;printf(“data=%f\n”,data);}整型变量占字节数随机器不同而不同,一般占一个机器字short≤int≤long可用sizeof(类型标识符)测量实型变量float:占4字节,提供7位有效数字double:占8字节,提供15~16位有效数字字符型变量字符变量存放字符ASCII码char与int数据间可进行算术运算例 float a;a=111111.111; /* a=111111.1*/double b;b=111111.111; /* b=111111.111*/例 a=‘D’; /* a=68; */x=‘A’+5; /* x=65+5; */s=‘!’+‘G’ /* s=33+71; */没有字符串变量,用字符数组存放例 /*ch2_003.c*/#define PRICE 12.5main(){ int num=3;float total;char ch1,ch2=‘D’;total=num*PRICE;ch1=ch2-‘A’+‘a’;printf(“total=%f,ch1=%c\n”,total,ch1);}宏定义变量定义输出结果运行结果:total=37.500000, ch1=d3.4 不同类型数据间的转换隐式转换什么情况下发生运算转换------不同类型数据混合运算时赋值转换------把一个值赋给与其类型不同的变量时输出转换------输出时转换成指定的输出格式函数调用转换------实参与形参类型不一致时转换运算转换规则:不同类型数据运算时先自动转换成同一类型doublefloatlongunsignedintchar,short低高说明:必定的转换运算对象类型不同时转换例 char ch;int i;float f;double d;ch/i + f*d - (f+i)intintdoubledoubledoubledoubledoubledoubleintintdoubledoubledoubledoubledoubledouble10+‘a’ +i*f - d/l例 int i;float f;double d;long l;显式转换(强制转换)一般形式:(类型名)(表达式)例 (int)(x+y)(int)x+y(double)(3/2)(int)3.6说明:强制转换得到所需类型的中间变量,原变量类型不变例 main(){ float x;int i;x=3.6;i=(int)x;printf(“x=%f,i=%d”,x,i);}结果:x=3.600000,i=3精度损失问题较高类型向较低类型转换时可能发生3.5 运算符和表达式C运算符算术运算符:(+ - * / % ++ --)关系运算符:(< <= == > >= !=)逻辑运算符:((! && ||)位运算符 :(<< >> ~ | ^ &)赋值运算符:(= 及其扩展)条件运算符:( :)逗号运算符:(,)指针运算符:(* &)求字节数 :(sizeof)强制类型转换:(类型)分量运算符:(. ->)下标运算符:([])其它 :(( ) -)学习运算符应注意:运算符功能与运算量关系要求运算量个数要求运算量类型运算符优先级别结合方向结果的类型算术运算符和表达式基本算术运算符: + - * / %结合方向:从左向右优先级: - ---->* / % -----> + -(2) (3) (4)说明:“-”可为单目运算符时,右结合性两整数相除,结果为整数%要求两侧均为整型数据例 5/2 =-5/2.0 =例 5%2 =-5%2 =1%10 =5%1 =5.5%2例 5/2 = 2-5/2.0 = -2.5例 5%2 = 1-5%2 = -11%10 = 15%1 = 05.5%2 ( )自增、自减运算符++ --作用:使变量值加1或减1种类:前置 ++i, --i (先执行i+1或i-1,再使用i值)后置 i++,i-- (先使用i值,再执行i+1或i-1)例 j=3; k=++j;j=3; k=j++;j=3; printf(“%d”,++j);j=3; printf(“%d”,j++);a=3;b=5;c=(++a)*b;a=3;b=5;c=(a++)*b;//k=4,j=4//k=3,j=4//4//3//c=20,a=4//c=15,a=4自增、自减运算符++ --作用:使变量值加1或减1种类:前置 ++i, --i (先执行i+1或i-1,再使用i值)后置 i++,i-- (先使用i值,再执行i+1或i-1)说明:++ -- 不能用于常量和表达式,如5++,(a+b)++++ --结合方向: 自右向左优先级:- ++ -- ------>* / % ----->+ -(2) (3) (4)例 -i++ -(i++)i=3; printf(“%d”,-i++); //-3例 j+++k; (j++)+k;例 -i++i=3; printf(“%d”,-i++);赋值运算符和表达式简单赋值运算符符号: =格式: 变量标识符=表达式作用:将一个数据(常量或表达式)赋给一个变量复合赋值运算符种类:+= -= *= /= %= 《= 》= &= ^= |=含义: exp1 op= exp2 exp1 = exp1 op exp2a+=3a=a+3x*=y+8x=x*(y+8)x%=3x=x%3例 a=3;d=func();c=d+2;说明:结合方向:自右向左优先级: 14左侧必须是变量,不能是常量或表达式赋值表达式的值与变量值相等,且可嵌套赋值转换规则:使赋值号右边表达式值自动转换成其左边变量的类型例 3=x-2*y;a+b=3;例 float f;int i;i=10;f=i;则 f=10.0例 int i;i=2.56; //结果i=2;例: a=b=c=5a=(b=5)a=5+(c=6)a=(b=4)+(c=6)a=(b=10)/(c=2)//表达式值为5,a,b,c值为5// b=5;a=5//表达式值11,c=6,a=11//表达式值10,a=10,b=4,c=6//表达式值5,a=5,b=10,c=2说明:结合方向:自右向左优先级: 12左侧必须是变量,不能是常量或表达式赋值表达式的值与变量值相等,且可嵌套赋值转换规则:使赋值号右边表达式值自动转换成其左边变量的类型例: a=12;a+=a-=a*a例: int a=2;a%=4-1;a+=a*=a-=a*=3;//a=-264 等价于a=a+(a=a-(a*a))//a=0 等价于a=a+(a=a*(a=a-(a=a*3)))逗号运算符和表达式形式:表达式1,表达式2,……表达式n结合性:从左向右优先级: 15逗号表达式的值:等于表达式n的值用途:常用于循环for语句中例 a=3*5,a*4a=3*5,a*4,a+5例 x=(a=3,6*3)x=a=3,6*a例 a=1;b=2;c=3;printf(“%d,%d,%d”,a,b,c);printf(“%d,%d,%d”,(a,b,c),b,c);//a=15,表达式值60//a=15,表达式值20//赋值表达式,表达式值18,x=18//逗号表达式,表达式值18,x=3//1,2,3//3,2,3逗号运算符和表达式形式:表达式1,表达式2,……表达式n结合性:从左向右优先级: 15逗号表达式的值:等于表达式n的值用途:常用于循环for语句中例:/*ch2_6.c*/#include main(){ int x,y=7;float z=4;x=(y=y+6,y/z);printf("x=%d\n",x);}运行结果:x=3关系运算符和表达式关系运算符种类:< <= == >= > !=结合方向:自左向右优先级别:<<=>>===!=优先级6(高)优先级7(低)例 c>a+b //c>(a+b)a>b!=c //(a>b)!=ca==ba=b>c //a=(b>c)关系表达式的值:是逻辑值“真”或“假”,用1和0表示例 int a=3,b=2,c=1,d,f;a>b(a>b)==cb+cd=a>bf=a>b>c//表达式值1//表达式值1//表达式值0//d=1//f=0关系运算注意:例 若a=0; b=0.5; x=0.3;则 a<=x<=b的值为0例 5>2>7>8在C中是允许的,值为0例 int i=1, j=7,a;a=i+(j%4!=0);则a=2例 ‘a’>0 结果为‘A’>100 结果为10关系运算注意:例 注意区分“=”与“==”int a=0,b=1;if(a=b)printf(“a equal to b”);elseprintf(“a not equal to b”);例 应避免对实数作相等或不等的判断如 1.0/3.0*3.0==1.0 结果为可改写为:fabs(1.0/3.0*3.0-1.0)<1e-60ab!a!ba&&ba||b真假真假假假真真逻辑运算符和表达式逻辑运算符种类: ! && ||逻辑运算真值表C语言中,运算量: 0表示“假”,非0表示“真”,运算结果: 0表示“假”,1表示“真”,真假假假假假真真假假真真真假真真! (2)&& (11)|| (12)高低例 a<=x && x<=ba>b&&x>ya==b||x==y!a||a>b优先级:结合方向:! :从右向左&& :从左向右|| :从左向右// (a<=x) && (x<=b)//(a>b)&&(x>y)//(a==b)||(x==y)//(!a)||(a>b)! (2)&& (11)|| (12)高低优先级:结合方向:! :从右向左&& :从左向右|| :从左向右例 a=4;b=5;!aa&&ba||b!a||b4&&0||25>3&&2||8<4-!0‘c’&&‘d’值为1值为0值为1值为1值为1值为1//(5>3)&&2||(8<(4-(!0))) 值为1! (2)&& (11)|| (12)高低优先级:结合方向:! :从右向左&& :从左向右|| :从左向右短路特性:逻辑表达式求解时,并非所有的逻辑运算符都被执行,只是在必须执行下一个逻辑运算符才能求出表达式的解时,才执行该运算符<>例 a&&b&&c //只在a为真时,才判别b的值;只在a、b都为真时,才判别 c的值例 a||b||c //只在a为假时,才判别b的值;只在a、b都为假时,才判别 c的值例 a=1;b=2;c=3;d=4;m=1;n=1;(m=a>b)&&(n=c>d)//结果m=0,n=1条件运算符与表达式一般形式: expr1 expr2 : expr3执行过程功能:相当于条件语句,但不能取代一般if语句例 if (a>b)printf(“%d”,a);elseprintf(“%d”,b);printf(“%d”,a>b a:b);例 求 a+|b|printf(“a+|b|=%d\n”,b>0 a+b:a-b);expr1取expr2值取expr3值非0=0例 (a==b) ’Y’:’N’(x%2==1) 1:0(x>=0) x:-x(c>=‘a’ && c<=‘z’) c-’a’+’A’:c条件运算符可嵌套如 x>0 1:(x<0 -1:0)优先级: 13结合方向:自右向左如 a>b a:c>d c:d a>b a:(c>d c:d)expr1、expr2、expr3类型可不同,表达式值取较高的类型例 x ‘a’:‘b’ //x=0,表达式值为‘b’; x 0,表达式值为‘a’x>y 1:1.5 //x>y ,值为1.0; x01 11 11 11 11 11 11 11int型:最大:3276710 00 00 00 00 00 00 00最小:-3276811 11 11 11 11 11 11 11unsigned int型:最大:6553500 00 00 00 00 00 00 00最小:0数制基权表示数码特点10 ,10 ,10 ,…十进制数0~910逢十进一二进制数0~122 ,2 ,2 ,…逢二进一八进制数0~788 ,8 ,8 ,…逢八进一十六进制数0~9,A~F,a~f1616 ,16 ,16 ,…逢十六进一十进制:4956= 4 10 +9 10 +5 10 +6 10 二进制:1011=1 2 +0 2 +1 2 +1 2 十六进制:81AE=8 16 +1 16 +10 16 +14 16 八进制:4275=4 8 +2 8 +7 8 +5 8 (共37张PPT)第7章 数组构造数据类型之一数组:有序数据的集合,用数组名标识元素:属同一数据类型,用数组名和下标确定7.1 一维数组一维数组的定义定义方式: 数据类型 数组名[常量表达式];合法标识符表示元素个数下标从0开始[ ] :数组运算符单目运算符优先级(1)左结合不能用( )例 int a[6];a[0]0145a[1]a[2]a[3]a[4]a[5]23a编译时分配连续内存内存字节数=数组维数*sizeof(元素数据类型)数组名表示内存首地址,是地址常量一维数组的引用数组必须先定义,后使用只能逐个引用数组元素,不能一次引用整个数组数组元素表示形式: 数组名[下标]其中:下标可以是常量或整型表达式例 int i=15;int data[i]; ( 不能用变量定义数组维数)例 int a[10];printf(“%d”,a); ( )必须 for(j=0;j<10;j++)printf(“%d\t”,a[j]); ( )例 int data[5];data[5]=10; //C语言对数组不作越界检查,使用时要 注意一维数组的初始化初始化方式 在定义数组时,为数组元素赋初值(在编译阶段使之得到初值)int a[5]={1,2,3,4,5};等价于:a[0]=1; a[1]=2; a[2]=3; a[3]=4; a[4]=5;说明:数组不初始化,其元素值为随机数对static数组元素不赋初值,系统会自动赋以0值当全部数组元素赋初值时,可不指定数组长度如 int a[5]={6,2,3};等价于: a[0]=6; a[1]=2;a[2]=3; a[3]=0; a[4]=0;如 int a[3]={6,2,3,5,1}; ( )static int a[5];等价于:a[0]=0; a[1]=0; a[2]=0; a[3]=0; a[4]=0;只给部分数组元素赋初值int a[]={1,2,3,4,5,6};编译系统根据初值个数确定数组维数程序举例例 读10个整数存入数组,找出其中最大值和最小值步骤:1. 输入:for循环输入10个整数2. 处理:(a) 先令max=min=x[0](b) 依次用x[i]和max,min比较(循环)若max若min>x[i],令min=x[i]3. 输出:max和min#include #define SIZE 10main(){ int x[SIZE],i,max,min;printf("Enter 10 integers:\n");for(i=0;i{ printf("%d:",i+1);scanf("%d",&x[i]);}max=min=x[0];for(i=1;i{ if(maxif(min>x[i]) min=x[i];}printf("Maximum value is %d\n",max);printf("Minimum value is %d\n",min);}例 用数组求Fibonacci数列前20个数f[0]f[1]f[2]f[3]f[4]f[5]f[19]……...11f[19]01452319235#include main(){ int i;int f[20]={1,1};for(i=2;i<20;i++)f[i]=f[i-2]+f[i-1];for(i=0;i<20;i++){ if(i%5==0) printf("\n");printf("%12d",f[i]);}}例 用冒泡法对10个数排序排序过程:(1)比较第一个数与第二个数,若为逆序a[0]>a[1],则交换;然后比较第二个数与第三个数;依次类推,直至第n-1个数和第n个数比较为止——第一趟冒泡排序,结果最大的数被安置在最后一个元素位置上(2)对前n-1个数进行第二趟冒泡排序,结果使次大的数被安置在第n-1个元素位置(3)重复上述过程,共经过n-1趟冒泡排序后,排序结束a[0]0145a[1]a[2]a[3]a[4]a[5]23a输入n 个数给a[1] 到 a[n]for j=1 to n-1for i=1 to n-ja[i]>a[i+1]真假a[i] a[i+1]输出a[1] 到 a[n]#include main(){ int a[11],i,j,t;printf("Input 10 numbers:\n");for(i=1;i<11;i++)scanf("%d",&a[i]);printf("\n");for(j=1;j<=9;j++)for(i=1;i<=10-j;i++)if(a[i]>a[i+1]){t=a[i]; a[i]=a[i+1]; a[i+1]=t;}printf("The sorted numbers:\n");for(i=1;i<11;i++)printf("%d ",a[i]);}例 用简单选择法对10个数排序排序过程:(1)首先通过n-1次比较,从n个数中找出最小的, 将它与第一个数交换—第一趟选择排序,结果最小的数被安置在第一个元素位置上(2)再通过n-2次比较,从剩余的n-1个数中找出关键字次小的记录,将它与第二个数交换—第二趟选择排序(3)重复上述过程,共经过n-1趟排序后,排序结束例初始: [ 49 38 65 97 76 13 27 ]kji=11349一趟: 13 [38 65 97 76 49 27 ]i=22738二趟: 13 27 [65 97 76 49 38 ]三趟: 13 27 38 [97 76 49 65 ]四趟: 13 27 38 49 [76 97 65 ]五趟: 13 27 38 49 65 [97 76 ]六趟: 13 27 38 49 65 76 [97 ]kkkkjjjjjjjjjjCh5_201.c输入n 个数给a[1] 到 a[n]for i=1 to n-1for j=i+1 to na[j]真假k=j输出a[1] 到 a[n]k=ia[i] a[k]i != k真假#include main(){ int a[11],i,j,k,x;printf("Input 10 numbers:\n");for(i=1;i<11;i++)scanf("%d",&a[i]);printf("\n");for(i=1;i<10;i++){ k=i;for(j=i+1;j<=10;j++)if(a[j]if(i!=k){ x=a[i]; a[i]=a[k]; a[k]=x;}}printf("The sorted numbers:\n");for(i=1;i<11;i++)printf("%d ",a[i]);}7.2 二维数组及多维数组二维数组的定义定义方式: 数据类型 数组名[常量表达式][常量表达式];数组元素的存放顺序原因:内存是一维的二维数组:按行序优先多维数组:最右下标变化最快例 int a[3][4];float b[2][5];int c[2][3][4];int a[3,4]; ( )行数列数元素个数=行数*列数int a[3][2]a[0][1]a[1][0]a[1][1]a[2][0]a[2][1]014523a[0][0]a[0][0] a[0][1]a[1][0] a[1][1]a[2][0] a[2][1]int c[2][3][4]01234567………...20212223c[0][0][0]c[0][0][1]c[0][0][2]c[0][0][3]c[0][1][0]c[0][1][1]c[0][1][2]c[0][1][3]c[0][2][0]c[0][2][1]c[0][2][2]c[0][2][3]c[1][0][0]c[1][0][1]c[1][0][2]c[1][0][3]c[1][1][0]c[1][1][1]c[1][1][2]c[1][1][3]c[1][2][0]c[1][2][1]c[1][2][2]c[1][2][3]二维数组理解例 int a[3][4];20161720181920202120222320089201011201213201415200012002320045200067a[0][0]a[0][1]a[0][2]a[0][3]a[1][0]a[1][1]a[1][2]a[1][3]a[2][0]a[2][1]a[2][2]a[2][3]每个元素a[i]由包含4个元素的一维数组组成二维数组a是由3个元素组成a[0]a[1]a[2]行名014523a[0][1]a[0][2]a[0][3]a[1][0]a[1][1]a[0][0]a[1][3]a[2][0]a[2][1]a[2][2]a[2][3]a[1][2]67101189a[0]a[1]a[2]二维数组元素的引用形式: 数组名[下标][下标]二维数组元素的初始化分行初始化:例 int a[2][3]={{1,2,3},{4,5,6}};a[0][0]a[0][1]a[0][2]a[1][0]a[1][1]a[1][2]123456全部初始化按元素排列顺序初始化例 int a[2][3]={{1,2},{4}};a[0][0]a[0][1]a[0][2]a[1][0]a[1][1]a[1][2]120400部分初始化例 int a[][3]={{1},{4,5}};a[0][0]a[0][1]a[0][2]a[1][0]a[1][1]a[1][2]100450第一维长度省略初始化例 int a[2][3]={1,2,3,4,5,6};a[0][0]a[0][1]a[0][2]a[1][0]a[1][1]a[1][2]123456全部初始化例 int a[2][3]={1,2,4};a[0][0]a[0][1]a[0][2]a[1][0]a[1][1]a[1][2]124000部分初始化例 int a[][3]={1,2,3,4,5};a[0][0]a[0][1]a[0][2]a[1][0]a[1][1]a[1][2]123450第一维长度省略初始化程序举例例 将二维数组行列元素互换,存到另一个数组中a=1 2 34 5 6b=1 42 53 6#include main(){ int a[2][3]={{1,2,3},{4,5,6}};int b[3][2],i,j;printf("array a:\n");for(i=0;i<=1;i++){ for(j=0;j<=2;j++){ printf("%5d",a[i][j]);b[j][i]=a[i][j];}printf("\n");}printf("array b:\n");for(i=0;i<=2;i++){ for(j=0;j<=1;j++)printf("%5d",b[i][j]);printf("\n");}}例 求二维数组中最大元素值及其行列号max=a[0][0]for i=0 to 2for j=0 to 3a[i][j]>max真假max=a[i][j]row=icolum=j输出:max和row,colum#include main(){ int a[3][4]={{1,2,3,4},{9,8,7,6},{-10,10,-5,2}};int i,j,row=0,colum=0,max;max=a[0][0];for(i=0;i<=2;i++)for(j=0;j<=3;j++)if(a[i][j]>max){ max=a[i][j];row=i;colum=j;}printf("max=%d,row=%d, \colum=%d\n",max,row,colum);}例 读入下表中值到数组,分别求各行、各列及表中所有数之和12 4 615 7 98 23 32 5 1712 4 6 2215 7 9 318 23 3 342 5 17 2437 39 35 111#include main(){ int x[5][4],i,j;for(i=0;i<4;i++)for(j=0;j<3;j++)scanf("%d",&x[i][j]);for(i=0;i<3;i++)x[4][i]=0;for(j=0;j<5;j++)x[j][3]=0;for(i=0;i<4;i++)for(j=0;j<3;j++){ x[i][3]+=x[i][j];x[4][j]+=x[i][j];x[4][3]+=x[i][j];}for(i=0;i<5;i++){ for(j=0;j<4;j++)printf("%5d\t",x[i][j]);printf("\n");}}7.3 字符数组和字符串字符数组定义字符数组的初始化逐个字符赋值用字符串常量字符数组的引用例 char c[10], ch[3][4];例 char ch[5]={‘H’,’e’,’l’,’l’,’o’};ch[0]Hello逐个字符赋值ch[1]ch[2]ch[3]ch[4]有问题!例 char ch[5]={‘H’,’e’,’l’,’l’,’o’};ch[0]Hello逐个字符赋值ch[1]ch[2]ch[3]ch[4]例 char ch[5]={‘B’,’o’,’y’};ch[0]Boy\0\0逐个字符赋值ch[1]ch[2]ch[3]ch[4]例 char ch[5]=“Boy”;ch[0]Boy\0\0用字符串常量ch[1]ch[2]ch[3]ch[4]例 char ch[6]={“Hello”};char ch[6]=“Hello”;char ch[]=“Hello”;用字符串常量ch[0]Helloch[1]ch[2]ch[3]ch[4]\0ch[5]例 char diamond[][5]={{'.', '.','*'},{'.','*','.','*'},{'*', '.', '.', '.' ,'*'},{'.','*', '.','*'},{'.', '.','*'}};二维字符数组初始化..*\0\0.*.*\0*...*.*.*\0..*\0\0diamond[0]diamond[1]diamond[2]diamond[3]diamond[4]例 char fruit[][7]={“Apple”,”Orange”,”Grape”,”Pear”,”Peach”};二维字符数组初始化fruit[0]fruit[1]fruit[2]fruit[3]fruit[4]Apple\0\0Orange\0Grape\0\0Pear\0\0\0Peach\0\0例 输出一个字符串#include main(){ char c[10]={'I',' ','a','m',' ','a',' ','b','o','y'};int i;for(i=0;i<10;i++)printf("%c",c[i]);printf("\n");}Iamaboy0123456789字符串字符串及其结束标志无字符串变量,用字符数组处理字符串字符串结束标志:‘\0’例 “hello”共5个字符,在内存占6个字节 字符串长度5h e l l o \0104 101 108 108 111 0内存存放字符ASCII码字符串的输入输出逐个字符I/O: %c整个字符串I/O: %s例 用%cmain(){ char str[5];int i;for(i=0;i<5;i++)scanf(“%c”, &str[i]);for(i=0;i<5;i++)printf(“%c”, str[i]);}例 用%smain(){ char str[5];scanf(“%s”, str);printf(“%s”, str);}用字符数组名,不要加&输入串长度<数组维数遇空格或回车结束自动加‘\0’用字符数组名,遇‘\0’结束例 main( ){ char a[5]={‘H’,’e’,’l’,’l’,’o’};printf(“%s”,a);}例 main( ){ char a[ ]=“Hello”;printf(“%s”,a);}结果:Hello#-=*h e l l o02314结果:Hello用“%s”输出时,遇‘\0’结束main(){char a[]={'h','e','l','\0','l','o','\0'};printf("%s",a);}例输出:helh e l \0 l o \0数组中有多个‘\0’时,遇第一个结束main(){int i;char a[5];scanf("%s",a);for(i=0;i<5;i++)printf("%d,",a[i]);}运行情况:(1)若输入 hel , 正常(2)若输入 hell , 正常(3)若输入 hello , 用%s 输出时,会出现问题h e l \0h e l l \0h e l l o输入字符串长度<数组维数例 字符串输入举例H o w \0a r e \0y o u \0#include main(){ char a[15],b[5],c[5];scanf("%s%s%s",a,b,c);printf("a=%s\nb=%s\nc=%s\n",a,b,c);scanf("%s",a);printf("a=%s\n",a);}运行情况:输入:How are you 输出:a=Howb=arec=you 输入:How are you 输出:a=Howscanf中%s输入时,遇空格或回车结束运行情况:输入:How are you 例 若准备将字符串“This is a string.”记录下来,错误的输入语句为:(A)scanf(“%20s”,s);(B)for(k=0;k<17;k++)s[k]=getchar();(C)while((c=getchar())!=‘\n’)s[k++]=c;常用的字符串处理函数包含在头文件 string.h字符串输出函数puts格式:puts(字符数组)功能:向显示器输出字符串(输出完,换行)说明:字符数组必须以‘\0’结束字符串输入函数gets格式:gets(字符数组)功能:从键盘输入一以回车结束的字符串放入字符数组中,并自动加‘\0’说明:输入串长度应小于字符数组维数例 #include main( ){ char string[80];printf(“Input a string:”);gets(string);puts(string);}输入: How are you 输出: How are you 字符串连接函数strcat格式:strcat(字符数组1,字符数组2)功能:把字符数组2连到字符数组1后面返值:返回字符数组1的首地址说明: 字符数组1必须足够大 连接前,两串均以‘\0’结束;连接后,串1的‘\0’取消,新串最后加‘\0’字符串拷贝函数strcpy格式:strcpy(字符数组1,字符串2)功能:将字符串2,拷贝到字符数组1中去返值:返回字符数组1的首地址说明: 字符数组1必须足够大 拷贝时‘\0’一同拷贝 不能使用赋值语句为一个字符数组赋值例 char str1[20],str2[20];str1={“Hello!”}; ( )str2=str1; ( )例 strcpy与strcat举例#include #include void main(){ char destination[25];char blank[] = " ", c[]= "C++",turbo[] = "Turbo";strcpy(destination, turbo);strcat(destination, blank);strcat(destination, c);printf("%s\n", destination);}Turbo C++TrboC++0123456789u\024…….Trbo0123456789u\024…….…….Trbo\00123456789u24…….…...字符串比较函数strcmp格式:strcmp(字符串1,字符串2)功能:比较两个字符串比较规则:对两串从左向右逐个字符比较(ASCII码),直到遇到不同字符或‘\0’为止返值:返回int型整数,a. 若字符串1< 字符串2, 返回负整数b. 若字符串1> 字符串2, 返回正整数c. 若字符串1== 字符串2, 返回零说明:字符串比较不能用“==”,必须用strcmp字符串长度函数strlen格式:strlen(字符数组)功能:计算字符串长度返值:返回字符串实际长度,不包括‘\0’在内例 对于以下字符串,strlen(s)的值为:(1)char s[10]={‘A’,‘\0’,‘B’,‘C’,‘\0’,‘D’};(2)char s[ ]=“\t\v\\\0will\n”;(3)char s[ ]=“\x69\082\n”;答案:1 3 1#include #include main(){ char str1[] = ”Hello!", str2[] = ”How are you ”,str[20];int len1,len2,len3;len1=strlen(str1); len2=strlen(str2);if(strcmp(str1, str2)>0){ strcpy(str,str1); strcat(str,str2); }else if (strcmp(str1, str2)<0){ strcpy(str,str2); strcat(str,str1); }else strcpy(str,str1);len3=strlen(str);puts(str);printf(”Len1=%d,Len2=%d,Len3=%d\n”,len1,len2,len3);}例 strcmp与strlen举例How are you Hello!Len1=6,Len2=12,Len3=18Ch6_23.c应用举例例 输入一行字符,统计其中有多少个单词输入一字符串给 stringi=0 num=0 word=0当((c=string[i])!=‘\0’)c=空格真真假假word=0word=1num=num+1i=i+1输出:numword==0#include main(){ char string[81];int i,num=0,word=0;char c;gets(string);for(i=0;(c=string[i])!='\0';i++)if(c==' ') word=0;else if(word==0){ word=1; num++; }printf("There are %d words \in the line\n",num);}当前字符=空格是否未出现新单词,使word=0,num不累加前一字符为空格(word==0),新单词出现,word=1,num加1前一字符为非空格(word==1),未出现新单词,num不变否0是11是1未01否0是12否1未12是1未02否0是13是1未03否0是14否1未14否1未14否1未14例 输入:I am a boy.当前字符是否空格word原值新单词开始否word新值num值 Iamaboy.例 有三个字符串,找出其中最大者H o w \0H e l l o \0H i g h \0str[0]str[1]str[2]#include #include main(){ char string[20],str[3][20];int i;for(i=0;i<3;i++)gets(str[i]);if(strcmp(str[0],str[1])>0)strcpy(string,str[0]);elsestrcpy(string,str[1]);if(strcmp(str[2],string)>0)strcpy(string,str[2]);printf("\nThe largest string \is:\n%s\n",string);}例 有十个学生的成绩,求平均分92 85 68 75 54 88 98 45 61 7992856854889845756179a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]a#include main(){ int k , a[10];for(k=0;k<10;k++)scanf(“%d”,&a[k]);for(k=0;k<10;k++)sum+=a[k];printf(“Average is %d\n”,sum/10);}例 有三个学生四门课成绩92 85 68 7554 88 98 4561 79 81 40张三李四王二数学化学英语物理二维数组例 比较 int a[2][3]={{5,6},{7,8}};与 int a[2][3]={5,6,7,8};5 6 07 8 05 6 78 0 0例 int a[][10];float f[2][]={1.2 ,2.2};例 int a[5];a={2,4,6,8,10};例 int a[10];float i=3;a[i]=10;例 char name[0];float weight[10.3];int array[-100];例 char str[]=“Hello”;char str[]={‘H’,‘e’,‘l’,‘l’,‘o’};h e l l o02314h e l l o \0023145(共19张PPT)第9章 编译预处理作用:对源程序编译之前做一些处理,生成扩展C源程序种类宏定义 #define文件包含 #include条件编译 #if--#else--#endif等格式:“#”开头占单独书写行语句尾不加分号如 if(x==YES) printf(“correct!\n”);else if (x==NO) printf(“error!\n”);展开后: if(x==1) printf(“correct!\n”);else if (x==0) printf(“error!\n”);9.1 宏定义不带参数宏定义一般形式: #define 宏名 [宏体]功能:用指定标识符(宏名)代替字符序列(宏体)宏展开:预编译时,用宏体替换宏名---不作语法检查如 #define YES 1#define NO 0#define PI 3.1415926#define OUT printf(“Hello,World”);宏体可缺省,表示宏名定义过或取消宏体定义位置:任意(一般在函数外面)作用域:从定义命令到文件结束#undef可终止宏名作用域格式: #undef 宏名例 #define YES 1main(){ ……..}#undef YES#define YES 0max(){……..}YES原作用域YES新作用域宏定义可嵌套,不能递归例 #define MAX MAX+10 ( )引号中的内容与宏名相同也不置换例 #define PI 3.14159printf(“2*PI=%f\n”,PI*2);宏展开:printf(“2*PI=%f\n”,3.14159*2);宏定义中使用必要的括号()例 #define WIDTH 80#define LENGTH WIDTH+40var=LENGTH*2;宏展开:var= 80+40 *2;( )( )例 #define WIDTH 80#define LENGTH WIDTH+40var=LENGTH*2;宏展开:var= 80+40 *2;带参数宏定义一般形式: #define 宏名(参数表) 宏体例 #define S (r) PI*r*r相当于定义了不带参宏S,代表字符串“(r) PI*r*r”宏展开:形参用实参换,其它字符保留宏体及各形参外一般应加括号()例 #define S(a,b) a*b………..area=S(3,2);宏展开: area=3*2;不能加空格例 #define POWER(x) x*xx=4; y=6;z=POWER(x+y);宏展开:z=x+y*x+y;一般写成: #define POWER(x) ((x)*(x))宏展开: z=((x+y)*(x+y));#define MAX(x,y) (x)>(y) (x):(y)…….main(){ int a,b,c,d,t;…….t=MAX(a+b,c+d);……}宏展开:t=(a+b)>(c+d) (a+b):(c+d);int max(int x,int y){ return(x>y x:y);}main(){ int a,b,c,d,t;…….t=max(a+b,c+d);………}例 用宏定义和函数实现同样的功能带参的宏与函数区别带参宏函数处理过程不分配内存简单的字符置换分配内存先求实参值,再代入形参处理时间编译时程序运行时参数类型无类型问题定义实参,形参类型程序长度变长不变运行速度不占运行时间调用和返回占时间9.2 文件包含功能:一个源文件可将另一个源文件的内容全部包含进来一般形式: #include “文件名”或 #include <文件名>#include “file2.c”file1.cfile2.cfile1.cfile2.cABA处理过程:预编译时,用被包含文件的内容取代该预处理命令,再对“包含”后的文件作一个源文件编译<> 直接按标准目录搜索“” 先在当前目录搜索,再搜索标准目录可指定路径被包含文件内容源文件(*.c)头文件(*.h)宏定义数据结构定义函数说明等文件包含可嵌套#include “file2.c”file1.cAfile3.cC#include “file3.c”file2.cBfile1.cAfile3.cfile2.c例 文件包含举例/* powers.h */#define sqr(x) ((x)*(x))#define cube(x) ((x)*(x)*(x))#define quad(x) ((x)*(x)*(x)*(x))/*ch8_10.c*/#include #include "d:\fengyi\bkc\powers.h"#define MAX_POWER 10void main(){ int n;printf("number\t exp2\t exp3\t exp4\n");printf("----\t----\t-----\t------\n");for(n=1;n<=MAX_POWER;n++)printf("%2d\t %3d\t %4d\t %5d\n",n,sqr(n),cube(n),quad(n));}9.3 条件编译一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。条件编译命令的形式(1)#ifdef 标识符 程序段1 #else 程序段2 #endif它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。 这里的“程序段”可以是语句组,也可以是命令行。这种条件编译对于提高C源程序的通用性是很有好好处的。如果一个C源程序在不同计算机系统上系统上运行,而不同的计算机又有一定的差异(例如,有的机器以16位(2个字节)来存放一个整数,而有的则以32位存放一个整数),这样往往需要对源程序作必要的修改,这就降低了程序的通用性。可以用以下的条件编译(见下页):#ifdef IBM-PC #define INTEGER_SIZE 16 #else #define INTEGER_SIZE 32 #endif 即如果IBM-PC在前面已被定义过,则编译下面的命令行: #define INTEGER_SIZE 16 如果在这组条件编译命令之前曾出现以下命令行: #define IBM-PC 0 则预编译后程序中的INTEGER_SIZE都用16代替,否则都用32代替(以上是针对IBM型计算机而定的)。这样,源程序可以不必作任何修改就可以用于不同类型的计算机系统。例如,在调试程序时,常常希望输出一些所需的信息,而在调试完成后不再输出这些信息。可以在源程序中插入以下的条件编译段: #ifdef DEBUG printf("x=%d,y=%d,z=%d\n",x,y,z); #endif 如果在它的前面有以下命令行: #define DEBUG 则在程序运行时输出x,y,z的值,以便调试分析。调试完成后只需将这个define命令行删除即可。有人可能觉得不用条件编译也可达此目的,即在调试时加一批printf语句,调试后一一将printf语句删除去。的确,这是可以的。但是,当调试时加的printf语句比较多时,修改的工作量是很大的。用条件编译,则不必一一删改printf语句,只需删除前面的一条“#define DEBUG”命令即可,这时所有的用DEBUG作标识符的条件编译段都使其中的printf语句不起作用,即起统一控制的作用,如同一个“开关”一样。条件编译命令的形式(2)#ifndef 标识符 程序段1 #else 程序段2 #endif 只是第一行与第一种形式不同:将“ifdef”改为“ifndef”。它的作用是:若标识符未被定义则编译程序段1,否则编译程序段2。这种形式与第一种形式的作用相反。以上两种形式用法差不多,根据需要任选一种,视方便而定。例如,上面调试时输出信息的条件编译段也可以改为: #ifndef RUN printf("x=%d,y=%d,z=%d\n",x,y,z); #end if 如果在此之前未对RUN定义,则输出x,y,z的值。调试完成后,在运行之前,加以下命令行: #define RUN 则不再输出x,y,z的值。条件编译命令的形式(3)#if 表达式 程序段1 #else 程序段2 #endif 它的作用是:当指定的表达式值为真(非零)时就编译程序段1,否则编译程序段2。可以事先给定一定条件,使程序在不同的条件下执行不同的功能。例:输入一行字母字符,根据需要设置条件编译,使之能将字母全改为大写输出,或全改为小写字母输出。 #define LETTER 1 main() { char str[20]="C Language",c; int 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); } } 运行结果为:C LANGUAGE现在先定义LETTER为1,这样在预处理条件编译命令时,由于LETTER为真(非零),则对第一个if语句进行编译,运行时使小写字母变大写。如果将程序第一行改为:#define LETTER 0则在预处理时,对第二个if语句进行编译处理,使大写字母变成小写字母(大写字母与相应的小写字母的ASCII代码差32)。此时运行情况为: c language有人会问:不用条件编译命令而直接用if语句也能达到要求,用条件编译命令有什么好处呢?的确,此问题完全可以不用条件编译处理,但那样做目标程序长(因为所有语句都编译),而采用条件编译,可以减少被编译的语句,从而减少目标的长度。当条件编译段比较多时,目标程序长度可以大大减少。本章介绍的预编译功能是C语言特有的,有利于程序的可移植性,增加程序的灵活性。1. 教学提纲教学内容 章节 讲授 课内实验 课外实验C语言程序设计初步 第01章 C语言概述 ( http: / / htxy.cai.swufe. / C / ch01.ppt ) 2 2 4第02章 程序的概念 ( http: / / htxy.cai.swufe. / C / ch021.ppt )、算法的概念 ( http: / / htxy.cai.swufe. / C / ch022.htm )数据描述与基本操作 第03章 数据类型、运算符与表达式 ( http: / / jhd.cai.swufe. / c / ch03.ppt ) (另版 ( http: / / jhd.cai.swufe. / c / ch031.ppt )) 4 3 3 C程序的流程设计(另版 ( http: / / jhd.cai.swufe. / c / ch04_06.ppt )) 第04章(1) 数据输入与输出 ( http: / / jhd.cai.swufe. / c / ch041.ppt ) 6 3 6第04章(2) 顺序程序设计 ( http: / / jhd.cai.swufe. / c / ch04.ppt )第05章 选择结构程序设计 ( http: / / jhd.cai.swufe. / c / ch05.ppt )第06章 循环控制 ( http: / / jhd.cai.swufe. / c / ch06.ppt )数组 第07章 数组 ( http: / / jhd.cai.swufe. / c / ch07.ppt ) (另版 ( http: / / jhd.cai.swufe. / c / ch071.ppt )) 4 2 4模块化程序设计 第08章 函数 ( http: / / jhd.cai.swufe. / c / ch08.ppt ) (另版) ( http: / / jhd.cai.swufe. / c / ch081.ppt ) 6 2 4第09章 编译预处理 ( http: / / jhd.cai.swufe. / c / ch09.ppt ) (另版 ( http: / / jhd.cai.swufe. / c / ch091.ppt ))指针 第10章 指针 ( http: / / jhd.cai.swufe. / c / ch10.ppt ) (另版 ( http: / / jhd.cai.swufe. / c / ch101.ppt )) 7 2 7结构体与共用体 第11章 结构体与共用体 ( http: / / jhd.cai.swufe. / c / ch11.ppt ) (另版 ( http: / / jhd.cai.swufe. / c / ch111.ppt )) 3 1 3位运算 第12章 位运算 ( http: / / jhd.cai.swufe. / c / ch12.ppt ) (另版 ( http: / / jhd.cai.swufe. / c / ch121.ppt )) 1 1 2文件 第13章 文件 ( http: / / jhd.cai.swufe. / c / ch13.ppt ) (另版 ( http: / / jhd.cai.swufe. / c / ch131.ppt )) 2 2 3综合应用举例 举 ( http: / / jhd.cai.swufe. / c / ex12a.htm )例 ( http: / / jhd.cai.swufe. / c / ex12a.htm ) 例1 ( http: / / jhd.cai.swufe. / c / ex12a.c ) 例2 ( http: / / jhd.cai.swufe. / c / ex12b.c ) 3 合计 36 18 34 教学内容:教学内容 教案 作业及讨论 上机安排 等级考试习题 相关程序C语言程序设计初步 第一、二章 ( http: / / htxy.cai.swufe. / c%B3%CC%D0%F2%C9%E8%BC%C6%B5%DA%D2%BB%A1%A2%B6%FE%D5%C2.ppt ) 第三、四章 ( http: / / htxy.cai.swufe. / c_3_4.ppt ) P37 2.4,2.8 等级考试及习题参考 ( http: / / hefuliang.cai.swufe. ) 蛇吃老鼠游戏 ( http: / / c_code / C1.C )、P65 3.3 3.6,3.7-3.12 C常用函数使用介绍 ( http: / / Cyyb / function.htm )数据描述与基本操作 P85 4.5-4.10 第5周(周二) C程序的流程设计 第五章 ( http: / / htxy.cai.swufe. / c_5.ppt ) P105 5.4-5.10 第9周(周二) 第六章 ( http: / / htxy.cai.swufe. / c_6.ppt ) 两球碰撞程序 ( http: / / htxy.cai.swufe. / box.c ) 第11周(周二) 图圆程序1 ( http: / / htxy.cai.swufe. / program6_2.htm ),画正弦图程序 ( http: / / htxy.cai.swufe. / program6_3.htm )数组 第七章 ( http: / / htxy.cai.swufe. / c_7.ppt ) 模块化程序设计(函数) 指针 结构体与共用体 位运算 文件 综合应用举例 合计 2.教 材:《C程序设计第二版》,谭浩强编,清华大学出版社 参考书:《C程序设计题解与上机指导》,谭浩强编,清华大学出版社 《C程序设计实验指导》,徐士良,清华大学出版社3.实验指导实验1 a. C语言上机指南和Visual C++ 6.0的基本用法 ( http: / / htxy.cai.swufe. / c / ex011.htm )b. C语言程序初步 ( http: / / htxy.cai.swufe. / C / ex012.htm ) ( http: / / jhd.cai.swufe. / c / ex012.htm )实验2 数据类型、运算符与表达式:实验2a ( http: / / htxy.cai.swufe. / c / ex02a.htm ) 实验2b ( http: / / htxy.cai.swufe. / c / ex02b.htm )实验3 输入与输出:实验3a ( http: / / htxy.cai.swufe. / c / ex03a.htm ) 实验3b ( http: / / htxy.cai.swufe. / c / ex03b.htm )实验4 选择结构:实验4a ( http: / / htxy.cai.swufe. / c / ex04a.htm ) 实验4b ( http: / / htxy.cai.swufe. / c / ex04b.htm )实验5 循环结构: 实验5a ( http: / / htxy.cai.swufe. / C / ex05a.htm ) 实验5b ( http: / / htxy.cai.swufe. / c / ex05b.htm ) 实验5c ( http: / / htxy.cai.swufe. / c / ex05c.htm )实验6 数组: 实验 ( http: / / jhd.cai.swufe. / c / ex06a.htm )6a ( http: / / htxy.cai.swufe. / c / ex06a.htm ) 实验6b ( http: / / htxy.cai.swufe. / c / ex06b.htm )实验7 函数的定义及调用: 实验7a ( http: / / htxy.cai.swufe. / c / ex07a.htm ) 实验7b ( http: / / htxy.cai.swufe. / c / ex07b.htm )实验8 指针:实验8a ( http: / / htxy.cai.swufe. / c / ex08a.htm ) 实验8b ( http: / / htxy.cai.swufe. / c / ex08b.htm ) 实验8c ( http: / / htxy.cai.swufe. / c / ex08c.htm )实验9 结构体和共用体 实验9 ( http: / / htxy.cai.swufe. / c / ex09a.htm )实验10 位运算 实验 ( http: / / htxy.cai.swufe. / c / ex10a.htm )10 ( http: / / jhd.cai.swufe. / c / ex10a.htm )实验11 文件 实验 ( http: / / jhd.cai.swufe. / c / ex11a.htm )11 ( http: / / htxy.cai.swufe. / c / ex11a.htm )实验12 综合应用实验12 ( http: / / htxy.cai.swufe. / c / ex12a.htm )4.《 ( http: / / jhd.cai.swufe. / c / sybg.htm )C程序设计》课程实习题目与实验报告要求 ( http: / / htxy.cai.swufe. / c / sybg.htm )(共14张PPT)第2章 程序的概念程序—直观理解“程序”一词来自生活,通常指完成某些事务的一种既定方式和过程。1,起床2,刷牙3,洗脸4,吃饭5,早自习按顺序实施这些步骤,即完成了该项事务。一些直观特征:按部就班地进行;开始与结束;完成工作;基本动作;需要用某种记法形式描述;不同的描述粒度(细节程度);等等。程序是人与计算机交流信息的基本方式。人通过程序指挥计算机的活动。程序与程序开发人们描述(编制)计算机程序的工作称为程序设计或编程,其产品就是程序。由于计算机的本质特征,从它诞生之初就有了程序设计工作。要用计算机处理问题,写程序时就必须精确描述所需的全部细节,不能有一点含糊。写程序需用程序设计语言。特点:计算机可以处理,按它完成工作。程序设计语言是人与计算机交流的一类媒介。在硬件层次,程序是机器指令的序列。程序执行:将程序存入内存,通知CPU第一条指令的地址。开始!一般情况下,CPU执行完一条指令后,取出下一条指令。并如此继续下去。转跳指令另行设置指令地址,指定下一条指令的位置。人命令计算机去执行某程序,计算机会按照程序规定,一丝不苟地执行,直至程序结束。机器语言和程序机器语言就是机器指令形成的语言;二进制编码,机器可直接执行;000000010000000010000000000100010000101000000101000000000001000000010001000011000000010000000000000100000010000000001110数据装入寄存器0数据装入寄存器1寄存器0与1的数据乘数据装入寄存器1寄存器0与1的数据加保存寄存器0里的数据难写难读,人使用不便,程序开发效率极低。汇编语言和程序汇编语言:助记符号形式,指令与机器指令对应,便于人识别和使用。load 0 aload 1 bmult 0 1load 1 cadd 0 1save 0 d将单元a的数据装入寄存器0将单元b的数据装入寄存器1寄存器0与1的数据乘将单元c的数据装入寄存器1寄存器0与1的数据加将寄存器0里的数据存入单元d计算机无法直接执行汇编语言程序,执行前需要翻译为机器指令程序。最早是人自己翻译。后来人们开发出“汇编系统”,让计算机去完成程序翻译工作。每条指令的意义容易理解。程序粒度太小,细节太多。程序无结构,缺乏组织手段。写大程序仍然很困难。高级语言和程序具有类似文字的表现形式;用类似数学的表达式形式描述基本计算;用变量等概念取代低级的存储概念,人摆脱了繁琐的存储安排工作;提供高级操作流程控制手段和程序组织手段。在C语言里写前面同样的程序:d = a * b + c;计算机无法直接执行高级语言写出的程序。常规方式是做一个编译系统,完成高级语言程序到机器语言可执行程序的翻译加工。编程 -> 编译加工 -> 执行大大提高了编程工作的效率;使人更容易思考和把握复杂程序的意义;使更多人愿意投身于此,使编程发展成为一种职业和谋生方式。术语“程序设计语言”已专指“高级语言”。1954年到1957年:高级语言Fortran诞生。至1960年代中,开发了Algol60,LISP ,COBOL,BASIC等。1970年代开始:Pascal 和 C语言逐渐分别为教学科研和软件开发用的主要语言。1980年代:逻辑程序语言Prolog,面向对象语言Smalltalk 和后来的C++。1995年左右的Java。高级语言的简单历史起床刷牙洗脸吃饭早自习拿饭碗去食堂排队买饭吃饭洗碗离开食堂排队选饭选菜付款“程序”分解实例(早起活动):程序设计与程序结构编写程序不应该从细节开始逐步分解,分解到程序语言所提供的功能。需要了解:程序语言提供了那些基本功能?各种语言功能的形式和意义。学习所用的编程工具(C语言系统):编程环境工作过程编程序时需要的就是这种工作方式:从问题需要出发,从高层开始设计程序;并逐步分解程序功能,直至可以用程序语言实现。(共167张PPT)《C程序设计》信息工程学院 黄涛第一部份 绪 论*计算机的有关基础知识C程序的基本结构和特点C程序的运行步骤第一章 C语言概述1.1 计算机的有关基础知识计算机的基本原理计算机的基本结构内存的组织形式计算机编程语言计算机中数的表示1. 二进制计算机中所有的信息都是以二进制形式的数据来表示、存储、传递和运算的。二进制数据就是全部由 “0” 和 “1” 组成的数据。计算机的基本原理计算机的基本原理使用二进制的主要原因:物理上很容易实现两种相对立的稳定的能量状态(如晶体管的导通与不导通、高电平与低电平、磁性材料中剩磁的顺时针或逆时针方向等)二进制数据的运算规则数量少而且简单,操作实现简便计算机的基本原理2. 存储程序控制是指:在利用计算机解决问题时,首先需要人找出解决方法,然后将这个方法用某种计算机语言转换成计算机程序(包含了原始数据和处理指令),再送入计算机中存储,最后由人发出命令,计算机开始按照程序中的指令和流程执行这个程序(即对数据进行加工处理) 。计算机的基本原理计算机本身只是一个计算工具,至于对哪些数据进行计算,如何计算等等这些问题则是由人来解决和控制的。计算机硬件的基本结构硬件系统主机外设中央处理器CPU内存储器运算器控制器外存储器输入输出设备(I/O)存储器上述用绿色标示的四个部分即是计算机的四大组成部分。计算机硬件的基本结构运算器:对数据进行加工、运算的部件。其主要功能是对二进制数据进行算术运算和逻辑运算。计算机硬件的基本结构控制器:计算机的指挥控制中心。其主要功能是识别翻译指令代码并向计算机各部分发出适当的控制信号,以便执行机器指令。计算机硬件的基本结构存储器:存放数据和程序的部件。其主要功能是按照要求向指定的位置存进或取出信息。计算机硬件的基本结构输入设备:输入设备功能是将数据和程序送入到计算机的存储器中。常见的输入设备有键盘、鼠标、扫描仪等。计算机硬件的基本结构输出设备:输出设备功能是将计算机产生的结果送出计算机外。常见的输出设备有显示器、打印机、绘图仪等。计算机的基本工作过程计算程序 原始数据输入输出设备I/O命令运算命令运算器控制器存取命令控制流存储器数据流计算结果输入输出取数存数指令内存储器和外存储器的对比内存储器(主存储器) 外存储器(辅助存储器)结构 属于硬件系统的主机部分,位于主机板的插槽上,与运算器和控制器直接联系 属于硬件系统的外设部分,与运算器和控制器必须通过一定的硬件接口相联系内存储器和外存储器的对比内存储器(主存储器) 外存储器(辅助存储器)物理 组成 电介质:半导体集成电路计算机断电会使内存数据丢失 磁介质:磁盘或磁带磁介质损伤或消磁会使数据丢失内存储器和外存储器的对比内存储器(主存储器) 外存储器(辅助存储器)作用 存放计算机运行期间正在执行的程序和数据 存放系统程序、各种应用程序和数据文件等当前不参与运行的大量信息内存储器和外存储器的对比内存储器(主存储器) 外存储器(辅助存储器)容量 小:128 MB或 256 MB 大:20 GB、 30 GB、…速度 快:ns 级 慢:ms 级注:1GB=1024MB内存储器的组织形式1 0 1 1 0 0 1 10 1 0 1 0 1 1 00 0 0 1 1 0 0 0……1 1 0 1 0 1 1 01 0 0 1 0 0 1 01“位”:bit最小的电子线路单元,只能保存一位二进制数即一个 0 或一个 1 。因此一“位”单元可以保存的最大的十进制数据是 1 。内存储器的组织形式1 0 1 1 0 0 1 10 1 0 1 0 1 1 00 0 0 1 1 0 0 0……1 1 0 1 0 1 1 01 0 0 1 0 0 1 01“字节”:byte ,由若干个“位”组成。微机中,1字节=8位。最基本的数据存储单元,即数据在内存中的存储是以“字节”为基本单位的。任何数据总是占据整数个“字节”单元 。1 0 1 1 0 0 1 1内存储器的地址和内容1 0 1 1 0 0 1 10 1 0 1 0 1 1 00 0 0 1 1 0 0 0……1 1 0 1 0 1 1 01 0 0 1 0 0 1 0计算机怎么知道到哪一个内存单元去找需要的数据??内存储器的地址和内容1 0 1 1 0 0 1 10 1 0 1 0 1 1 00 0 0 1 1 0 0 0……1 1 0 1 0 1 1 01 0 0 1 0 0 1 000000001000200030004……099809991000内存储器从第一个字节开始到最后一个字节为止,每一个字节单元依次有一个“编号” ,这个编号就叫内存单元的“地址”。内存储器的地址和内容1 0 1 1 0 0 1 10 1 0 1 0 1 1 00 0 0 1 1 0 0 0……1 1 0 1 0 1 1 01 0 0 1 0 0 1 0← 0000← 0001← 0002← 0003← 0004……← 0998← 0999← 1000该地址所指向的内存单元中存放的数据就叫内存单元的“内容”。内存储器的地址和内容1 0 1 1 0 0 1 10 1 0 1 0 1 1 00 0 0 1 1 0 0 0……1 1 0 1 0 1 1 01 0 0 1 0 0 1 0← 0000← 0001← 0002← 0003← 0004……← 0998← 0999← 1000计算机首先找到所需数据的地址(编号),再到该地址所指向的内存单元去读取内存单元的内容(数据的值)。计算机编程语言:机器语言计算机能直接执行的用于表示机器指令的二进制数字代码称为“机器语言”,也就是机器指令集合。缺点:不易记忆和理解,使用、修改和维护都很费时费力,而且不易推广。优点:需要的后援软设备和硬设备较少,程序执行效率高,速度最快。计算机编程语言:汇编语言使用一些符号来代表指令、寄存器、存储单元等机器指令中的要素,然后用这些符号来写程序。使用这些助记符代码的语言,叫做“汇编语言”。缺点:不能独立于机器。汇编语言源程序必须经过“汇编程序”的汇编后才能执行。优点:比机器语言易理解、记忆和使用,速度与机器语言相仿。大量用于实时控制。计算机编程语言:高级语言使用与人类的自然语言和数学语言相近的符号和运算符构成的语言,叫做“高级语言”。缺点:不能够直接执行,源程序必须经过“编译程序”的编译或“解释程序”的解释后形成目标程序才行。执行速度慢,系统开销大。优点:独立于机器,通用性和可移植性强;容易理解、记忆和学习,使用方便,源程序较短。机器语言、汇编语言和高级语言高级语言 (源程序)汇编语言 (源程序)机器语言 (程序)面向机器面向机器面向用户汇编 程序编译或 解释程序计算机中数的表示一个数据总要占据一定数目的字节单元(整数个、有限个)不同类型的数据占据不同数目的字节单元(比如,PC 机 DOS 环境下一个整数占2 个字节,一个实数占 4 个字节)计算机中数的表示所有数据都是以二进制数的形式表示的数据有正有负。计算机中表示一个数据时,总是用它所占字节单元的所有位中最高位表示数的符号:0 表示正,1 表示负。计算机中数的表示Ns 1 0 1 0 1 0 1符号位最高位:第7位最低位:第0位Ns 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1符号位最高位:第15位最低位:第0位1个字节2个字节定点数在计算机中,小数点位置固定的数称为定点数。定点整数定点整数:小数点默认为在一个二进制数最低位的后面符号位数值部分小数点N = 1010101Ns 1 0 1 0 1 0 1定点小数符号位数值部分小数点N = 0.1010101Ns 1 0 1 0 1 0 1定点小数:小数点默认为在一个二进制数最高位(符号位)的后面定点数可见,不管是定点整数还是定点小数,由于它们的小数点位置固定,因此在运算时可以不考虑小数点的位置,只要区分是定点整数还是定点小数就可以了。浮点数在计算机中,既有整数部分又有小数部分的数称为浮点数浮点数P = ± S × 2 ± NP的尾数 (定点小数)P的阶码 (定点整数)尾数符号阶码符号可见,浮点数表示中的各部分又都是定点数。在一个浮点数所占字节单元中,一部分“位”用来保存阶码,一部分“位”用来保存尾数。浮点数0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0阶码符号阶码数值尾数符号尾数数值阶码部分 占 6 位尾数部分 占 10 位1.2 C程序的基本结构和特点C语言特点几个简单的C程序例子C程序的基本结构和特点C语言的发展1960 ALGOL1963 CPL1967 BCPL ( Basic Combind Programming Language)1969 UNIX(汇编语言)1970 B (Ken Thompson &Dennis Ritchie)1972 C1980 C++1995 JAVA“源于ALGOL,始于CPL,继于BCPL,承于B”C语言的特点C是中级语言C语言比较灵活2. C是结构式语言3. C语言功能齐全4. C语言适用范围大C程序例1#include int main(){printf(“Hello, World\n”);return(0);}预处理命令。include的作用是包含文件。stdio.h 中包含有函数 printf 的原型声明。C程序例1#include int main(){printf(“Hello, World\n”);return(0);}main 主函数。每个C程序必须有且只能 有一个 main 函数,它是程序的执行起点。C程序例1#include int main(){printf(“Hello, World\n”);return(0);}包含在{ }中的是主函数的函数体。C 程序中每个函数的函数体必须包含在一对 大括号中。C程序例1#include int main(){printf(“Hello, World\n”);return(0);}printf 是C语言中的系统库函数。其功能 是在屏幕上输出数据。其中,“ ” 括起来的 是字符串常量。C程序例1#include int main(){printf(“Hello, World\n”);return(0);}return是C语言的关键字。功能是使函数 调用返回。C程序例1#include int main(){printf(“Hello, World\n”);return(0);}每个语句后都有一个分号。分号是C语言 的语句结束符。C程序例1#include int main(){printf(“Hello, World\n”);return(0);}程序运行结果是:在屏幕上显示Hello, World#include int main(){/* define variables */int a , b , sum ;a=123; b=456;sum=a+b;printf(“sum=%d\n”, sum);return(0);}C程序例2同样必须有main主函数。它是程序的执行起点。#include int main(){/* define variables */int a , b , sum ;a=123; b=456;sum=a+b;printf(“sum=%d\n”, sum);return(0);}C程序例2/* */ 之间的所有内容称为“注释”。注释不被编译。#include int main(){/* define variables */int a , b , sum ;a=123; b=456;sum=a+b;printf(“sum=%d\n”, sum);return(0);}C程序例2变量定义语句。int是C语言的关键字,用于定义整型变量。#include int main(){/* define variables */int a , b , sum ;a=123; b=456;sum=a+b;printf(“sum=%d\n”, sum);return(0);}C程序例2=是C语言的赋值运算符,由它构成赋值表达式。此处把常量的值赋给变量。#include int main(){/* define variables */int a , b , sum ;a=123; b=456;sum=a+b;printf(“sum=%d\n”, sum);return(0);}C程序例2sum=a+b;也是赋值语句:把一个加法表达式的值赋给变量。#include int main(){/* define variables */int a , b , sum ;a=123; b=456;sum=a+b;printf(“sum=%d\n”, sum);return(0);}C程序例2printf函数中的%d是格式字符,用于格式输出数据。#include int main(){/* define variables */int a , b , sum ;a=123; b=456;sum=a+b;printf(“sum=%d\n”, sum);return(0);}C程序例2程序运行结果在屏幕上输出sum=579#include int main(){int a=10, b=20, p;p=func(a , b);printf(“p=%d”, p);return(0);}int func(int a , int b){ return(a*b); }C程序例3必须有main主函数。且只能有一个。#include int main(){int a=10, b=20, p;p=func(a , b);printf(“p=%d”, p);return(0);}int func(int a , int b){ return(a*b); }C程序例3函数调用语句。此处调用的是用户自定义的函数。函数调用方法是:函数名(实参列表)调用后的返回值被赋给变量p。#include int main(){int a=10, b=20, p;p=func(a , b);printf(“p=%d”, p);return(0);}int func(int a , int b){ return(a*b); }C程序例3本程序中的另一个函数是用户自定义函数,函数名是func,有两个int型形参,函数返回值是int型的。#include int main(){int a=10, b=20, p;p=func(a , b);printf(“p=%d”, p);return(0);}int func(int a , int b){ return(a*b); }C程序例3自定义函数func的函数体。其中return语句使函数调用返回。返回的同时还带回一个值。#include int main(){int a=10, b=20, p;p=func(a , b);printf(“p=%d”, p);return(0);}int func(int a , int b){ return(a*b); }C程序例3程序运行结果在屏幕上输出p=200C程序的基本结构和特点11. 一个C程序是由函数构成的。C语言又被称作“函数式语言”函数可能是系统库函数或用户自定义函数。 使用函数构成程序可以容易实现结构化程序设计方法。int main(){……prod=func(…);printf(……);return(0);}int func( … , … ){……}C程序的基本结构和特点1主函数 main自定义函数 func调用系统 库函数 printf调用用户 自定义函数 funcC程序的基本结构和特点22. 一个C程序中必须有一个main函数,而且只能有一个。无论该主函数位于程序的何处,程序都由它开始执行。int func( … , … ){……}int main(){……prod=func(…);printf(……);return(0);}C程序的基本结构和特点2主函数main在自定义函数func之后自定义函数func 在主函数前面C程序的基本结构和特点33. 一个函数由两部分组成:函数原型和函数体。函数原型确定函数名,形参数量和类型,函数返回值及类型;函数体包含在一对{ }中,由若干语句组成以实现函数功能。int func(int a ){……}int main(){……prod=func(…);printf(……);return(0);}C程序的基本结构和特点3自定义函数的原型函数名:func 形参:一个,int 型 返回值:有,int 型主函数的原型函数名:main 形参:没有 返回值:有, int 型C程序的基本结构和特点44. 每个C语句必须以分号结束。C语句的种类有变量定义语句函数调用语句表达式语句控制语句int main(){/* define variables */int a, b, prod;a=10; b=20;prod=func(a , b);printf(“prod=%d”, prod);return(0);}C程序的基本结构和特点4变量 定义 语句控制 语句表达式语句函数调用语句C程序的基本结构和特点55. 可以用/* */(块注释符)在程序中任何地方作注释。注释的内容不会被编译。必要的注释可以大大增强程序的可读性。int main(){/* define variables */int a, b, prod;/* ………… */printf(“prod=%d”, prod);return(0); /* end of main */}C程序的基本结构和特点5注释在一行上注释占用多行也可以这注释也行C程序的基本结构和特点66. C程序的书写格式很自由,但是为了保证程序的可读性和易维护性,还是要保持良好的书写规范和编程习惯。int main(){/* define variables */int a, prod;for(a=1; a<10;a++)prod=prod*a;printf(“prod=%d”, prod);return(0);}C程序的基本结构和特点6一行只写一个语句使用有规则的缩进式代码排列方式在需要的地方加上一些空行适当的注释会大大增加程序的可读性和可维护性1.3 C程序的上机步骤高级语言源程序为了使计算机能够按照人的意志进行工作,必须根据实际问题和它的解决方法,编写出相应的程序。所谓程序就是一组计算机能识别和执行的指令。每一条指令使计算机执行特定的操作。高级语言源程序编写程序就必须使用某种计算机语言。同样的问题既可以用任一种汇编语言编程,也可以用任一种高级语言编程。计算机高级语言有很多种,比如BASIC、C、C++ 、FORTRAN、PASCAL、等等。这样得到的程序我们称之为“源程序”。源程序的编译和链接从根本上说,计算机不能直接识别和执行“源程序”,因此必须使用一种称之为“编译程序” 的程序,把“源程序”编译成二进制形式的“目标程序” 。这个“目标程序”再经过与已有的系统函数库二进制代码或其它的“目标程序”的链接,就形成了可执行的程序。源程序的编译和链接高级 语言 源程序编译 程序目标程序其它目标程序 或库函数链接 程序可执行程序C程序的上机步骤操作流程文件的输入输出上机输入和编辑源程序源程序 文件 *.c编译有错?链接执行结果正确?结束YesYesNoNo目标程序 文件 *.obj可执行程序 文件 *.exe调试NoNucleus Wirth:Algorithm + Data Structures = Programs程序设计:算法:数据结构:为计算机处理问题编制一组指令集处理问题的策略问题的数学模型算法和算法的衡量一、算法二、算法设计的原则算法是为了解决某类问题而规定的一个有限长的操作序列。一个算法必须满足以下五个重要特性:1.有穷性 2.确定性 3.可行性4.有输入 5.有输出一、算法1.有穷性 对于任意一组合法输入值,在执行有穷步骤之后一定能结束,即:算法中的每个步骤都能在有限时间内完成。2.确定性 对于每种情况下所应执行的操作,在算法中都有确切的规定,使算法的执行者或阅读者都能明确其含义及如何执行。并且在任何条件下,算法都只有一条执行路径。3.可行性 算法中的所有操作都必须足够基本,都可以通过已经实现的基本操作运算有限次实现之。4.有输入 作为算法加工对象的量值,通常体现为算法中的一组变量。有些输入量需要在算法执行过程中输入,而有的算法表面上可以没有输入,实际上已被嵌入算法之中。5.有输出 它是一组与“输入”有确定关系的量值,是算法进行信息加工后得到的结果,这种确定关系即为算法的功能。二、算法设计的原则设计算法时,通常应考虑达到以下目标:1.正确性2. 可读性3.健壮性4.高效率与低存储量需求常量与变量整型、实型、字符型数据变量的初始化各类数值型数据间的混合运算算术运算符与算术表达式赋值运算符与赋值表达式逗号运算符与逗号表达式第三章 数据类型、运算符与表达式数据类型的概念数据类型:数据存在的组织和形式。程序中用到的所有数据都必须指定其数据类型。运算符:一些具有事先规定的运算功能的特定的符号。这里的运算功能可以是算术运算(加减乘除)、比较运算(大于小于等于)或逻辑运算(与或非)或,等等。C语言共有34种运算符。运算符的概念表达式:按照运算符的性质,用运算符把数据连接起来以达到数据运算目的而构成的式子。表达式的概念写表达式的最终目的是要进行计算它求出它的值。求解表达式的过程是由计算机完成的,因此,要想得到正确的结果,就必须写出正确的表达式。而一个表达式中往往包含多个运算符,所以必须熟悉每一种运算符的性质(优先级别和结合性),才能写出有正确求解结果的表达式。3.1 C的数据类型C的数据类型C的数据类型基本类型构造类型整型单精度型双精度型数组类型结构体类型实型共用体类型指针类型字符型枚举类型空类型3.2 常量与变量常量程序运行期间,值不能被改变的量称为常量C语言有两种实现常量的方法:直接常量和符号常量直接常量直接常量,也可以称为“字面常量”,就是直接具有具体的值的数据。如: 1, 2, 3; 整型常量0.1, 10.0; 实型常量‘a’, ‘1’; 字符常量“hello” 字符串常量符号常量符号常量:用一个标识符代表一个常量实现方式:使用 #define 预处理命令#define预处理命令的用法:#define 标识符 字符串它的作用是用指定的标识符去代表一个字符串。在预处理时,源程序中所有在该预处理命令后出现的该标识符全部用指定的字符串代替。预处理编译预处理:是指在对源程序进行正式编译之前所作的一些处理。这些处理是由预处理命令指定的。源程序经过预处理后,再由编译程序进行正式的编译。预处理命令是由ANSI C规定的,但是它不是C语言本身的组成部分,不能直接对它进行编译。预处理命令都是以符号“#”开头的。#define PI 3.14159int main(){float r, c, s, v; r=20;c=2*PI*r; printf(“周长=%f”, c);s=PI*r*r; printf(“面积=%f”, s);v=4*PI*r *r *r/3; printf(“体积=%f”, v);return(0);}符号常量的实现这个字符串将要在预处理时去替换符号PIPI会被指定的字符串3.14159代替符号常量的好处使用符号常量的好处是:从标识符名可以清楚地看出该常量的含义,做到“见名知意”在需要修改常量的值时可以做到“一改全改”,容易调整符号常量的注意事项必须注意的是:符号常量不是变量,不能被赋值。#define PI 3.14159int main(){……PI = 100;……}这样的赋值是错误的符号常量的注意事项必须注意的是:如果指定的符号出现在双引号中,就不被替换。#define PI 3.14159int main(){……printf(“PI = %f”, PI);……}这个PI不会被替换这个PI会被替换变量程序运行期间,值可以被改变的量称为变量C语言允许把数据存放在变量中,每一个变量都由一个变量名来标识。每个变量都必须具有一个类型。每个变量根据它的类型,在内存中占据一定数目的字节单元,该变量的值就存放在这些内存单元中。变量在编译系统对程序进行编译时,根据每一个变量的类型,给它分配一定数目的字节单元,同时,系统内部维护着变量名和它所占内存单元地地址的对应信息。变量123prod变量名 prod 实际上是一个符号地址内存单元的地址123是变量 prod的变量值内存单元的内容变量的本质就是命名的内存单元变量的命名规定C语言中,变量(标识符)的命名规定:只能由大小写字母、下划线和数字组成;开头的第一个字符只能是大小写字母或下划线;大小写是敏感的;不能与C的关键字重复;名字在整个程序中必须是唯一的;名字的长度不能超过系统的规定。变量合法命名的例子合法的C变量名:area, sum, answer, month,name, Name, step1, step2,number_of_student,average_score, class1变量非法命名的例子非法的C变量名:3rd_entry (数字开头)all$done (包含非法字符$)the end (包含空格)int (系统关键字)变量的命名习惯避免使用无意义的名字,应尽量做到“见名知意”。尽量使用代表变量实际含义的英文单词或缩写作标识符名;当名字包含多个单词时,通常使每个单词的首字母大写;以下划线开头的名字通常是系统内部保留的标识符名;C语言中,变量(标识符)的命名习惯:好的变量命名的例子好的C变量名:CircleArea, TotalScore,UserSelection, choice,StudentNumber,AverageScore,SampleFrequency不好的变量命名的例子不是很好的C变量名:a, aa (不知何意)numberofstudent (全部小写难以辨认)totaltotals (名字太相似)变量的定义和使用C语言中规定,变量必须“先定义,后使用”变量定义具有三个目的:⑴ 定义变量名;⑵ 定义变量类型;⑶ 向程序员描述该变量。系统将根据变量的定义为其分配相应大小的内存单元。变量的定义和使用int radius; /* radius of circle */关键字 int 告诉编译系统我要定义一个整型变量,用它来保存一个整型的数值。这是变量的名字。以后的程序中就用这个名字来访问这个变量。注释用来解释这个变量,它是给程序员的信息。对程序中的重要变量作注释是很好的编程习惯。变量的定义格式变量定义的一般格式是:type name; /* comment */类型 名字; /* 注释 */int age; /* age of student */float score; /* score of student */float average; /* average of score */long number; /* number of student */char UserSelection; /* key user hit */变量先定义后使用的好处变量“先定义,后使用”的好处是:C只允许使用定义过的变量。凡是未被事先定义的符号,不作为变量名。这样可以检查出程序中错误使用的变量名。#include int main(){int a , b , sum ;a=123; b=456;sum=a+b;printf(“sum=%d\n”, sim);return(0);}变量先定义后使用的好处此处定义的变量名是 sum此处由于输入错误或其它原因,出现了名字为 sim 的符号。由于该符号在这之前从未定义过,所以在编译时,便会出现错误信息。Error:Undefined symbol 'sim'变量先定义后使用的好处变量“先定义,后使用”的好处是:C定义变量时指明了变量的类型,这样在对源程序进行编译时,可以就此类型检查该变量所参与的运算是否合法。#include int main(){int a, re; float c;a=23; c=2.5;re= a%c ;printf(“余数=%d\n”, re);return(0);}变量先定义后使用的好处变量a是整型符号%是“求余”运算符,它要求参与运算的数据必须是整型的。而变量c却是实型的,所以编译时就会出现错误信息。Error:Illegal use of floating point变量c是实型3.3 整型数据整型常量整型变量整型数据在内存中的存储方式整型常量十进制整型常量:如: 123, -456, 0整型常量即整常数。C中有下面三种形式的整型常量:整型常量八进制整型常量:以数字“0”开头的整型常量如: 0123, 即(0123)8 ,等于(83)10八进制整型常量中不应出现“7”以上的数码。如: 0128, 0209 都是非法的八进制整型常量。整型常量十六进制整型常量:以 “0x”开头的整型常量如: 0xFF, 即(0xFF)16 ,等于(255)10十六进制整型常量中不应出现“F”以上的数码。整型变量的分类注:有符号整型变量中,关键字signed可以省略。类型 关键字 字节数目 (TC2.0)有符号signed 基本整型 signed int 2 字节短整型 signed short int (signed short) 2 字节长整型 signed long int (signed long) 4 字节无符号unsigned 基本整型 unsigned int 2 字节短整型 unsigned short int (unsigned short) 2 字节长整型 unsigned long int (unsigned long) 4 字节整型数据在内存中的存储方式数据在内存中是以“二进制数”的形式存放的。更准确的说:数据在内存中是以“二进制数”的“补码”形式存放的。整型数据在内存中的存储方式如果是语句:int a=-10; 那么内存中实际存放的是 -10 的补码a -10 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0例如有语句:int a=10; 那么内存中实际存放的是 +10 的补码a 10 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0整型数据补码的求法:正数正数的补码等于正数的原码(二进制表示)。0000000010100000+10的原码:+10的二进制数,最高位为00000000010100000+10的反码:等于+10的原码0000000010100000+10的补码:等于+10的原码整型数据补码的求法:负数负数的补码等于负数的反码加1。0000000010100001-10的原码:+10的原码,将最高位变为11111111101011111-10的反码:原码最高位不动,其余位取反1111111011011111-10的补码:反码整个加上1有符号和无符号的区别◆ 有符号和无符号的区别是什么?关键在于“最高位”的性质。对于有符号数据:把最高位作为符号位表示数据的符号(0为正,1为负),其余位保存具体数值,因此可以用于正确保存限值范围内的正数或负数。对于无符号数据:最高位不作为符号位,全部的位都用来表示数据的数值,因此可以用于正确保存限值范围内的正数。整型数据的表示范围◆由于每一种整型数据占有的字节单元数目都是有限的,所以每一种整型数据可表示的数的范围也是有限的。◆由于不同类型的整型数据占有的字节单元数目是不同的,所以不同类型的整型数据可表示的数的范围也是不同的。整型数据的表示范围ANSI C标准定义的整型类型类 型 Bit 数 有效表示范围[signed] int 16 -32768 ~ +32767unsigned int 16 0 ~ +65535[signed] short [int] 16 -32768 ~ +32767unsigned short [int] 16 0 ~ +65535[signed] long [int] 32 -2147483648 ~ +2147483647unsigned long [int] 32 0 ~ +4294967295整型变量的定义变量的定义一般放在函数体的开头部分。定义方法:类型符 变量名列表; (用逗号分隔)如:int num;unsigned int age,score;long distant,SampleTimes;3.4 实型数据实型常量实型变量实型数据在内存中的存储方式实型常量十进制小数形式:由数字和小数点组成。(小数点必须有)如: 10.23, -0.456, 78.0, 0.0, 89.实数又叫“浮点数”。实数常量有两种表示形式:实型常量指数形式:±s E ±n 或 ±s e ±n 形式。注意: E或e前面必须有数字,后面的指数必须为整数。如: 218.3 = 218.3E0 = 2183E-1= 0.02183E+4实型数据在内存中的存储方式实型数据在内存中是以“尾数”和“阶码”的形式存放的。在一个实型数据(浮点数)所占字节单元中,一部分位用来保存阶码,一部分位用来保存尾数。± 尾数部分 ± 阶码部分不同的编译系统位数分配不同。总之,尾数部分位数越多,可以表示的数的有效数字位数就越多,精度也就越高;阶码部分位数越多,可以表示的数的范围就越大。实型变量的分类类型 单精度 双精度 长双精度关键字 float double long double字节数 Turbo C 4 字节 8 字节 16 字节有效数字位数 6~7位 15~16位 18~19位数值范围 10-37~1038 10-307~10308 10-4931~104932由于不同的实型数据是用不同数目的有限字节单元保存的,所以它们能表示的数值范围是不同的,能保留的有效数字位数也是不同而且是有限的。实型变量的定义变量的定义一般放在函数体的开头部分。定义方法:类型符 变量名列表; (用逗号分隔)如:float TotalScore, AverScore;double area, result;Turb c 中实型数据使用中应注意1.实型常数只有一种进制(十进制)2.所有实型常数都被默认为double3.绝对值小于1的实型数,其小数点前的零可以省略:如0.09可写作5.0954.Turbo C中默认输出实型数时,最多只保留小数点后六位3.5 字符型数据字符常量字符变量字符型数据在内存中的存储方式字符串常量字符型常量比如: ‘a’ , ‘A’ , ‘d’ , ‘D’ ,‘ ’ , ‘%’ ,‘$’ , ‘2’ , ‘4’注意: ‘a’ 和 ‘A’ , ‘d’ 和 ‘D’ 都是不同的字符常量。C的字符常量是用单引号(‘)括起来的一个字符。转义字符常量另外:C语言还存在另一种形式的特殊的字符常量。它是用单引号括起来的以一个“\”字符开头的规定的字符序列。“\” 字符是反斜杠字符。这种特殊的字符常量称为“转义字符常量”。比如: ‘\n’ , ‘\t’ , ‘\r’ , ‘\\’转义字符常量及其含义转义字符 含义 ASCII 代码控制字符 \n 回车换行(光标移到下一行第一列) 10\t 水平制表 (光标移到下一个Tab位置) 9\b 退格(光标移回到同一行前一列) 8\r 回车(光标移到同一行第一列) 13\f 换页(光标移到下一页开头) 12转义字符常量及其含义转义字符 含义 ASCII 代码特殊标点符号 \\ 反斜杠字符 “\” 92\‘ 单引号(撇号)字符 ‘ 39\“ 双引号字符 “ 34\ddd 1到3位8进制数代表的字符\xhh 1到2位16进制数代表的字符printf(“A=\xAB”) 的结果是?字符型变量字符型变量用来存储字符型常量。一个字符变量只能保存一个字符。类型 关键字 占字节数目有符号 [signed] char 1 字节无符号 unsigned char 1 字节字符型变量的分类:字符型变量的定义变量的定义一般放在函数体的开头部分。定义方法:类型符 变量名列表; (用逗号分隔)如:char InputKey, UserSelection;unsigned char lines, grade;字符型数据在内存中的存储方式每个字符型数据在内存中占一个字节,该字节中实际保存的是该字符的ASCII代码。ASCII (美国信息交换标准代码) American Standard Code for Information InterchangeASCII 代码实际上是一个0~255之间的整数。每一个整数代表着一个不同的字符,即每一个字符都有一个不同的整数代码。对字符型数据来讲,它的那一个字节中保存的就是这个整数。字符型数据在内存中的存储方式常用字符的ASCII代码表ASCII值 字符 ASCII值 字符 ASCII值 字符 ASCII值 字符0 NUL 48 0 65 A 97 a10 LF 换行 49 1 66 B 98 b13 CR 回车 … … … … … …32 空格 57 9 90 Z 122 z字符型数据在内存中的存储方式A0 1 0 0 0 0 0 165这个字节存储的内容是01000001。如果它是一个整型数据,那就是整数65;如果它是一个字符型数据,那就是字符‘A’。ASCII 值=65字符型数据在内存中的存储方式可见,字符型数据存储对应的 ASCII 码,而 ASCII 码是一个整数,按整型数据的存储方式存储,因此在C语言中两者的存储方式是类似的,从而在一定范围内可以通用,即两种类型的数据可以相互赋值,相互输出,也可以在一起进行运算。“一定范围内” 指的是“一个字节”所能保存的整数范围内。整型数据和字符型数据通用:例1#include void main( ){char c1, c2; int n1, n2;c1 = ‘b’; c2 = 97;n1 = 98; n2 = ‘a’;printf(“c1=%c, c2=%c\n”, c1, c2);printf(“n1=%d, n2=%d\n”, n1, n2);printf(“c1=%d, c2=%d\n”, c1, c2);printf(“n1=%c, n2=%c\n”, n1, n2);}既可以把整型数据赋值给字符型变量。整型数据和字符型数据通用:例1#include void main( ){char c1, c2; int n1, n2;c1 = ‘b’; c2 = 97;n1 = 98; n2 = ‘a’;printf(“c1=%c, c2=%c\n”, c1, c2);printf(“n1=%d, n2=%d\n”, n1, n2);printf(“c1=%d, c2=%d\n”, c1, c2);printf(“n1=%c, n2=%c\n”, n1, n2);}也可以把字符型数据赋值给整型变量。整型数据和字符型数据通用:例1#include void main( ){char c1, c2; int n1, n2;c1 = ‘b’; c2 = 97;n1 = 98; n2 = ‘a’;printf(“c1=%c, c2=%c\n”, c1, c2);printf(“n1=%d, n2=%d\n”, n1, n2);printf(“c1=%d, c2=%d\n”, c1, c2);printf(“n1=%c, n2=%c\n”, n1, n2);}既可以把字符型数据按整数形式输出。(%d 整型格式符)整型数据和字符型数据通用:例1#include void main( ){char c1, c2; int n1, n2;c1 = ‘b’; c2 = 97;n1 = 98; n2 = ‘a’;printf(“c1=%c, c2=%c\n”, c1, c2);printf(“n1=%d, n2=%d\n”, n1, n2);printf(“c1=%d, c2=%d\n”, c1, c2);printf(“n1=%c, n2=%c\n”, n1, n2);}也可以把整型数据按字符形式输出。 (%c 字符格式符)整型数据和字符型数据通用:例1#include void main( ){char c1, c2; int n1, n2;c1 = ‘b’; c2 = 97;n1 = 98; n2 = ‘a’;printf(“c1=%c, c2=%c\n”, c1, c2);printf(“n1=%d, n2=%d\n”, n1, n2);printf(“c1=%d, c2=%d\n”, c1, c2);printf(“n1=%c, n2=%c\n”, n1, n2);}运行结果:c1=b,c2=an1=98,n2=97c1=98,c2=97n1=b,n2=a整型数据和字符型数据通用:例2#include void main( ){char c1, c2, c3;c1 = ‘A’; c2 = ‘b’; c3 = ‘ ’;c1 = c1 + 32;c2 = c2 – c3;printf(“c1=%c, c1=%d\n”, c1, c1);printf(“c2=%c, c2=%d\n”, c2, c2);}空格的ASCII码=32整型数据和字符型数据通用:例2#include void main( ){char c1, c2, c3;c1 = ‘A’; c2 = ‘b’; c3 = ‘ ’;c1 = c1 + 32;c2 = c2 – c3;printf(“c1=%c, c1=%d\n”, c1, c1);printf(“c2=%c, c2=%d\n”, c2, c2);}字符数据可以和整型数据一起进行算术运算。整型数据和字符型数据通用:例2#include void main( ){char c1, c2, c3c1 = ‘A’; c2 = ‘b’; c3 = ‘ ’;c1 = c1 + 32;c2 = c2 – c3;printf(“c1=%c, c1=%d\n”, c1, c1);printf(“c2=%c, c2=%d\n”, c2, c2);}字符数据之间也可以进行算术运算。整型数据和字符型数据通用:例2#include void main( ){char c1, c2, c3;c1 = ‘A’; c2 = ‘b’; c3 = ‘ ’;c1 = c1 + 32;c2 = c2 – c3;printf(“c1=%c, c1=%d\n”, c1, c1);printf(“c2=%c, c2=%d\n”, c2, c2);}实际上这些运算都是整数之间的运算。整型数据和字符型数据通用:例2#include void main( ){char c1, c2, c3;c1 = ‘A’; c2 = ‘b’; c3 = ‘ ’;c1 = c1 + 32;c2 = c2 – c3;printf(“c1=%c, c1=%d\n”, c1, c1);printf(“c2=%c, c2=%d\n”, c2, c2);}既可以把字符型数据按字符形式%c输出,也可以把字符型数据按整数形式%d输出。整型数据和字符型数据通用:例2#include void main( ){char c1, c2, c3;c1 = ‘A’; c2 = ‘b’; c3 = ‘ ’;c1 = c1 + 32;c2 = c2 – c3;printf(“c1=%c, c1=%d\n”, c1, c1);printf(“c2=%c, c2=%d\n”, c2, c2);}运行结果:c1=a,c1=97c2=B,c2=66字符串常量比如: “a” , “A” , “hello” ,“OK” , “SiChuanDaXue”C的字符串常量是用双引号(“)括起来的若干个字符。◆字符串常量在内存中占用连续的字节单元,每个字符按顺序占一个字节。字符串常量 C语言规定:自动在每一个字符串常量的结尾加一个“字符串结束标志字符”,以便系统据此判断字符串是否结束。一个字符串常量中包含的字符个数是不确定的。那么,系统在处理字符串常量时如何知道何处结束呢?字符串结束标志字符◆ ‘\0’ 是ASCII码为0的字符,即位于字符表中的第一个NUL字符。它是一个“空操作字符”,就是说,它既不引起任何控制动作,也不产生任何显示。C的字符串结束标志字符是 ‘\0’ 。◆ 因此,C的字符串常量在内存中占用的字节数等于可见字符数加上1(即结束标志字符占用的一个字节)。字符串结束标志字符“HELLO”H E L L O \0字符串结束标志字符也是一个字符,也要占用一个字节。总共占用6个字节5个可见字符‘A’和“A”的区别◆由于C语言对每一个字符串常量均在其末尾自动添加一个字符串结束标志字符‘\0’,因此:‘A’ 是字符常量,占一个字节单元“A” 是字符串常量,占两个字节单元,包括‘a’和‘\0’两个字符‘A’和“A”的区别◆不能把一个字符串常量赋值给一个字符变量。如 char c1 = “A” ; 是错误的。 为什么是错误的。原因1:“A” 占两个字节单元,而字符变量c1只有一个字节,所以无法赋值;原因2:真正的原因。C语言实际上把字符串处理成字符指针,把一个指针赋值给一个字符变量当然是错误的。课堂练习◆ 问题1:字符串 “” 占几个字节单元?(A) 0个(B) 1个(C) 2个◆ 问题2:这样的赋值char c1=“” ;是否正确?(A) 正确(B) 不正确答案:(B)答案:(B)课堂练习◆ 问题3:1 和 ‘1’ 是否相同?为什么?答案:不相同。因为前者是整数1,它的值是1,而后者是字符 ‘1’,它的值是49(ASCII码)。◆ 问题4:1+‘0’ 和 ‘1’ 是否相同?答案:相同。因为字符 ‘0’ 的值是48,加1等于49 ,正好是字符 ‘1’ 的值。(共50张PPT)第8章 函 数C语言是通过函数来实现模块化程序设计的。所以较大的C语言应用程序,往往是由多个函数组成的,每个函数分别对应各自的功能模块。8.1 函数的定义与调用8.2 函数的嵌套调用与递归调用8.3 数组作为函数参数8.4 内部变量与外部变量8.5 内部函数与外部函数8.6 变量的动态存储与静态存储[Return]8.1 函数的定义与调用8.1.1 函数的定义8.1.2 函数的返回值与函数类型8.1.3 对被调用函数的说明和函数原型8.1.4 函数的调用8.1.5 函数的形参与实参[Return]8.1 .1 函数的定义1.任何函数(包括主函数main())都是由函数说明和函数体两部分组成。根据函数是否需要参数,可将函数分为无参函数和有参函数两种。(1)无参函数的一般形式函数类型 函数名( void ){ 说明语句部分;可执行语句部分;}注意:在旧标准中,函数可以缺省参数表。但在新标准中,函数不可缺省参数表;如果不需要参数,则用“void”表示,主函数main()例外。(2)有参函数的一般形式函数类型 函数名( 数据类型 参数[,数据类型 参数2……] ){ 说明语句部分;可执行语句部分;}有参函数比无参函数多了一个参数表。调用有参函数时,调用函数将赋予这些参数实际的值。为了与调用函数提供的实际参数区别开,将函数定义中的参数表称为形式参数表,简称形参表。[案例7.1] 定义一个函数,用于求两个数中的大数。/*案例代码文件名:AL7_1.C*//*功能:定义一个求较大数的函数并在主函数中调用*/int max(int n1, int n2) /*定义一个函数max()*/{ return (n1>n2 n1:n2);}main(){ int max(int n1, int n2); /*函数说明*/int num1,num2;printf("input two numbers:\n");scanf("%d%d", &num1, &num2);printf("max=%d\n", max(num1,num2));getch(); /*使程序暂停,按任一键继续*/}2.说明(1)函数定义不允许嵌套。在C语言中,所有函数(包括主函数main())都是平行的。一个函数的定义,可以放在程序中的任意位置,主函数main()之前或之后。但在一个函数的函数体内,不能再定义另一个函数,即不能嵌套定义。(2)空函数──既无参数、函数体又为空的函数。其一般形式为:[函数类型] 函数名(void){ }(3)在老版本C语言中,参数类型说明允许放在函数说明部分的第2行单独指定。[Return]8.1.2 函数的返回值与函数类型C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为有返回值函数和无返回值函数两种。1.函数返回值与return语句有参函数的返回值,是通过函数中的return语句来获得的。(1)return语句的一般格式: return ( 返回值表达式 );(2)return语句的功能:返回调用函数,并将“返回值表达式”的值带给调用函数。注意:调用函数中无return语句,并不是不返回一个值,而是一个不确定的值。为了明确表示不返回值,可以用“void”定义成“无(空)类型”。2.函数类型在定义函数时,对函数类型的说明,应与return语句中、返回值表达式的类型一致。如果不一致,则以函数类型为准。如果缺省函数类型,则系统一律按整型处理。良好的程序设计习惯:为了使程序具有良好的可读性并减少出错,凡不要求返回值的函数都应定义为空类型;即使函数类型为整型,也不使用系统的缺省处理。[Return]8.1.3 对被调用函数的说明和函数原型在ANSI C新标准中,采用函数原型方式,对被调用函数进行说明,其一般格式如下:函数类型 函数名(数据类型[ 参数名][, 数据类型[ 参数名2]…]);C语言同时又规定,在以下2种情况下,可以省去对被调用函数的说明:(1)当被调用函数的函数定义出现在调用函数之前时。因为在调用之前,编译系统已经知道了被调用函数的函数类型、参数个数、类型和顺序。(2)如果在所有函数定义之前,在函数外部(例如文件开始处)预先对各个函数进行了说明,则在调用函数中可缺省对被调用函数的说明。[Return]8.1.4 函数的调用在程序中,是通过对函数的调用来执行函数体的,其过程与其它语言的子程序调用相似。C语言中,函数调用的一般形式为: 函数名([实际参数表])切记:实参的个数、类型和顺序,应该与被调用函数所要求的参数个数、类型和顺序一致,才能正确地进行数据传递。在C语言中,可以用以下几种方式调用函数:(1)函数表达式。函数作为表达式的一项,出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。(2)函数语句。C语言中的函数可以只进行某些操作而不返回函数值,这时的函数调用可作为一条独立的语句。(3)函数实参。函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的。说明:(1)调用函数时,函数名称必须与具有该功能的自定义函数名称完全一致。(2)实参在类型上按顺序与形参,必须一一对应和匹配。如果类型不匹配,C编译程序将按赋值兼容的规则进行转换。如果实参和形参的类型不赋值兼容,通常并不给出出错信息,且程序仍然继续执行,只是得不到正确的结果。(3)如果实参表中包括多个参数,对实参的求值顺序随系统而异。有的系统按自左向右顺序求实参的值,有的系统则相反。Turbo C和MS C是按自右向左的顺序进行的 。[Return]8.1.5 函数的形参与实参函数的参数分为形参和实参两种,作用是实现数据传送。形参出现在函数定义中,只能在该函数体内使用。发生函数调用时,调用函数把实参的值复制1份,传送给被调用函数的形参,从而实现调用函数向被调用函数的数据传送。[案例7.3] 实参对形参的数据传递。/*实参对形参的数据传递。*//*案例代码文件名:AL7_3.C*/void main(){ void s(int n); /*说明函数*/int n=100; /*定义实参n,并初始化*/s(n); /*调用函数*/printf("n_s=%d\n",n); /*输出调用后实参的值,便于进行比较*/getch();}/* */void s(int n){ int i;printf("n_x=%d\n",n); /*输出改变前形参的值*/for(i=n-1; i>=1; i--) n=n+i; /*改变形参的值*/printf("n_x=%d\n",n); /*输出改变后形参的值*/}说明:(1)实参可以是常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此,应预先用赋值、输入等办法,使实参获得确定的值。(2)形参变量只有在被调用时,才分配内存单元;调用结束时,即刻释放所分配的内存单元。因此,形参只有在该函数内有效。调用结束,返回调用函数后,则不能再使用该形参变量。(3)实参对形参的数据传送是单向的,即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。(4)实参和形参占用不同的内存单元,即使同名也互不影响。[Return]8.2 函数的嵌套调用和递归调用8.2.1 函数的嵌套调用函数的嵌套调用是指,在执行被调用函数时,被调用函数又调用了其它函数。这与其它语言的子程序嵌套调用的情形是类似的,其关系可表示如图7-1。[案例7.4] 计算s=1k+2k+3k+……+N k/*案例代码文件名:AL7_4.C*//*功能:函数的嵌套调用*/#define K 4#define N 5long f1(int n,int k) /*计算n的k次方*/{ long power=n;int i;for(i=1;ireturn power;}long f2(int n,int k) /*计算1到n的k次方之累加和*/{ long sum=0;int i;for(i=1;i<=n;i++) sum += f1(i, k);return sum;}main(){ printf("Sum of %d powers of integers from 1 to %d = ",K,N);printf("%d\n",f2(N,K));getch();}8.2.2 函数的递归调用函数的递归调用是指,一个函数在它的函数体内,直接或间接地调用它自身。C语言允许函数的递归调用。在递归调用中,调用函数又是被调用函数,执行递归函数将反复调用其自身。每调用一次就进入新的一层。为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。常用的办法是加条件判断,满足某种条件后就不再作递归调用,然后逐层返回。[案例7.5] 用递归法计算n!。/*案例代码文件名:AL7_5.C*//*功能:通过函数的递归调用计算阶乘*/long power(int n){ long f;if(n>1) f=power(n-1)*n;else f=1;return(f);}main(){ int n;long y;printf("input a inteager number:\n");scanf("%d",&n);y=power(n);printf("%d!=%ld\n",n,y);getch();}[Return]8.3 数组作为函数参数数组用作函数参数有两种形式:一种是把数组元素(又称下标变量)作为实参使用;另一种是把数组名作为函数的形参和实参使用。8.3.1 数组元素作为函数参数8.3.2 数组名作为函数的形参和实参[Return]8.3.1 数组元素作为函数参数数组元素就是下标变量,它与普通变量并无区别。数组元素只能用作函数实参,其用法与普通变量完全相同:在发生函数调用时,把数组元素的值传送给形参,实现单向值传送。[案例7.6] 写一函数,统计字符串中字母的个数。/*案例代码文件名:AL7_6.C*//*功能:数组元素作为函数实参*/int isalp(char c){ if (c>='a'&&c<='z'||c>='A'&&c<='Z')return(1);else return(0);}main(){ int i,num=0;char str[255];printf("Input a string: ");gets(str);for(i=0;str[i]!='\0';i++)if (isalp(str[i])) num++;puts(str);printf("num=%d\n",num);getch();}说明:(1)用数组元素作实参时,只要数组类型和函数的形参类型一致即可,并不要求函数的形参也是下标变量。换句话说,对数组元素的处理是按普通变量对待的。(2)在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。在函数调用时发生的值传送,是把实参变量的值赋予形参变量。[Return]8.3.2 数组名作为函数的形参和实参数组名作函数参数时,既可以作形参,也可以作实参。数组名作函数参数时,要求形参和相对应的实参都必须是类型相同的数组(或指向数组的指针变量),都必须有明确的数组说明[案例7.7] 已知某个学生5门课程的成绩,求平均成绩。/*案例代码文件名:AL7_7.C*/float aver(float a[ ]) /*求平均值函数*/{ int i;float av,s=a[0];for(i=1;i<5;i++) s += a[i];av=s/5;return av;}void main(){ float sco[5],av;int i;printf("\ninput 5 scores:\n");for(i=0;i<5;i++) scanf("%f",&sco[i]);av=aver(sco); /*调用函数,实参为一数组名*/printf("average score is %5.2f\n",av);getch();}说明:(1)用数组名作函数参数,应该在调用函数和被调用函数中分别定义数组,且数据类型必须一致,否则结果将出错。例如,在本案例中,形参数组为a[],实参数组为sco[],它们的数据类型相同。(2)C编译系统对形参数组大小不作检查,所以形参数组可以不指定大小。例如,本案例中的形参数组a[]。如果指定形参数组的大小,则实参数组的大小必须大于等于形参数组,否则因形参数组的部分元素没有确定值而导致计算结果错误。[Return]8.4 内部变量与外部变量C语言中所有的变量都有自己的作用域。变量说明的位置不同,其作用域也不同,据此将C语言中的变量分为内部变量和外部变量。8.4.1 内部变量8.4.2 外部变量[Return]8.4.1 内部变量在一个函数内部说明的变量是内部变量,它只在该函数范围内有效。也就是说,只有在包含变量说明的函数内部,才能使用被说明的变量,在此函数之外就不能使用这些变量了。所以内部变量也称“局部变量”。例如:int f1(int a) /*函数f1*/{ int b,c;……} /*a,b,c作用域:仅限于函数f1()中*/ int f2(int x) /*函数f2*/{ int y,z;……} /*x,y,z作用域:仅限于函数f2()中*/ main(){ int m,n;……} /*m,n作用域:仅限于函数main()中*/关于局部变量的作用域还要说明以下几点:1.主函数main()中定义的内部变量,也只能在主函数中使用,其它函数不能使用。同时,主函数中也不能使用其它函数中定义的内部变量。因为主函数也是一个函数,与其它函数是平行关系。这一点是与其它语言不同的,应予以注意。2.形参变量也是内部变量,属于被调用函数;实参变量,则是调用函数的内部变量。3.允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。4.在复合语句中也可定义变量,其作用域只在复合语句范围内。[Return]8.4.2 外部变量在函数外部定义的变量称为外部变量。以此类推,在函数外部定义的数组就称为外部数组。外部变量不属于任何一个函数,其作用域是:从外部变量的定义位置开始,到本文件结束为止。外部变量可被作用域内的所有函数直接引用,所以外部变量又称全局变量。[案例7.9] 输入长方体的长(l)、宽(w)、高(h),求长方体体积及正、侧、顶三个面的面积。/*案例代码文件名:AL7_9.C*//*功能:利用全局变量计算长方体的体积及三个面的面积*/int s1,s2,s3;int vs(int a,int b,int c){ int v;v=a*b*c; s1=a*b; s2=b*c; s3=a*c;return v;}main(){int v,l,w,h;clrscr();printf("\ninput length,width and height: ");scanf("%d%d%d",&l,&w,&h);v=vs(l,w,h);printf("v=%d s1=%d s2=%d s3=%d\n",v,s1,s2,s3);getch();}对于全局变量还有以下几点说明:(1)外部变量可加强函数模块之间的数据联系,但又使这些函数依赖这些外部变量,因而使得这些函数的独立性降低。从模块化程序设计的观点来看这是不利的,因此不是非用不可时,不要使用外部变量。(2)在同一源文件中,允许外部变量和内部变量同名。在内部变量的作用域内,外部变量将被屏蔽而不起作用。(3)外部变量的作用域是从定义点到本文件结束。如果定义点之前的函数需要引用这些外部变量时,需要在函数内对被引用的外部变量进行说明。外部变量说明的一般形式为:extern 数据类型 外部变量[,外部变量2……];注意:外部变量的定义和外部变量的说明是两回事。外部变量的定义,必须在所有的函数之外,且只能定义一次。而外部变量的说明,出现在要使用该外部变量的函数内,而且可以出现多次。[案例7.10] 外部变量的定义与说明。/*案例代码文件名:AL7_10.C*/int vs(int xl,int xw){ extern int xh; /*外部变量xh的说明*/int v;v=xl*xw*xh; /*直接使用外部变量xh的值*/return v;}main(){ extern int xw,xh; /*外部变量的说明*/int xl=5; /*内部变量的定义*/printf("xl=%d,xw=%d,xh=%d\nv=%d",xl,xw,xh,vs(xl,xw));}int xl=3,xw=4,xh=5; /*外部变量xl、xw、xh的定义*/[Return]8.5 内部函数和外部函数8.5.1 内部函数(又称静态函数)8.5.2 外部函数8.5.3 多个源程序文件的编译和连接当一个源程序由多个源文件组成时,C语言根据函数能否被其它源文件中的函数调用,将函数分为内部函数和外部函数。[Return]8.5.1 内部函数(又称静态函数)如果在一个源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用,这种函数称为内部函数。定义一个内部函数,只需在函数类型前再加一个“static”关键字即可,如下所示:static 函数类型 函数名(函数参数表){……}关键字“static”,译成中文就是“静态的”,所以内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。[Return]8.5.2 外部函数外部函数的定义:在定义函数时,如果没有加关键字“static”,或冠以关键字“extern”,表示此函数是外部函数:[extern] 函数类型 函数名(函数参数表){……}调用外部函数时,需要对其进行说明:[extern] 函数类型 函数名(参数类型表)[,函数名2(参数类型表2)……];[案例7.11] 外部函数应用。(1)文件mainf.cmain(){ extern void input(…),process(…),output(…);input(…); process(…); output(…);}(2)文件subf1.c……extern void input(……) /*定义外部函数*/{……}(3)文件subf2.c……extern void process(……) /*定义外部函数*/{……}(4)文件subf3.c……extern void output(……) /*定义外部函数*/{……}[Return]8.5.3 多个源程序文件的编译和连接(1)一般过程编辑各源文件 → 创建Project(项目)文件 → 设置项目名称 → 编译、连接,运行,查看结果。(2)创建Project(项目)文件用编辑源文件相同的方法,创建一个扩展名为.PRJ的项目文件:该文件中仅包括将被编译、连接的各源文件名,一行一个,其扩展名.C可以缺省;文件名的顺序,仅影响编译的顺序,与运行无关。注意:如果有某个(些)源文件不在当前目录下,则应在文件名前冠以路径。(3)设置项目名称打开菜单,选取Project/Project name,输入项目文件名即可。(4)编译、连接,运行,查看结果与单个源文件相同。编译产生的目标文件,以及连接产生的可执行文件,它们的主文件名,均与项目文件的主文件名相同。注意:当前项目文件调试完毕后,应选取Project/Clear project,将其项目名称从“Project name”中清除(清除后为空)。否则,编译、连接和运行的,始终是该项目文件!(5)关于错误跟踪缺省时,仅跟踪当前一个源程序文件。如果希望自动跟踪项目中的所有源文件,则应将Options/Environment/Message Tracking开关置为“All files ”:连续按回车键,直至“All files”出现为止。此时,滚动消息窗口中的错误信息时,系统会自动加载相应的源文件到编辑窗口中。也可关闭跟踪(将“Message Tracking”置为“Off”)。此时,只要定位于感兴趣的错误信息上,然后回车,系统也会自动将相应源文件加载到编辑窗口中。[Return]8.6 变量的动态存储与静态存储简介在C语言中,对变量的存储类型说明有以下四种:自动变量(auto)、寄存器变量(register)、外部变量(extern)、静态变量(static)。自动变量和寄存器变量属于动态存储方式,外部变量和静态内部变量属于静态存储方式。8.6.1 内部变量的存储方式8.6.2 外部变量的存储方式[Return]8.6.1 内部变量的存储方式1.静态存储──静态内部变量(1)定义格式: static 数据类型 内部变量表;(2)存储特点1)静态内部变量属于静态存储。在程序执行过程中,即使所在函数调用结束也不释放。换句话说,在程序执行期间,静态内部变量始终存在,但其它函数是不能引用它们的。2)定义但不初始化,则自动赋以"0"(整型和实型)或'\0'(字符型);且每次调用它们所在的函数时,不再重新赋初值,只是保留上次调用结束时的值!(3)何时使用静态内部变量1)需要保留函数上一次调用结束时的值。2)变量只被引用而不改变其值。2.动态存储──自动局部变量(又称自动变量)(1)定义格式:[auto] 数据类型 变量表;(2)存储特点1)自动变量属于动态存储方式。在函数中定义的自动变量,只在该函数内有效;函数被调用时分配存储空间,调用结束就释放。在复合语句中定义的自动变量,只在该复合语句中有效;退出复合语句后,也不能再使用,否则将引起错误。2)定义而不初始化,则其值是不确定的。如果初始化,则赋初值操作是在调用时进行的,且每次调用都要重新赋一次初值。3)由于自动变量的作用域和生存期,都局限于定义它的个体内(函数或复合语句),因此不同的个体中允许使用同名的变量而不会混淆。即使在函数内定义的自动变量,也可与该函数内部的复合语句中定义的自动变量同名。建议:系统不会混淆,并不意味着人也不会混淆,所以尽量少用同名自动变量![案例7.13]自动变量与静态局部变量的存储特性。/*案例代码文件名:AL7_13.C*/void auto_static(void){ int var_auto=0; /*自动变量:每次调用都重新初始化*/static int var_static=0; /*静态局部变量:只初始化1次*/printf(“var_auto=%d, var_static=%d\n”, var_auto, var_static);++var_auto;++var_static;}main(){ int i;for(i=0; i<5; i++) auto_static();}3.寄存器存储──寄存器变量一般情况下,变量的值都是存储在内存中的。为提高执行效率,C语言允许将局部变量的值存放到寄存器中,这种变量就称为寄存器变量。定义格式如下:register 数据类型 变量表;(1)只有局部变量才能定义成寄存器变量,即全局变量不行。(2)对寄存器变量的实际处理,随系统而异。例如,微机上的MSC和TC 将寄存器变量实际当作自动变量处理。(3)允许使用的寄存器数目是有限的,不能定义任意多个寄存器变量。[Return]8.6.2 外部变量的存储方式外部变量属于静态存储方式:(1)静态外部变量──只允许被本源文件中的函数引用其定义格式为: static 数据类型 外部变量表;(2)非静态外部变量──允许被其它源文件中的函数引用定义时缺省static关键字的外部变量,即为非静态外部变量。其它源文件中的函数,引用非静态外部变量时,需要在引用函数所在的源文件中进行说明:extern 数据类型 外部变量表;注意:在函数内的extern变量说明,表示引用本源文件中的外部变量!而函数外(通常在文件开头)的extern变量说明,表示引用其它文件中的外部变量。静态局部变量和静态外部变量同属静态存储方式,但两者区别较大:(1)定义的位置不同。静态局部变量在函数内定义,静态外部变量在函数外定义。(2)作用域不同。静态局部变量属于内部变量,其作用域仅限于定义它的函数内;虽然生存期为整个源程序,但其它函数是不能使用它的。静态外部变量在函数外定义,其作用域为定义它的源文件内;生存期为整个源程序,但其它源文件中的函数也是不能使用它的。(3)初始化处理不同。静态局部变量,仅在第1次调用它所在的函数时被初始化,当再次调用定义它的函数时,不再初始化,而是保留上1次调用结束时的值。而静态外部变量是在函数外定义的,不存在静态内部变量的“重复”初始化问题,其当前值由最近1次给它赋值的操作决定。务必牢记:把局部变量改变为静态内部变量后,改变了它的存储方式,即改变了它的生存期。把外部变量改变为静态外部变量后,改变了它的作用域,限制了它的使用范围。因此,关键字“static”在不同的地方所起的作用是不同的。[Return](共33张PPT)第13章 文 件在程序运行时,程序本身和数据一般都存放在内存中。当程序运行结束后,存放在内存中的数据被释放。如果需要长期保存程序运行所需的原始数据,或程序运行产生的结果,就必须以文件形式存储到外部存储介质上。13.1 C语言文件概述13.2 文件的打开与关闭13.3 文件的读写操作13.4 位置指针与文件定位13.5 出错检测[Return]13.1 C语言文件概述1.文件与文件名 文件是指存放在外部存储介质上的数据集合。为标识一个文件,每个文件都必须有一个文件名,其一般结构为:主文件名[.扩展名] 文件命名规则,遵循操作系统的约定。2.文件分类可以从不同的角度对文件进行分类:(1)根据文件的内容,可分为程序文件和数据文件,程序文件又可分为源文件、目标文件和可执行文件。(2)根据文件的组织形式,可分为顺序存取文件和随机存取文件。(3)根据文件的存储形式,可分为ASCII码文件和二进制文件。ASCII码文件的每1个字节存储1个字符,因而便于对字符进行逐个处理。但一般占用存储空间较多,而且要花费转换时间(二进制与ASCII码之间的转换)。二进制文件是把内存中的数据,原样输出到磁盘文件中。可以节省存储空间和转换时间,但1个字节并不对应1个字符,不能直接输出字符形式。3.读文件与写文件所谓读文件是指,将磁盘文件中的数据传送到计算机内存的操作。所谓写文件是指,从计算机内存向磁盘文件中传送数据的操作。4.构成文件的基本单元与流式文件C语言将文件看作是由一个一个的字符(ASCII码文件)或字节(二进制文件)组成的。将这种文件称为流式文件。而在其它高级语言中,组成文件的基本单位是记录,对文件操作的基本单位也是记录。5.文件类型FILE系统给每个打开的文件都在内存中开辟一个区域,用于存放文件的有关信息(如文件名、文件位置等)。这些信息保存在一个结构类型变量中,该结构类型由系统定义、取名为FILE。注意:结构类型名“FILE”必须大写。6.ANSI C的缓冲文件系统所谓缓冲文件系统是指,系统自动地在内存区为每个正在使用的文件开辟一个缓冲区。从内存向磁盘输出数据时,必须首先输出到缓冲区中。待缓冲区装满后,再一起输出到磁盘文件中。从磁盘文件向内存读入数据时,则正好相反:首先将一批数据读入到缓冲区中,再从缓冲区中将数据逐个送到程序数据区。[Return]13.2 文件的打开与关闭对文件进行操作之前,必须先打开该文件;使用结束后,应立即关闭,以免数据丢失。C语言规定了标准输入输出函数库,用fopen()函数打开一个文件,用fclose()函数关闭一个文件。13.2.1 文件的打开──fopen()函数1.用法: FILE *fopen("文件名","操作方式");2.功能:返回一个指向指定文件的指针。3.函数原型:stdio.h 。注:对文件操作的库函数,函数原型均在头文件stdio.h中。后续函数不再赘述。(1)“文件名”是指要打开(或创建)的文件名。如果使用字符数组(或字符指针),则不使用双引号。(2)“操作方式”如表12-1所示。例如,FILE *fp;fp=fopen("data.99","r");3.说明(1)如果不能实现打开指定文件的操作,则fopen()函数返回一个空指针NULL (其值在头文件stdio.h中被定义为0)。为增强程序的可靠性,常用下面的方法打开一个文件:if((fp=fopen("文件名","操作方式"))==NULL){ printf("can not open this file\n");exit(0);}●关于exit()函数1)用法:void exit([程序状态值]);2)功能:关闭已打开的所有文件,结束程序运行,返回操作系统,并将“程序状态值”返回给操作系统。当“程序状态值”为0时,表示程序正常退出;非0值时,表示程序出错退出。(2)“r(b)+”与“a(b)+”的区别:使用前者打开文件时,读写位置指针指向文件头;使用后者时,读写指针指向文件尾。(3)使用文本文件向计算机系统输入数据时,系统自动将回车换行符转换成一个换行符;在输出时,将换行符转换成回车和换行两个字符。使用二进制文件时,内存中的数据形式与数据文件中的形式完全一样,就不再进行转换。(4)有些C编译系统,可能并不完全提供上述对文件的操作方式,或采用的表示符号不同,请注意所使用系统的规定。(5)在程序开始运行时,系统自动打开三个标准文件,并分别定义了文件指针:1)标准输入文件——stdin:指向终端输入(一般为键盘)。如果程序中指定要从stdin所指的文件输入数据,就是从终端键盘上输入数据。2)标准输出文件——stdout:指向终端输出(一般为显示器)。3)标准错误文件——stderr:指向终端标准错误输出(一般为显示器)。13.2.2 文件的关闭──fcolse()函数1.用法: int fclose(FILE *文件指针);2.功能:关闭“文件指针”所指向的文件。如果正常关闭了文件,则函数返回值为0;否则,返回值为非0。例如,fclose(fp);/*关闭fp所指向的文件*/[Return]13.3 文件的读写操作文件打开之后,就可以对它进行读与写的操作了。13.3.1 读/写文件中的一个字符 13.3.2 读/写一个字符串13.3.3 读/写一个数据块13.3.4 对文件进行格式化读/写13.3.5 读/写函数的选用原则[Return]13.3.1 读/写文件中的一个字符1.将一个字符写到文件中──fputc()函数 [案例12.1] 将键盘上输入的一个字符串(以“@”作为结束字符),以ASCII码形式存储到一个磁盘文件中。/*案例代码文件名:AL12_1.C*/ /*程序功能:从键盘上输入一个字符串,存储到一个磁盘文件中*/ /*使用格式:可执行文件名 要创建的磁盘文件名*/ #include “stdio.h” main(int argc, char *argv[]) { FILE *fp;char ch; if(argc!=2) /*参数个数不对*/ { printf("the number of arguments not correct\n\n");printf(“Usage: 可执行文件名 filename \n”); exit(0); }if ((fp=fopen(argv[1],"w"))==NULL) /*打开文件失败*/{ printf("can not open this file\n");exit(0);}/*输入字符,并存储到指定文件中*/for( ; (ch=getchar()) != '@' ; )fputc(ch,fp); /*输入字符并存储到文件中*/fclose(fp); /*关闭文件*/} [程序演示]程序运行情况:abcdefg1234567@←┘库函数fputc():1)用法:int fputc(字符数据,文件指针);其中“字符数据”,既可以是字符常量,也可以是字符变量。2)功能:将字符数据输出到“文件指针”所指向的文件中去,同时将读写位置指针向前移动1个字节(即指向下一个写入位置)。如果输出成功,则函数返回值就是输出的字符数据;否则,返回一个符号常量EOF(其值在头文件stdio.h中,被定义为-1)。2.从文件中读入一个字符──fgetc()函数和feof()函数[案例12.2] 顺序显示[案例12.1]创建的磁盘ASCII码文件。/*案例代码文件名:AL12_2.C*//*程序功能:顺序显示一个磁盘ASCII码文件*//*参数:带参主函数,使用格式:可执行文件名 源文件名*/#include "stdio.h"main(int argc, char *argv[]){ FILE *fp;char ch;if(argc!=2) /*参数个数不对*/{ printf("the number of arguments not correct\n");printf(“\n Usage: 可执行文件名 源文件名");exit(0);}if ((fp=fopen(argv[1],"r"))==NULL){ printf("can not open source file\n");exit(0);}/*顺序输出文件的内容*/for(; (ch=fgetc(fp))!=EOF; )putchar(ch); /*顺序读入并显示*/fclose(fp); /*关闭打开的文件*/}[程序演示]程序运行情况:abcdefg1234567(1)库函数fgetc()1)用法:int fgetc(文件指针);2)功能:从“文件指针”所指向的文件中,读入一个字符,同时将读写位置指针向前移动1个字节(即指向下一个字符)。该函数无出错返回值。例如,fgetc(fp)表达式,从文件fp中读一个字符,同时将fp的读写位置指针向前移动到下一个字符。(2)关于符号常量EOF在对ASCII码文件执行读入操作时,如果遇到文件尾,则读操作函数返回一个文件结束标志EOF(其值在头文件stdio.h中被定义为-1)。在对二进制文件执行读入操作时,必须使用库函数feof()来判断是否遇到文件尾。[案例12.3] 实现制作ASCII码文件副本的功能。/*案例代码文件名:AL12_2.C*//*程序功能:制作ASCII码文件的副本*//*使用格式:可执行文件名 源文件名 目标文件名*/#include "stdio.h"main(int argc, char *argv[]){ FILE *input, *output; /* input:源文件指针, output:目标文件指针 */char ch;if(argc!=3) /*参数个数不对*/{ printf("the number of arguments not correct\n");printf("\n Usage: 可执行文件名 source-file dest-file");exit(0);}if ((fp=fopen(argv[1],"r"))==NULL) /*打开源文件失败*/{ printf("can not open source file\n");exit(0);}if ((fp=fopen(argv[2],"w"))==NULL) /*创建目标文件失败*/{ printf("can not create destination file\n");exit(0);}/*复制源文件到目标文件中*/for( ; (!feof(input)) ; ) fputc(fgetc(input),output);fclose(input); fclose(output); /*关闭源文件和目标文件*/}[程序演示]库函数feof(): 1)用法:int feof(文件指针); 2)功能:在执行读文件操作时,如果遇到文件尾,则函数返回逻辑真(1);否则,则返回逻辑假(0)。feof()函数同时适用于ASCII码文件和二进制文件。 例如,!feof(input))表示源文件(用于输入)未结束,循环继续。[Return]13.3.2 读/写一个字符串──fgets()和fputs()[案例12.4] 将键盘上输入的一个长度不超过80的字符串,以ASCII码形式存储到一个磁盘文件中;然后再输出到屏幕上。/*案例代码文件名:AL12_4.C*//*参数:可执行文件名 要创建的磁盘文件名*/#include "stdio.h"main(int argc, char *argv[]){ FILE *fp;char string[81]; /*字符数组用于暂存输入输出的字符串*/if(argc>2) /*参数太多,提示 出错*/{ printf("Too many parameters…\n\n");printf("Usage: 可执行文件名 filename\n");exit(0);}if(argc= =1) /*缺磁盘文件名,提示输入*/{ printf("Input the filename: ");gets(string); /*借用string暂存输入的文件名*/argv[1]=(char *)malloc(strlen(string)+1);/*给文件名参数申请内存空间*/strcpy(argv[1],string);/*复制文件名到形参中*/}if ((fp=fopen(argv[1],"w"))==NULL) /*打开文件失败*/{ printf("can not open this file\n");exit(0);}/*从键盘上输入字符串,并存储到指定文件中*/printf("Input a string: "); gets(string); /*从键盘上输入字符串*/fputs(string, fp); /*存储到指定文件*/fclose(fp);/*重新打开文件,读出其中的字符串,并输出到屏幕上*/if ((fp=fopen(argv[1],"r"))==NULL) /*打开文件失败*/{ printf("can not open this file\n");exit(0);}fgets(string, strlen(string)+1, fp); /*从文件中读一个字符串*/printf("Output the string: "); puts(string); /*将字符串输出到屏幕上*/fclose(fp);}[程序演示](1)为增强程序的可靠性,程序中对参数过多的情况,提示出错、并终止程序运行;而遗漏文件名时,提示用户输入。同时,为增强程序的人机交互性,凡是需要用户输入数据的地方,都设置提示输入的信息;凡是输出数据的地方,都设置输出说明信息。(2) 库函数fputs()──向指定文件输出一个字符串1)用法:int fputs(字符串,文件指针);其中“字符串”可以是一个字符串常量,或字符数组名,或字符指针变量名。2)功能:向指定文件输出一个字符串,同时将读写位置指针向前移动strlength(字符串长度)个字节。如果输出成功,则函数返回值为0;否则,为非0值。(3) 库函数fgets()──从文件中读一个字符串1)用法:char *fgets(指针,串长度+1,文件指针);2)功能:从指定文件中读入一个字符串,存入“字符数组/指针”中,并在尾端自动加一个结束标志'\0';同时,将读写位置指针向前移动strlength(字符串长度)个字节。如果在读入规定长度之前遇到文件尾EOF或换行符,读入即结束。[Return]13.3.3 读/写一个数据块──fread()和fwrite()实际应用中,常常要求1次读/写1个数据块。为此,ANSI C 标准设置了 fread( ) 和fwrite()函数。1.用法:int fread(void *buffer,int size,int count,FILE *fp);int fwrite(void *buffer,int size,int count,FILE *fp);2.功能:fread()──从fp所指向文件的当前位置开始,一次读入size个字节,重复count次,并将读入的数据存放到从buffer开始的内存中;同时,将读写位置指针向前移动size* count个字节。其中,buffer是存放读入数据的起始地址(即存放何处)。fwrite()──从buffer开始,一次输出size个字节,重复count次, 并将输出的数据存放到fp所指向的文件中;同时,将读写位置指针向前移动size* count个字节。其中,buffer是要输出数据在内存中的起始地址(即从何处开始输出)。如果调用fread()或fwrite()成功,则函数返回值等于count。fread()和fwrite()函数,一般用于二进制文件的处理。[Return]13.3.4 对文件进行格式化读/写──fscanf()和fprintf()函数与scanf()和printf()函数的功能相似,区别在于:fscanf()和fprintf()函数的操作对象是指定文件,而scanf()和printf()函数的操作对象是标准输入(stdin)输出(stdout)文件。int fscanf(文件指针,"格式符",输入变量首地址表);int fprintf(文件指针,"格式符",输出参量表);例如,......int i=3; float f=9.80;......fprintf(fp,"%2d,%6.2f", i, f);......fprintf()函数的作用是,将变量i按%2d格式、变量f按%6.2f格式, 以逗号作分隔符,输出到fp所指向的文件中:□3,□□9.80(□表示1个空格)。[Return]13.3.5 读/写函数的选用原则从功能角度来说,fread()和fwrite()函数可以完成文件的任何数据读/写操作。 但为方便起见,依下列原则选用:1.读/写1个字符(或字节)数据时:选用fgetc()和fputc()函数。2.读/写1个字符串时:选用fgets()和fputs()函数。3.读/写1个(或多个)不含格式的数据时:选用fread()和fwrite()函数。4.读/写1个(或多个)含格式的数据时:选用fscanf()和fprintf()函数。[Return]13.4 位置指针与文件定位文件中有一个读写位置指针,指向当前的读写位置。每次读写1个(或1组)数据后,系统自动将位置指针移动到下一个读写位置上。 如果想改变系统这种读写规律,可使用有关文件定位的函数。13.4.1 位置指针复位函数rewind()1.用法:int rewind(文件指针);2.功能:使文件的位置指针返回到文件头。13.4.2 随机读写与fseek()函数对于流式文件,既可以顺序读写,也可随机读写,关键在于控制文件的位置指针。 所谓顺序读写是指,读写完当前数据后,系统自动将文件的位置指针移动到下一个读写位置上。所谓随机读写是指,读写完当前数据后,可通过调用fseek()函数,将位置指针移动到文件中任何一个地方。1.用法:int fseek(文件指针,位移量,参照点);2.功能:将指定文件的位置指针,从参照点开始,移动指定的字节数。(1)参照点:用0(文件头)、1(当前位置)和2(文件尾)表示。在ANSI C标准中,还规定了下面的名字:SEEK_SET──文件头,SEEK_CUR──当前位置,SEEK_END──文件尾(2)位移量:以参照点为起点,向前(当位移量>0时)或后(当位移量<0时)移动的字节数。在ANSI C标准中,要求位移量为long int型数据。fseek()函数一般用于二进制文件。13.4.3 返回文件当前位置的函数ftell()由于文件的位置指针可以任意移动,也经常移动,往往容易迷失当前位置,ftell()就可以解决这个问题。1.用法:long ftell(文件指针);2.功能:返回文件位置指针的当前位置(用相对于文件头的位移量表示)。如果返回值为-1L,则表明调用出错。例如:offset=ftell(fp);if(offset= =-1L)printf(“ftell() error\n”);[Return]13.5 出错检测13.5.1 ferror()函数 在调用输入输出库函数时,如果出错,除了函数返回值有所反映外,也可利用ferror()函数来检测。 1.用法: int ferror(文件指针);2.功能:如果函数返回值为0,表示未出错;如果返回一个非0值,表示出错。 (1)对同一文件,每次调用输入输出函数均产生一个新的ferror()函数值。因此在调用了输入输出函数后,应立即检测,否则出错信息会丢失。 (2)在执行fopen()函数时,系统将ferror()的值自动置为0。13.5.2 clearerr()函数1.用法: void clearerr(文件指针);2.功能:将文件错误标志(即ferror()函数的值)和文件结束标志(即feof()函数的值)置为0。 对同一文件,只要出错就一直保留,直至遇到clearerr()函数或rewind()函数,或其它任何一个输入输出库函数。[Return](共16张PPT)第9章 编译预处理所谓编译预处理是指,在对源程序进行编译之前,先对源程序中的编译预处理命令进行处理;然后再将处理的结果,和源程序一起进行编译,以得到目标代码。9.1 宏定义与符号常量9.2 文件包含9.3 条件编译[Return]9.1 宏定义与符号常量在C语言中,“宏”分为无参数的宏(简称无参宏)和有参数的宏(简称有参宏)两种。9.1.1 无参宏定义9.1.2 符号常量9.1.3 有参宏定义[Return]9.1.1 无参宏定义1.无参宏定义的一般格式#define 标识符 语言符号字符串其中:“define”为宏定义命令;“标识符”为所定义的宏名,通常用大写字母表示,以便于与变量区别;“语言符号字符串”可以是常数、表达式、格式串等。2.使用宏定义的优点(1)可提高源程序的可维护性(2)可提高源程序的可移植性(3)减少源程序中重复书写字符串的工作量[案例8.1] 输入圆的半径,求圆的周长、面积和球的体积。要求使用无参宏定义圆周率。/*案例代码文件名:AL8_1.C*//*程序功能:输入圆的半径,求圆的周长、面积和球的体积。 */#define PI 3.1415926 /*PI是宏名,3.1415926用来替换宏名的常数*/main(){ float radius,length,area,volume;printf("Input a radius: ");scanf("%f",&radius);length=2*PI*radius; /*引用无参宏求周长*/area=PI*radius*radius; /*引用无参宏求面积*/volume=PI*radius*radius*radius*3/4; /*引用无参宏求体积*/printf("length=%.2f,area=%.2f,volume=%.2f\n", length, area, volume);}[程序演示]3.说明(1)宏名一般用大写字母表示,以示与变量区别。但这并非是规定。(2)宏定义不是C语句,所以不能在行尾加分号。否则,宏展开时,会将分号作为字符串的1个字符,用于替换宏名。(3)在宏展开时,预处理程序仅以按宏定义简单替换宏名,而不作任何检查。如果有错误,只能由编译程序在编译宏展开后的源程序时发现。(4)宏定义命令#define出现在函数的外部,宏名的有效范围是:从定义命令之后, 到本文件结束。通常,宏定义命令放在文件开头处。(5)在进行宏定义时,可以引用已定义的宏名 。(6)对双引号括起来的字符串内的字符,即使与宏名同名,也不进行宏展开。[Return]9.1.2 符号常量在定义无参宏时,如果“语言符号字符串”是一个常量,则相应的“宏名”就是一个符号常量。恰当命名的符号常量,除具有宏定义的上述优点外,还能表达出它所代表常量的实际含义,从而增强程序的可读性。#define EOF -1 /*文件尾*/#define NULL 0 /*空指针*/#define MIN 1 /*极小值*/#define MAX 31 /*极大值*/#define STEP 2 /*步长*/[Return]9.1.3 有参宏定义1.带参宏定义的一般格式#define 宏名(形参表) 语言符号字符串2.带参宏的调用和宏展开(1)调用格式:宏名(实参表)(2)宏展开:用宏调用提供的实参字符串,直接置换宏定义命令行中、相应形参字符串,非形参字符保持不变。3.说明(1)定义有参宏时,宏名与左圆括号之间不能留有空格。否则,C编译系统将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。(2)有参宏的展开,只是将实参作为字符串,简单地置换形参字符串,而不做任何语法检查。在定义有参宏时,在所有形参外和整个字符串外,均加一对圆括号。(3)虽然有参宏与有参函数确实有相似之处,但不同之处更多,主要有以下几个方面:1)调用有参函数时,是先求出实参的值,然后再复制一份给形参。而展开有参宏时,只是将实参简单地置换形参。2)在有参函数中,形参是有类型的,所以要求实参的类型与其一致;而在有参宏中,形参是没有类型信息的,因此用于置换的实参,什么类型都可以。有时,可利用有参宏的这一特性,实现通用函数功能。3)使用有参函数,无论调用多少次,都不会使目标程序变长,但每次调用都要占用系统时间进行调用现场保护和现场恢复;而使用有参宏,由于宏展开是在编译时进行的,所以不占运行时间,但是每引用1次,都会使目标程序增大1次。[Return]9.2 文件包含1.文件包含的概念文件包含是指,一个源文件可以将另一个源文件的全部内容包含进来。2.文件包含处理命令的格式#include “包含文件名” 或 #include <包含文件名>两种格式的区别仅在于:(1)使用双引号:系统首先到当前目录下查找被包含文件,如果没找到,再到系统指定的“包含文件目录”(由用户在配置环境时设置)去查找。(2)使用尖括号:直接到系统指定的“包含文件目录”去查找。一般地说,使用双引号比较保险。3.文件包含的优点一个大程序,通常分为多个模块,并由多个程序员分别编程。有了文件包含处理功能,就可以将多个模块共用的数据(如符号常量和数据结构)或函数,集中到一个单独的文件中。这样,凡是要使用其中数据或调用其中函数的程序员,只要使用文件包含处理功能,将所需文件包含进来即可,不必再重复定义它们,从而减少重复劳动。4.说明(1)编译预处理时,预处理程序将查找指定的被包含文件,并将其复制到#include命令出现的位置上。(2)常用在文件头部的被包含文件,称为“标题文件”或“头部文件”,常以“h”(head)作为后缀,简称头文件。在头文件中,除可包含宏定义外,还可包含外部变量定义、结构类型定义等。(3)一条包含命令,只能指定一个被包含文件。如果要包含n个文件,则要用n条包含命令。(4)文件包含可以嵌套,即被包含文件中又包含另一个文件。[Return]9.3 条件编译条件编译可有效地提高程序的可移植性,并广泛地应用在商业软件中,为一个程序提供各种不同的版本。9.3.1 #ifdef ~ #endif和#ifndef ~ #endif命令9.3.2 #if ~ #endif[Return]9.3.1 #ifdef ~ #endif和#ifndef ~ #endif命令1.一般格式#ifdef 标识符程序段1;[#else程序段2;]#endif2.功能:当“标识符”已经被#define命令定义过,则编译程序段1,否则编译程序段2。(1)在不同的系统中,一个int 型数据占用的内存字节数可能是不同的。(2)利用条件编译,还可使同一源程序即适合于调试(进行程序跟踪、打印较多的状态或错误信息),又适合高效执行要求。3.关于#ifndef ~ #endif命令格式与#ifdef ~ #endif命令一样,功能正好与之相反。[Return]9.3.2 #if ~ #endif1.一般格式#if 常量表达式程序段1;[#else程序段2;]#endif2.功能:当表达式为非0(“逻辑真”)时,编译程序段1,否则编译程序段2。[案例8.2] 输入一个口令,根据需要设置条件编译,使之能将口令原码输出,或仅输出若干星号“*”。/*案例代码文件名:AL8_2.C*/#define PASSWORD 0 /*预置为输出星号*/main(){ ……/*条件编译*/#if PASSWORD /*源码输出*/……#else /*输出星号*/……#endif……}[Return](共18张PPT)第6章 循环结构程序设计6.1 循环语句概述 6.2 for语句和while语句 6.3 直到型循环do-while语句 6.4 break语句与continue语句6.5 应用举例良好的源程序书写习惯──注释(续)[Return]6.1 循环语句概述求1~100的累计和。根据已有的知识,可以用“1+2+……+100”来求解,但显然很繁琐。现在换个思路来考虑:首先设置一个累计器sum,其初值为0,利用sum += n来计算(n依次取1、2、……、100),只要解决以下3个问题即可: (1)将n的初值置为1; (2)每执行1次“sum += n”后,n增1; (3)当n增到101时,停止计算。此时,sum的值就是1~100的累计和。 根据已有的知识,单独实现每一步都不难。但是,由于需要经常使用这种重复计算结构(称为循环结构),C语言提供了3条循环语句来实现,以简化、并规范循环结构程序设计。在C语言中,可用以下语句实现循环:(1)用for语句。(2)用do-while语句。(3)用while语句。(4)用goto语句和if语句构成循环。使用goto语句实现求解1~100累计和的程序可以如下:main(){ int n=1, sum=0;loop: sum += n; n++;if (n<=100) goto loop;printf(“sum=%d\n”, sum);}其中“loop:”为语句标号(格式:标号: 语句行),其命名遵循标识符命名规则。goto语句格式:goto 标号,功能为:使系统转向标号所在的语句行执行。注意:结构化程序设计方法,主张限制使用goto语句。因为滥用goto语句,将会导致程序结构无规律、可读性差。 另外,从功能上说,for语句可完全代替当型循环语句while,所以该语句也不是必需的。[Return]6.2 for语句和while语句在3条循环语句中,for语句最为灵活,不仅可用于循环次数已经确定的情况,也可用于循环次数虽不确定、但给出了循环继续条件的情况。 [案例5.1] 求1~100的累计和。 /*案例代码文件名:AL5_1.C*/ /*程序功能:求1~100的累计和*/ main() { int i,sum=0; /*将累加器sum初始化为0*/ for(i=1; i<=100; i++) sum += i; /*实现累加*/ printf("sum=%d\n",sum); }程序运行情况如下:sum=5050[案例5.2] 求n的阶乘n!(n!=1*2*……*n)。 /*案例代码文件名:AL5_2.C*/ /*程序功能:求n!*/ main() { int i, n; long fact=1; /*将累乘器fact初始化为1*/ printf(“Input n: ”); scanf(“%d”, &n);for(i=1; i<=n; i++) fact *= i; /*实现累乘*/printf("%d ! = %ld\n", n, fact); }程序运行情况如下: Input n: 5↙ 5 ! = 1201.for语句的一般格式 for([变量赋初值];[循环继续条件];[循环变量增值]) { 循环体语句组;}2.for语句的执行过程执行过程如图5-1所示。(1)求解“变量赋初值”表达式。(2)求解“循环继续条件”表达式。如果其值非0,执行(3);否则,转至(4)。(3)执行循环体语句组,并求解“循环变量增值”表达式,然后转向(2)。(4)执行for语句的下一条语句。3.说明(1)“变量赋初值”、“循环继续条件”和“循环变量增值”部分均可缺省,甚至全部缺省,但其间的分号不能省略。(2)当循环体语句组仅由一条语句构成时,可以不使用复合语句形式,如上例所示。(3)“循环变量赋初值”表达式,既可以是给循环变量赋初值的赋值表达式,也可以是与此无关的其它表达式(如逗号表达式)。例如,for(sum=0;i<=100;i++) sum += i;for(sum=0,i=1;i<=100;i++) sum += i;(4)“循环继续条件”部分是一个逻辑量,除一般的关系(或逻辑)表达式外,也允许是数值(或字符)表达式。4.while语句(1)一般格式 while(循环继续条件) { 循环体语句组;}(2)执行过程执行过程如图5-2所示。1)求解“循环继续条件”表达式。如果其值为非0,转2);否则转3)。2)执行循环体语句组,然后转1)。3)执行while语句的下一条。显然,while循环是for循环的一种简化形式(缺省“变量赋初值”和“循环变量增值”表达式)。[案例5.3] 用while语句求1~100的累计和。 /*案例代码文件名:AL5_3.C*/ /*程序功能:求1~100的累计和*/ main() { int i=1,sum=0; /*初始化循环控制变量i和累计器sum*/ while( i<=100 ) { sum += i; /*实现累加*/ i++; /*循环控制变量i增1*/ } printf(“sum=%d\n”,sum); }程序运行情况如下: sum=50505.循环嵌套(1)循环语句的循环体内,又包含另一个完整的循环结构,称为循环的嵌套。循环嵌套的概念,对所有高级语言都是一样的。(2)for语句和while语句允许嵌套,do-while语句也不例外。[Return]6.3 直到型循环do-while语句1.一般格式do{ 循环体语句组; }while(循环继续条件); /*本行的分号不能缺省*/当循环体语句组仅由一条语句构成时,可以不使用复合语句形式。2.执行过程执行过程如图5-3所示。(1)执行循环体语句组。(2)计算“循环继续条件”表达式。如果“循环继续条件”表达式的值为非 0(真),则转向(1)继续执行;否则,转向(3)。(3)执行do-while的下一条语句。do-while循环语句的特点是:先执行循环体语句组,然后再判断循环条件。[案例5.4] 用do-while语句求解1~100的累计和。 /*案例代码文件名:AL5_4.C*/ /*程序功能:求1~100的累计和*/ main() { int i=1, sum=0; /*定义并初始化循环控制变量,以及累计器*/ do { sum += i; /*累加*/ i++; } while(i<=100); /*循环继续条件:i<=100*/ printf(“sum=%d\n”,sum); }do-while语句比较适用于处理:不论条件是否成立,先执行1次循环体语句组的情况。除此之外,do-while语句能实现的,for语句也能实现,而且更简洁。[Return]6.4 break语句与continue语句为了使循环控制更加灵活,C语言提供了break语句和continue语句。1.一般格式: break;continue;2.功能(1)break:强行结束循环,转向执行循环语句的下一条语句。(2)continue:对于for循环,跳过循环体其余语句,转向循环变量增量表达式的计算;对于while和do-while循环,跳过循环体其余语句,但转向循环继续条件的判定。3.break和continue语句对循环控制的影响如图5-4所示。4.说明(1)break能用于循环语句和switch语句中,continue只能用于循环语句中。(2)循环嵌套时,break和continue只影响包含它们的最内层循环,与外层循环无关。[Return]6.5 应用举例[例5.5] 求Fibonacci数列的前40个数。该数列的生成方法为:F1=1,F2=1,Fn=Fn-1+Fn-2(n>=3),即从第3个数开始,每个数等于前2个数之和。算法设计,请参见第2章第1节(2.1)。参考源程序如下: /*案例代码文件名:AL5_5.C*/ main() { long int f1=1,f2=1; /*定义并初始化数列的头2个数*/ int i=1; /*定义并初始化循环控制变量i*/for( ; i<=20; i++ ) /*1组2个,20组40个数*/ { printf(“%15ld%15ld”, f1, f2); /*输出当前的2个数*/ if(i%2==0) printf(“\n”); /*输出2次(4个数),换行*/ f1 += f2; f2 += f1; /*计算下2个数*/ } }[例5.6] 输出10~100之间的全部素数。所谓素数n是指,除1和n之外,不能被2~(n-1)之间的任何整数整除。算法设计要点:(1)显然,只要设计出判断某数n是否是素数的算法,外面再套一个for循环即可。(2)判断某数n是否是素数的算法:根据素数的定义,用2~(n-1)之间的每一个数去整除n,如果都不能被整除,则表示该数是一个素数。判断一个数是否能被另一个数整除,可通过判断它们整除的余数是否为0来实现。参考源程序如下:main() { int i=11, j, counter=0; for( ; i<=100; i+=2) /*外循环:为内循环提供一个整数i*/{ for(j=2; j<=i-1; j++) /*内循环:判断整数i是否是素数*/ if(i%j= =0) /*i不是素数*/ break; /*强行结束内循环,执行下面的if语句*/ if(counter%10= =0) /*每输出10个数换一行*/ printf(“\n”); if( j >= i ) /*整数i是素数:输出,计数器加1*/ { printf(“%6d”,i); counter++; } } }思考题:外循环控制变量i的初值从11开始、增量为2的作法有什么好处?为提高运行速度,如何优化内循环?(提示:从减少计算次数角度来考虑)[Return]良好的源程序书写习惯──注释(续)(3)循环结构在C语言中,循环结构由循环语句for、while和do...while来实现。作为注释,应在它们的前面说明其功能,在循环条件判断语句行的后面,说明循环继续条件的含义,如下所示。 1)for语句/*功能*/for(变量初始化;循环条件;变量增值) /*循环继续条件的含义*/{ …… }2)while语句/*功能说明*/while(循环条件) /*循环继续条件的含义*/{ …… }3)do...while语句/*功能说明*/do { …… }while(循环条件); /*循环继续条件的含义*/如果循环嵌套,还应说明每层循环各控制什么。[Return](共83张PPT)第10章 指针C程序设计中使用指针可以:使程序简洁、紧凑、高效有效地表示复杂的数据结构动态分配内存得到多于一个的函数返回值10.1 指针的概念变量与地址程序中: int i;float k;内存中每个字节有一个编号-----地址…...…...2000200120022005内存02003ik编译或函数调用时为其分配内存单元变量是对程序中数据存储空间的抽象…...…...2000200420062005整型变量i10变量i_pointer200120022003指针与指针变量指针:一个变量的地址指针变量:专门存放变量地址的变量叫指针变量2000指针指针变量变量的内容变量的地址指针变量变量变量地址(指针)变量值指向地址存入指针变量&与*运算符含义含义: 取变量的地址单目运算符优先级: 2结合性:自右向左含义: 取指针所指向变量的内容单目运算符优先级: 2结合性:自右向左两者关系:互为逆运算理解…...…...2000200420062005整型变量i10变量i_pointer2001200220032000指针变量i_pointer-----指针变量,它的内容是地址量*i_pointer----指针的目标变量,它的内容是数据&i_pointer---指针变量占用内存的地址200010i_pointer*i_pointer&i_pointerii_pointer &i &(*i_pointer)i *i_pointer *(&i)i_pointer = &i = &(*i_pointer)i = *i_pointer = *(&i)直接访问与间接访问直接访问:按变量地址存取变量值间接访问:通过存放变量地址的变量去访问变量例 i=3; -----直接访问指针变量…...…...2000200420062005整型变量i10变量i_pointer20012002200320003例 *i_pointer=20; -----间接访问20指针变量…...…...2000200420062005整型变量i10变量i_pointer2001200220032000整型变量k例 k=i; --直接访问k=*i_pointer; --间接访问10例 k=i;k=*i_pointer;10.2 指针变量指针变量与其所指向的变量之间的关系指针变量的定义一般形式: [存储类型] 数据类型 *指针名;3变量i2000i_pointer*i_pointeri*i_pointer&ii_pointeri=3;*i_pointer=33变量i2000i_pointer*i_pointeri*i_pointer&ii_pointeri=3;*i_pointer=3合法标识符指针变量本身的存储类型指针的目标变量的数据类型表示定义指针变量不是‘*’运算符例 int *p1,*p2;float *q ;static char *name;注意:1、int *p1, *p2; 与 int *p1, p2;2、指针变量名是p1,p2 ,不是*p1,*p23、指针变量只能指向定义时所规定类型的变量4、指针变量定义后,变量值不确定,应用前必须先赋值指针变量的初始化一般形式:[存储类型] 数据类型 *指针名=初始地址值;赋给指针变量,不是赋给目标变量例 int i;int *p=&i;变量必须已说明过类型应一致例 int *p=&i;int i;例 int i;int *p=&i;int *q=p;用已初始化指针变量作初值例 main( ){ int i;static int *p=&i;..............} ( )不能用auto变量的地址去初始化static型指针例 main( ){ int i=10;int *p;*p=i;printf(“%d”,*p);}危险!例 main( ){ int i=10,k;int *p;p=&k;*p=i;printf(“%d”,*p);}指针变量必须先赋值,再使用…...…...2000200420062005整型变量i10指针变量p200120022003随机零指针与空类型指针零指针:(空指针)定义:指针变量值为零表示: int * p=0;p指向地址为0的单元,系统保证该单元不作它用表示指针变量值没有意义#define NULL 0int *p=NULL:p=NULL与未对p赋值不同用途:避免指针变量的非法引用在程序中常作为状态比较例 int *p;......while(p!=NULL){ ...…}void *类型指针表示: void *p;使用时要进行强制类型转换例 char *p1;void *p2;p1=(char *)p2;p2=(void *)p1;表示不指定p是指向哪一种类型数据的指针变量例 指针的概念main(){ int a;int *pa=&a;a=10;printf("a:%d\n",a);printf("*pa:%d\n",*pa);printf("&a:%x(hex)\n",&a);printf("pa:%x(hex)\n",pa);printf("&pa:%x(hex)\n",&pa);}运行结果:a:10*pa:10&a:f86(hex)pa:f86(hex)&pa:f88(hex)…...…...f86f8af8cf8b整型变量a10指针变量paf87f88f89f86例 输入两个数,并使其从大到小输出main(){ int *p1,*p2,*p,a,b;scanf("%d,%d",&a,&b);p1=&a; p2=&b;if(a{ p=p1; p1=p2; p2=p;}printf("a=%d,b=%d\n",a,b);printf("max=%d,min=%d\n",*p1,*p2);}运行结果:a=5,b=9max=9,min=5…...…...指针变量p1指针变量p20002008200220042006指针变量p2整型变量b整型变量a5200692008200620082006指针变量作为函数参数——地址传递特点:共享内存,“双向”传递swap(int x,int y){ int temp;temp=x;x=y;y=temp;}main(){ int a,b;scanf("%d,%d",&a,&b);if(aprintf("\n%d,%d\n",a,b);}例 将数从大到小输出…...…...20002008200A2002200420065变量a变量b(main)9变量temp变量y变量x(swap)55959COPY指针变量作为函数参数——地址传递特点:共享内存,“双向”传递swap(int x,int y){ int temp;temp=x;x=y;y=temp;}main(){ int a,b;scanf("%d,%d",&a,&b);if(aprintf("\n%d,%d\n",a,b);}例 将数从大到小输出值传递…...…...20002008200A2002200420065变量a变量b(main)9运行结果:5, 9swap(int *p1, int *p2){ int p;p=*p1;*p1=*p2;*p2=p;}main(){ int a,b;int *pointer_1,*pointer_2;scanf("%d,%d",&a,&b);pointer_1=&a; pointer_2=&b;if(aprintf("\n%d,%d\n",a,b);}…...20002008200A200220042006200C200E2010...59整型变量a整型变量b(main)指针pointer_1指针pointer_220002002(swap)指针p1指针p2整型p5920002002COPY5例 将数从大到小输出swap(int *p1, int *p2){ int p;p=*p1;*p1=*p2;*p2=p;}main(){ int a,b;int *pointer_1,*pointer_2;scanf("%d,%d",&a,&b);pointer_1=&a; pointer_2=&b;if(aprintf("\n%d,%d\n",a,b);}…...20002008200A200220042006200C200E2010...59整型变量a整型变量b(main)指针pointer_1指针pointer_22000200259例 将数从大到小输出运行结果:9,5地址传递swap(int *p1, int *p2){ int *p;*p=*p1;*p1=*p2;*p2=*p;}main(){ int a,b;int *pointer_1,*pointer_2;scanf("%d,%d",&a,&b);pointer_1=&a; pointer_2=&b;if(aprintf("\n%d,%d\n",a,b);}运行结果:9,9编译警告!结果不对!int x;int *p=&x;x;例 将数从大到小输出…...20002008200A200220042006200C200E2010...59整型变量a整型变量b(main)指针pointer_1指针pointer_2200020029920002002COPY(swap)指针p1指针p2指针p****假设2000指针变量在使用前必须赋值!/*ch9_32.c*/swap(int x,int y){ int t;t=x; x=y; y=t;}main(){ int a,b;int *pointer_1,*pointer_2;scanf("%d,%d",&a,&b);pointer_1=&a; pointer_2=&b;if(aprintf("\n%d,%d\n",a,b);}运行结果:5,9例 将数从大到小输出值传递…...20002008200A200220042006200C200E2010...59整型a整型b(main)pointer_1pointer_2200020029COPY(swap)整型x整型y整型t5559运行结果:5,9例 将数从大到小输出swap(int *p1, int *p2){ int *p;p=p1;p1=p2;p2=p;}main(){ int a,b;int *pointer_1,*pointer_2;scanf("%d,%d",&a,&b);pointer_1=&a; pointer_2=&b;if(aprintf("%d,%d",*pointer_1,*pointer_2);}…...20002008200A200220042006200C200E2010...59整型a整型b(main)pointer_1pointer_22000200220002002COPY(swap)指针p1指针p2指针p****2000地址传递2000200210.3 指针与数组指向数组元素的指针变量例 int array[10];int *p;p=&array[0]; // p=array;或 int *p=&array[0];或 int *p=array;array[0]array[1]array[2]array[3]array[9]...整型指针p&array[0]p数组名是表示数组首地址的地址常量指针的运算指针变量的赋值运算p=&a; (将变量a地址 p)p=array; (将数组array首地址 p)p=&array[i]; (将数组元素地址 p)p1=p2; (指针变量p2值 p1)不能把一个整数 p,也不能把p的值 整型变量如 int i, *p;p=1000; ( )i=p; ( )指针变量与其指向的变量具有相同数据类型指针的算术运算:p i p i d (i为整型数,d为p指向的变量所占字节数)p++, p--, p+i, p-i, p+=i, p-=i等若p1与p2指向同一数组,p1-p2=两指针间元素个数 (p1-p2)/dp1+p2 无意义例 p指向float数,则 p+1 p+1 4例 p指向int型数组,且p=&a[0];则p+1 指向a[1]例 int a[10];int *p=&a[2];p++;*p=1;例 int a[10];int *p1=&a[2];int *p2=&a[5];则:p2-p1=3;a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]a数组pp+1,a+1p+i,a+ip+9,a+91指针变量的关系运算若p1和p2指向同一数组,则p1p1>p2 表示p1指的元素在后p1==p2 表示p1与p2指向同一元素若p1与p2不指向同一数组,比较无意义p==NULL或p!=NULL数组元素表示方法a[0]a[1]a[2]a[3]a[9]...aa+9a+1a+2地址元素下标法a[0]a[1]a[2]a[9]a[0]a[1]a[2]a[3]a[9]...pp+9p+1p+2地址元素指针法*p*(p+1)*(p+2)*(p+9)[] 变址运算符a[i] *(a+i)a[i] p[i] *(p+i) *(a+i)*a*(a+1)*(a+2)*(a+9)p[0]p[1]p[2]p[9]a[0]a[1]a[2]a[3]a[4]例 数组元素的引用方法main(){ int a[5],*pa,i;for(i=0;i<5;i++)a[i]=i+1;pa=a;for(i=0;i<5;i++)printf("*(pa+%d):%d\n",i,*(pa+i));for(i=0;i<5;i++)printf("*(a+%d):%d\n",i,*(a+i));for(i=0;i<5;i++)printf("pa[%d]:%d\n",i,pa[i]);for(i=0;i<5;i++)printf("a[%d]:%d\n",i,a[i]);}12345pa例 int a[]={1,2,3,4,5,6,7,8,9,10},*p=a,i;数组元素地址的正确表示: (A)&(a+1) (B)a++ (C)&p (D)&p[i] 数组名是地址常量p++,p-- ( )a++,a-- ( )a+1, *(a+2) ( )例 void main(){ int a []={5,8,7,6,2,7,3};int y,*p=&a[1];y=(*--p)++;printf(“%d ”,y);printf(“%d”,a[0]);}输出:5 6pp58762730123456a例 注意指针变量的运算6main(){ int i,*p,a[7];p=a;for(i=0;i<7;i++)scanf("%d",p++);printf("\n");for(i=0;i<7;i++,p++)printf("%d",*p);}例 注意指针的当前值p=a;pp58762730123456apppppp指针变量可以指到数组后的内存单元数组名作函数参数数组名作函数参数,是地址传递数组名作函数参数,实参与形参的对应关系实参形参数组名指针变量数组名指针变量数组名数组名指针变量指针变量例 将数组a中的n个整数按相反顺序存放ij3 7 9 11 0 6 7 5 4 20 1 2 3 4 5 6 7 8 9ijijijji11760594723实参与形参均用数组void inv(int x[], int n){ int t,i,j,m=(n-1)/2;for(i=0;i<=m;i++){ j=n-1-i;t=x[i]; x[i]=x[j]; x[j]=t;}}main(){ int i,a[10]={3,7,9,11,0,6,7,5,4,2};inv(a,10);printf("The array has been reverted:\n");for(i=0;i<10;i++)printf("%d,",a[i]);printf("\n");}m=4例 将数组a中的n个整数按相反顺序存放void inv(int *x, int n){ int t,*p,*i,*j,m=(n-1)/2;i=x; j=x+n-1; p=x+m;for(;i<=p;i++,j--){ t=*i; *i=*j; *j=t; }}main(){ int i,a[10]={3,7,9,11,0,6,7,5,4,2};inv(a,10);printf("The array has been reverted:\n");for(i=0;i<10;i++)printf("%d,",a[i]);printf("\n");}实参用数组,形参用指针变量37911067542a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]xp=x+ma数组60711594723ijijijjiji例 将数组a中的n个整数按相反顺序存放void inv(int *x, int n){ int t,*i,*j,*p,m=(n-1)/2;i=x; j=x+n-1; p=x+m;for(;i<=p;i++,j--){ t=*i; *i=*j; *j=t; }}main(){ int i,a[10],*p=a;for(i=0;i<10;i++,p++)scanf("%d",p);p=a; inv(p,10);printf("The array has been reverted:\n");for(p=a;pprintf("%d",*p);}实参与形参均用指针变量例 将数组a中的n个整数按相反顺序存放void inv(int x[], int n){ int t,i,j,m=(n-1)/2;for(i=0;i<=m;i++){ j=n-1-i;t=x[i]; x[i]=x[j]; x[j]=t;}}main(){ int i,a[10],*p=a;for(i=0;i<10;i++,p++)scanf("%d",p);p=a; inv(p,10);printf("The array has been reverted:\n");for(p=arr;pprintf("%d ",*p);}实参用指针变量,形参用数组一级指针变量与一维数组的关系int *p 与 int q[10]数组名是指针(地址)常量p=q; p+i 是q[i]的地址数组元素的表示方法:下标法和指针法, 即若p=q, 则 p[i] q[i] *(p+i) *(q+i)形参数组实质上是指针变量,即int q[ ] int *q在定义指针变量(不是形参)时,不能把int *p 写成int p[];系统只给p分配能保存一个指针值的内存区(一般2字节);而给q分配2*10字节的内存区指针与二维数组二维数组的地址对于一维数组:(1)数组名array表示数组的首地址,即array[0]的地址;(2)数组名array是地址常量(3)array+i是元素array[i]的地址(4)array[i] *(array+i)arrayint array[10];对于二维数组:(1)a是数组名,包含三个元素a[0],a[1],a[2](2)每个元素a[i]又是一个一维数组,包含4个元素aa+1a+2*(*(a+0)+1)*(a[0]+1)int a[3][4];a[0]a[1]a[2]200020082016200020022008201020162018a[0][0]a[0][1]a[1][0]a[1][1]a[2][0]a[2][1]a[0][2]a[0][3]a[1][2]a[1][3]a[2][2]a[2][3]基类型行指针与列指针a[0]+1a[1]+1a[2]+1*(a+0)+1*(a+1)+1*(a+2)+1对二维数组 int a[3][4],有a-----二维数组的首地址,即第0行的首地址a+i-----第i行的首地址a[i] *(a+i)------第i行第0列的元素地址a[i]+j *(a+i)+j -----第i行第j列的元素地址*(a[i]+j) *(*(a+i)+j) a[i][j]a+i=&a[i]=a[i]=*(a+i) =&a[i][0], 值相等,含义不同a+i &a[i],表示第i行首地址,指向行a[i] *(a+i) &a[i][0],表示第i行第0列元素地址,指向列int a[3][4];a[0]a[1]a[2]200020082016200020022008201020162018a[0][0]a[0][1]a[1][0]a[1][1]a[2][0]a[2][1]a[0][2]a[0][3]a[1][2]a[1][3]a[2][2]a[2][3]aa+1a+2int a[3][4];a[0][0]a[0][1]a[1][0]a[1][1]a[2][0]a[2][1]a[0][2]a[0][3]a[1][2]a[1][3]a[2][2]a[2][3]二维数组元素表示形式:(1)a[1][2](2)*(a[1]+2)(3)*(*(a+1)+2)(4)*(&a[0][0]+1*4+2)地址表示:(1) a+1(2) &a[1][0](3) a[1](4) *(a+1)(5)(int *) (a+1)行指针列指针地址表示:(1) &a[1][2](2) a[1]+2(3) *(a+1)+2(4)&a[0][0]+1*4+2表示形式含义地址a二维数组名,数组首地址a[0],*(a+0),*a第0行第0列元素地址a+1第1行首地址a[1],*(a+1)第1行第0列元素地址a[1]+2,*(a+1)+2,&a[1][2]第1行第2列元素地址*(a[1]+2),*(*(a+1)+2),a[1][2]第1行第2列元素值2000200020082008201213二维数组的指针变量指向二维数组元素的指针变量例 指向二维数组元素的指针变量main(){ static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};int *p;for(p=a[0];p{ if((p-a[0])%4==0) printf("\n");printf("%4d ",*p);}}p=*a;p=&a[0][0];p=*(a+0);p=a;p=*a;p=&a[0][0];p=(int *)a;p=a;int a[3][4];a[0][0]a[0][1]a[1][0]a[1][1]a[2][0]a[2][1]a[0][2]a[0][3]a[1][2]a[1][3]a[2][2]a[2][3]p指向一维数组的指针变量定义形式: 数据类型 (*指针名)[一维数组维数];例 int (*p)[4];( )不能少int (*p)[4]与int *p[4]不同p的值是一维数组的首地址,p是行指针可让p指向二维数组某一行如 int a[3][4], (*p)[4]=a;int a[3][4];a[0][0]a[0][1]a[1][0]a[1][1]a[2][0]a[2][1]a[0][2]a[0][3]a[1][2]a[1][3]a[2][2]a[2][3]aa+1a+2pp+1p+2p[0]+1或 *p+1p[1]+2或 *(p+1)+2*(*p+1)或 (*p)[1]*(*(p+1)+2)一维数组指针变量维数和二维数组列数必须相同例 一维数组指针变量举例main(){ static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};int i,j,(*p)[4];for(p=a,i=0;i<3;i++,p++)for(j=0;j<4;j++)printf("%d ",*(*p+j));printf("\n");}p=a[0];p=*a;p=&a[0][0];p=&a[0];p=a[0];p=*a;p=&a[0][0];p=&a[0];int a[3][4];a[0][0]a[0][1]a[1][0]a[1][1]a[2][0]a[2][1]a[0][2]a[0][3]a[1][2]a[1][3]a[2][2]a[2][3]ppp p[0][j]例 二维数组与指针运算main(){ int a[3][4]={{1,2,3,4},{3,4,5,6},{5,6,7,8}};int i;int (*p)[4]=a,*q=a[0];for(i=0;i<3;i++){ if(i==0) (*p)[i+i/2]=*q+1;else p++,++q;}for(i=0;i<3;i++)printf("%d,",a[i][i]);printf("%d,%d\n",*((int *)p),*q);}运行结果:2,4,7,5,3123434565678pq2pqpq二维数组的指针作函数参数用指向变量的指针变量用指向一维数组的指针变量用二维数组名实参形参数组名int x[][4]指针变量int (*q)[4]数组名int x[][4]指针变量int (*q)[4]数组名a数组名a指针变量p1指针变量p1若int a[3][4]; int (*p1)[4]=a; int *p2=a[0];指针变量p2指针变量int *q例 3个学生各学4门课,计算总平均分,并输出第n个学生成绩main(){ void average(float *p,int n);void search(float (*p)[4],int n);float score[3][4]={{65,67,79,60},{80,87,90,81},{90,99,100,98}};average(*score,12);search(score,2);}void average(float *p,int n){ float *p_end, sum=0,aver;p_end=p+n-1;for(;p<=p_end;p++)sum=sum+(*p);aver=sum/n;printf("average=%5.2f\n",aver);}void search(float (*p)[4], int n){ int i;printf(" No.%d :\n",n);for(i=0;i<4;i++)printf("%5.2f ",*(*(p+n)+i));}列指针行指针函数说明float p[][4]6552796080879081909910098pp p[n][i]例 3个学生各学4门课,计算总平均分,并查找一门以上课 不及格学生, 输出其各门课成绩void search(float (*p)[4], int n){ int i,j,flag;for(j=0;j{ flag=0;for(i=0;i<4;i++)if(*(*(p+j)+i)<60) flag=1;if(flag==1){ printf("No.%d is fail,his scores are:\n",j+1);for(i=0;i<4;i++)printf("%5.1f ",*(*(p+j)+i));printf("\n");}}}main(){ void search(float (*p)[4], int n);float score[3][4]={{...},{...},{...}};search(score,3);}6552796080879081909910098p p[j][i]二维数组与一维数组指针变量的关系如 int a[5][10] 与 int (*p)[10];二维数组名是一个指向有10个元素的一维数组的指针常量p=a+i 使 p指向二维数组的第i行*(*(p+i)+j) a[i][j]二维数组形参实际上是一维数组指针变量, 即 int x[ ][10] int (*x)[10]变量定义(不是形参)时两者不等价系统只给p分配能保存一个指针值的内存区(一般2字节);而给a分配2*5*10字节的内存区10.4 指针与字符串字符串表示形式用字符数组实现例 main( ){ char string[]=“I love China!”;printf(“%s\n”,string);printf(“%s\n”,string+7);}IloveChistring[0]string[1]string[2]string[3]string[4]string[5]string[6]string[7]string[8]string[9]stringstring[10]string[11]string[12]string[13]n!a\0用字符指针实现例 main( ){ char *string=“I love China!”;printf(“%s\n”,string);string+=7;while(*string){ putchar(string[0]);string++;}}IloveChistringn!a\0字符指针初始化:把字符串首地址赋给string char *string;string=“I love China!”;string*string!=0字符串指针作函数参数例 用函数调用实现字符串复制(1)用字符数组作参数(2)用字符指针变量作参数aIamateaceh\0r.fromabyuarasutndetoboet.\0Iaaeceh\0r.t.\0mtavoid copy_string(char from[],char to[]){ int i=0;while(from[i]!='\0'){ to[i]=from[i];i++;}to[i]='\0';}main(){ char a[]="I am a teacher.";char b[]="You are a student.";printf("string_a=%s\n string_b=%s\n",a,b);copy_string(a,b);printf("\nstring_a=%s\nstring_b=%s\n",a,b);}void copy_string(char *from,char *to){ for(;*from!='\0';from++,to++)*to=*from;*to='\0';}main(){ char *a="I am a teacher.";char *b="You are a student.";printf("string_a=%s\nstring_b=%s\n",a,b);copy_string(a,b);printf("\nstring_a=%s\nstring_b=%s\n",a,b);}字符指针变量与字符数组char *cp; 与 char str[20];str由若干元素组成,每个元素放一个字符;而cp中存放字符串首地址char str[20]; str=“I love China!”; ( )char *cp; cp=“I love China!”; ( )str是地址常量;cp是地址变量cp接受键入字符串时,必须先开辟存储空间例 char str[10];scanf(“%s”,str); ( )而 char *cp;scanf(“%s”, cp); ( )改为: char *cp,str[10];cp=str;scanf(“%s”,cp); ( )字符串与数组关系字符串用一维字符数组存放字符数组具有一维数组的所有特点数组名是指向数组首地址的地址常量数组元素的引用方法可用指针法和下标法数组名作函数参数是地址传递等区别存储格式:字符串结束标志赋值方式与初始化输入输出方式:%s %cchar str[]={“Hello!”}; ( )char str[]=“Hello!”; ( )char str[]={‘H’,‘e’,‘l’,‘l’,‘o’,‘!’}; ( )char *cp=“Hello”; ( )int a[]={1,2,3,4,5}; ( )int *p={1,2,3,4,5}; ( )char str[10],*cp;int a[10],*p;str=“Hello”; ( )cp=“Hello!”; ( )a={1,2,3,4,5}; ( )p={1,2,3,4,5}; ( )scanf(“%s”,str);printf(“%s”,str);gets(str);puts(str);10.5 指针与函数函数指针:函数在编译时被分配的入口地址,用函数名表示max…...指令1指令2函数指针变量赋值:如p=max;函数返回值的数据类型专门存放函数入口地址可指向返回值类型相同的不同函数指向函数的指针变量定义形式: 数据类型 (*指针变量名)();如 int (*p)();函数指针变量指向的函数必须有函数说明函数调用形式: c=max(a,b); c=(*p)(a,b); c=p (a,b);对函数指针变量p n, p++, p--无意义( )不能省int (*p)() 与 int *p()不同例 用函数指针变量调用函数,比较两个数大小main(){ int max(int ,int);int a,b,c;scanf("%d,%d",&a,&b);c=max(a,b);printf("a=%d,b=%d,max=%d\n",a,b,c);}int max(int x,int y){ int z;if(x>y) z=x;else z=y;return(z);}main(){ int max(int ,int), (*p)();int a,b,c;p=max;scanf("%d,%d",&a,&b);c=(*p)(a,b);printf("a=%d,b=%d,max=%d\n",a,b,c);}int max(int x,int y){ int z;if(x>y) z=x;else z=y;return(z);}用函数指针变量作函数参数例 用函数指针变量作参数,求最大值、最小值和两数之和void main(){ int a,b,max(int,int),min(int,int),add(int,int);void process(int,int,int (*fun)());scanf("%d,%d",&a,&b);process(a,b,max);process(a,b,min);process(a,b,add);}void process(int x,int y,int (*fun)()){ int result;result=(*fun)(x,y);printf("%d\n",result);}max(int x,int y){ printf(“max=”);return(x>y x:y);}min(int x,int y){ printf(“min=”);return(x}add(int x,int y){ printf(“sum=”);return(x+y);}10.6 返回指针值的函数函数定义形式:类型标识符 *函数名(参数表);例 int *f(int x, int y)例 指针函数实现:有若干学生成绩,要求输入学生序号后,能输出其全部成绩main(){ float score[][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}};float *search(float (*pointer)[4],int n), *p;int i,m;printf("Enter the number of student:");scanf("%d",&m);printf("The scores of No.%d are:\n",m);p=search(score,m);for(i=0;i<4;i++)printf("%5.2f\t",*(p+i));}float *search(float (*pointer)[4], int n){ float *pt;pt=*(pointer+n);return(pt);}pointerpointer+1347890665689678860708090score数组pppp例 写一个函数,求两个int型变量中居于较大值的变量的地址int *f1(int *x,int *y){if(*x>*y)return x;elsereturn y;}main(){ int a=2,b=3;int *p;p=f1(&a, &b);printf("%d\n",*p);}…...20002008200A20022004200623指针变量y指针变量x(f1)20022000COPY变量a变量b(main)指针变量p**例 写一个函数,求两个int型变量中居于较大值的变量的地址…...…...20002008200A2002200420062变量a变量b(main)3指针变量p**2002int *f3(int *x,int *y){if(*x>*y)return x;elsereturn y;}main(){ int a=2,b=3;int *p;p=f1(&a,&b);printf("%d\n",*p);}例 写一个函数,求两个int型变量中居于较大值的变量的地址int *f3(int x,int y){if(x>y)return &x;elsereturn &y;}main(){ int a=2,b=3;int *p;p=f3(a, b);printf("%d\n",*p);}…...20002008200A20022004200623变量y变量x(f3)32COPY变量a变量b(main)指针变量p**例 写一个函数,求两个int型变量中居于较大值的变量的地址不能返回形参或局部变量的地址作函数返回值…...…...20002008200A2002200420062变量a变量b(main)3指针变量p**200Aint *f3(int x,int y){if(x>y)return &x;elsereturn &y;}main(){ int a=2,b=3;int *p;p=f3(a,b);printf("%d\n",*p);}10.7 指针数组和多级指针用于处理二维数组或多个字符串指针数组定义:数组中的元素为指针变量定义形式:[存储类型] 数据类型 *数组名[数组长度说明];例 int *p[4];指针所指向变量的数据类型指针本身的存储类型区分int *p[4]与int (*p)[4]指针数组赋值与初始化赋值:main(){ int b[2][3],*pb[2];pb[0]=b[0];pb[1]=b[1];……..}int *pb[2]pb[0]pb[1]int b[2][3]123246初始化:main(){ int b[2][3],*pb[ ]={b[0],b[1]};……..}int *pb[2]pb[0]pb[1]int b[2][3]123246指针数组赋值与初始化L i s p \0F o r t r a n \0B a s i c \0p[0]p[1]p[2]p[3]0赋值:main(){ char a[]="Fortran";char b[]="Lisp";char c[]="Basic";char *p[4];p[0]=a; p[1]=b; p[2]=c; p[3]=NULL;……..}或:main(){ char *p[4];p[0]= "Fortran";p[1]= "Lisp";p[2]= "Basic";p[3]=NULL;……..}初始化:main(){ char *p[]={"Fortran", "Lisp", "Basic",NULL};……..}L i s p \0F o r t r a n \0B a s i c \0p[0]p[1]p[2]p[3]0char name[5][9]={“gain”,“much”,“stronger”, “point”,“bye”};char *name[5]={“gain”,“much”,“stronger”, “point”,“bye”};g a i n \0s t r o n g e r \0p o i n t \0m u c h \0name[0]name[1]name[2]name[3]name[4]b y e \0g a i n \0s t r o n g e r \0p o i n t \0m u c h \0b y e \0二维数组与指针数组区别:二维数组存储空间固定字符指针数组相当于可变列长的二维数组分配内存单元=数组维数*2+各字符串长度指针数组元素的作用相当于二维数组的行名但指针数组中元素是指针变量二维数组的行名是地址常量main(){ int b[2][3],*pb[2];int i,j;for(i=0;i<2;i++)for(j=0;j<3;j++)b[i][j]=(i+1)*(j+1);pb[0]=b[0];pb[1]=b[1];for(i=0;i<2;i++)for(j=0;j<3;j++,pb[i]++)printf("b[%d][%d]:%2d\n",i,j,*pb[i]);}例 用指针数组处理二维数组int *pb[2]pb[0]pb[1]int b[2][3]b[0][0] *pb[0]b[0][1] *(pb[0]+1)b[0][2] *(pb[0]+2)b[1][0] *pb[1]b[1][1] *(pb[1]+1)b[1][2] *(pb[1]+2)123246例 对字符串排序(简单选择排序)main(){ void sort(char *name[],int n), print(char *name[],int n);char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer "};int n=5;sort(name,n);print(name,n);}void sort(char *name[],int n){ char *temp;int i,j,k;for(i=0;i{ k=i;for(j=i+1;jif(strcmp(name[k],name[j])>0) k=j;if(k!=i){ temp=name[i]; name[i]=name[k]; name[k]=temp;}}}name[0]name[1]name[2]name[3]name[4]nameGreat WallFORTRANComputerFollow meBASICkjkjjji=0例 对字符串排序(简单选择排序)main(){ void sort(char *name[],int n), print(char *name[],int n);char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer "};int n=5;sort(name,n);print(name,n);}void sort(char *name[],int n){ char *temp;int i,j,k;for(i=0;i{ k=i;for(j=i+1;jif(strcmp(name[k],name[j])>0) k=j;if(k!=i){ temp=name[i]; name[i]=name[k]; name[k]=temp;}}}name[0]name[1]name[2]name[3]name[4]nameGreat WallFORTRANComputerFollow meBASICkkjjji=1k例 对字符串排序(简单选择排序)main(){ void sort(char *name[],int n), print(char *name[],int n);char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer "};int n=5;sort(name,n);print(name,n);}void sort(char *name[],int n){ char *temp;int i,j,k;for(i=0;i{ k=i;for(j=i+1;jif(strcmp(name[k],name[j])>0) k=j;if(k!=i){ temp=name[i]; name[i]=name[k]; name[k]=temp;}}}name[0]name[1]name[2]name[3]name[4]nameGreat WallFORTRANComputerFollow meBASICkkjji=2例 对字符串排序(简单选择排序)main(){ void sort(char *name[],int n), print(char *name[],int n);char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer "};int n=5;sort(name,n);print(name,n);}void sort(char *name[],int n){ char *temp;int i,j,k;for(i=0;i{ k=i;for(j=i+1;jif(strcmp(name[k],name[j])>0) k=j;if(k!=i){ temp=name[i]; name[i]=name[k]; name[k]=temp;}}}name[0]name[1]name[2]name[3]name[4]nameGreat WallFORTRANComputerFollow meBASICkkji=3例 对字符串排序(简单选择排序)main(){ void sort(char *name[],int n), print(char *name[],int n);char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer "};int n=5;sort(name,n);print(name,n);}void sort(char *name[],int n){ char *temp;int i,j,k;for(i=0;i{ k=i;for(j=i+1;jif(strcmp(name[k],name[j])>0) k=j;if(k!=i){ temp=name[i]; name[i]=name[k]; name[k]=temp;}}}name[0]name[1]name[2]name[3]name[4]nameGreat WallFORTRANComputerFollow meBASIC多级指针定义: 指向指针的指针一级指针:指针变量中存放目标变量的地址p1&p2&i3P2(指针变量)i(整型变量)例 int **p1;int *p2;int i=3;p2=&i;p1=&p2;**p1=5;二级指针:指针变量中存放一级指针变量的地址例 int *p;int i=3;p=&i;*p=5;&i3P(指针变量)i(整型变量)一级指针单级间接寻址二级指针一级指针目标变量二级间接寻址定义形式:[存储类型] 数据类型 **指针名;如 char **p;例 int i, **p;p=&i; ( )//p是二级指针,不能用变量地址为其赋值指针本身的存储类型最终目标变量的数据类型*p是p间接指向对象的地址**p是p间接指向对象的值例 int i=3;int *p1;int **p2;p1=&i;p2=&p1;**p=5;ip1p23&i&p1**p2, *p1*p2多级指针例 三级指针 int ***p;四级指针 char ****p;20002008200A20022004200612变量a变量b(main)指针变量p2000指针变量q2002例 一级指针与二级指针#include void swap(int *r,int *s){ int *t;t=r;r=s;s=t;}main(){ int a=1,b=2,*p,*q;p=&a;q=&b;swap(p,q);printf("%d,%d\n",*p,*q);}20022000COPY指针变量s指针变量r(swap)指针变量t20002002200020002008200A20022004200612变量a变量b(main)指针变量p2000指针变量q2002例 一级指针与二级指针#include void swap(int *r,int *s){ int *t;t=r;r=s;s=t;}main(){ int a=1,b=2,*p,*q;p=&a;q=&b;swap(p,q);printf("%d,%d\n",*p,*q);}输出: 1,2例 一级指针与二级指针#include void swap(int *r,int *s){ int *t;t=r;r=s;s=t;}main(){ int a=1,b=2,*p,*q;p=&a;q=&b;swap(p,q);printf("%d,%d\n",*p,*q);}abpqabpqrsabpqsrabpq输出: 1,2例 一级指针与二级指针#include void swap(int **r,int **s){ int *t;t=*r;*r=*s;*s=t;}main(){ int a=1,b=2,*p,*q;p=&a;q=&b;swap(&p,&q);printf("%d,%d\n",*p,*q);}20002008200A20022004200612变量a变量b(main)指针变量p2000指针变量q200220062004COPY二级指针s二级指针r(swap)指针变量t200020022000例 一级指针与二级指针#include void swap(int **r,int **s){ int *t;t=*r;*r=*s;*s=t;}main(){ int a=1,b=2,*p,*q;p=&a;q=&b;swap(&p,&q);printf("%d,%d\n",*p,*q);}20002008200A20022004200612变量a变量b(main)指针变量p2000指针变量q200220002002输出: 2,1例 一级指针与二级指针#include void swap(int **r,int **s){ int *t;t=*r;*r=*s;*s=t;}main(){ int a=1,b=2,*p,*q;p=&a;q=&b;swap(&p,&q);printf("%d,%d\n",*p,*q);}abpqbapqabrspqabrspq输出: 2,1例 用二级指针处理字符串#define NULL 0void main(){char **p;char *name[]={"hello","good","world","bye",""};p=name+1;printf("%o : %s ", *p,*p);p+=2;while(**p!=NULL)printf("%s\n",*p++);}name[0]name[1]name[2]name[3]name[4]char *name[5]worldbye\0hellogoodnamep运行结果:644 : good bye用*p可输出地址(%o或%x),也可用它输出字符串(%s)p *(p++)二级指针与指针数组的关系int **p 与 int *q[10]指针数组名是二级指针常量p=q; p+i 是q[i]的地址指针数组作形参,int *q[ ]与int **q完全等价;但作为变量定义两者不同系统只给p分配能保存一个指针值的内存区;而给q分配10块内存区,每块可保存一个指针值命令行参数命令行:在操作系统状态下,为执行某个程序而键入的一行字符命令行一般形式:命令名 参数1 参数2………参数nmain(int argc, char *argv[]){ ………}命令行参数传递带参数的main函数形式:C:\TC> copy[.exe] source.c temp.c有3个字符串参数的命令行命令行中参数个数元素指向命令行参数中各字符串首地址形参名任意命令行实参main(形参)系统自动调用main函数时传递第一个参数: main所在的可执行文件名例 输出命令行参数/*test.c*/main(int argc, char *argv[]){ while(argc>1){ ++argv;printf("%s\n",*argv);--argc;}}main(int argc, char *argv[]){ while(argc-->0)printf("%s\n",*argv++);}1. 编译、链接test.c,生成可执行文件test.exe2. 在DOS状态下运行(test.exe所在路径下)例如: C:\TC> test[.exe] hello world!运行结果:helloworld!运行结果:testhelloworld!argv[0]argv[1]argv[2]char *argv[]worldtesthelloargvargc=3定义含义int i;int *p;int a[n];int *p[n];int (*p)[n];int f();int *p();int (*p)();int **p;定义整型变量ip为指向整型数据的指针变量定义含n个元素的整型数组an个指向整型数据的指针变量组成的指针数组pp为指向含n个元素的一维整型数组的指针变量f为返回整型数的函数p为返回指针的函数,该指针指向一个整型数据p为指向函数的指针变量,该函数返回整型数p为指针变量,它指向一个指向整型数据的指针变量指针的数据类型例 下列定义的含义(1)int *p[3];(2)int (*p)[3];(3)int *p(int);(4)int (*p)(int);(5)int *(*p)(int);(6)int (*p[3])(int);(7)int *(*p[3])(int);函数指针数组,函数返回int型指针指针数组指向一维数组的指针返回指针的函数指向函数的指针,函数返回int型变量指向函数的指针,函数返回int 型指针函数指针数组,函数返回int型变量(共117张PPT)《C程序设计》信息工程学院 黄涛第三章 C语言的数据类型、运算符及表达式*3.6 变量赋初值变量赋初值一个变量在定义之后,第一次赋值之前,它的值是不确定的。所以在对变量进行正确的初始化之前,不能让其参与运算,否则会得到不可预知的结果。int Total, Average;Total = Average * 40;Total和Average的值都是不确定的。变量赋初值◆ 可以在定义变量的同时进行初始化如:int TotalStudent=40;等价于:int TotalStudent;TotalStudent=40;变量赋初值◆当一次定义多个变量时,可以对其全部或部分进行初始化如:int TotalStudent=40, average,TotalScore=0;变量赋初值◆当一次定义多个变量时,不能用连续=号的方式给多个变量赋同一初值如:int TotalScore = AverScore = 0; 是错误的。正确的应该是:int TotalScore=0, AverScore=0;3.7 各类数值型数据间的混合运算各类数值型数据间的混合运算◆不同类型的数值型数据之间可以进行混合运算,即是说,各种整型数据、实型数据和字符型数据可以在一起构成表达式。如:10+‘a’+1.5*123.45*’B’◆系统在求解包含混合运算的表达式时,先把不同类型的数据转换成同一类型,然后进行运算。转换规则如图:各类数值型数据间的混合运算doublelongunsignedintfloatchar, short高低横向向左的箭头表示必定的转换。比如,char型数据必定先转换成int型,float型必定会转换成double型。纵向向上的箭头表示当有这些不同类型数据混合运算是才需要进行的转换。各类数值型数据间的混合运算:例1#include int main( ){int a=3, b=5, c=0;c = a/b;printf(“c=%d\n”, c); c = b/a;printf(“c=%d\n”, c);return(0);}两个整型变量a和b运算的结果肯定是整数,所以a/b这个除法表达式的值是 3/5,等于0。注意1:两个整数运算表达式的值一定是整数。各类数值型数据间的混合运算:例1#include int main( ){int a=3, b=5, c=0;c = a/b;printf(“c=%d\n”, c); c = b/a;printf(“c=%d\n”, c);return(0);}同样,a/b 运算的结果也肯定是整数,所以这个除法表达式的值是5/3 ,等于1。注意1:两个整数运算表达式的值一定是整数。各类数值型数据间的混合运算:例2#include void main( ){ unsigned int d=1, e=2; if ( d - e > 0 )printf(“d>e\n”);elseprintf(“d}此处是由两个无符号整型变量d和e构成的减法表达式,其值一定是正数,不会等于负数。所以,当进行d-e>0的判断时,得到的结论就是Yes,从而输出“d>e”,而实际上d不大于e。注意2:两个无符号整数运算表达式的值一定是正数。3.8 算术运算符和算术表达式基本的算术运算符优先级别和结合性的概念强制类型转换运算符自增、自减运算符算术运算符和算术表达式◆算术运算符(双目):+ 加 - 减 * 乘 / 除 % 求余◆算术表达式:用上述算术运算符按规定把运算量连接起来构成的式子就是算术表达式。其中,参与%求余运算的运算量必须是整型数据类型。如:20*4 ; 10-3+5*2-8%9/3 ;3%5算术运算符的优先级和结合性算术运算符的优先级:*、/、% 同级+、- 同级高低算术运算符的结合性:自左向右为了保证写出的含有多个不同运算符的表达式能计算出正确的结果,就必须掌握每种运算符的优先级和结合性。优先级和结合性的概念◆优先级:运算符的优先级并不决定计算的顺序,它只决定和它相邻的运算量是否优先和它一起构成相应的表达式。◆结合性:系统在求解一个表达式之前,先要对其进行“因式分解”。这个分解过程的依据就是各种运算符的“优先级”。整个表达式不断分解因式,分解完成后,最终确定为一种级别最低的运算表达式,然后就按照这种运算符的结合性开始计算整个表达式的值。优先级和结合性的概念参考课本p375-p376的附录ⅢC语言全部34种运算符的性质以及优先级和结合性。优先级和结合性的概念分解因式 (按各种运算符的优先级)10 - 3 + 5 * 2 - 8 % 9 / 310 - 3 + (5 * 2) – (8 % 9 / 3)7 + (5 * 2) – (8 % 9 / 3)7 + 10 – (8 % 9 / 3)17 – (8 % 9 / 3)17 – (8 / 3)17 – 215开始求解 (按各种运算符的结合性)强制类型转换运算符和表达式◆强制类型转换运算符(单目)运算符: (类型名)用 法: (类型名) (变量或表达式)优先级: 高于算术运算符结合性: 右结合性如:(long)(length-200)如:(float)5/3 相当于:((float)5)/3如:(int)x%(int)y相当于:((int)x)% ((int)y)强制类型转换运算符和表达式◆用途:① 满足某些运算符的规定(比如求余运算符%)② 确保函数调用时实参与形参的类型一致强制类型转换运算符和表达式:例#include void main( ){ float a = 3.5, b = 2.3;int c; c = (int) a - (int) b ;printf(“c=%d\n”, c); c = (int) a % c ;printf(“c=%d\n”, c);}由于强制类型转换运算符的优先级高于算术运算符,所以就相当于:((int)a)-((int)b)强制类型转换运算符和表达式:例#include void main( ){ float a = 3.5, b = 2.3;int c; c = (int) a - (int) b ;printf(“c=%d\n”, c); c = (int) a % c ;printf(“c=%d\n”, c);}参与求余运算的运算量必须是整型,所以此处须进行强制类型转换才行。所以就相当于:((int) a) % c强制类型转换运算符和表达式:例#include void main( ){ float a = 3.5, b = 2.3;int c; c = (int) a - (int) b ;printf(“c=%d\n”, c); c = (int) a % c ;printf(“c=%d\n”, c);}注意:参与强制类型转换的变量本身的类型并不会改变。自增、自减运算符◆自增、自减运算符(单目)运算符: ++ 自增 - - 自减优先级: 高于算术运算符,和强制类型转换运算符同级结合性: 右结合性作 用: 使变量的值加1或减1自增、自减运算符◆用法:++ 变量 -- 变量 (运算符前置用法)变量 ++ 变量 -- (运算符后置用法)运算符前置时: 变量先加(减)1,后被使用(参与其它运算) ++ i -- i 两种用法的区别在哪?运算符后置时: 变量先被使用(参与其它运算) ,后加(减)1 i ++ i --自增、自减运算符:例#include int main( ){int a = 5, m;m = ++ a;printf("a=%d, m=%d\n", a, m);m = a - -;printf("a=%d, m=%d\n", a, m);++ a; m - -;printf("a=%d, m=%d\n", a, m);printf("a=%d, m=%d\n", a - -, ++ m );}由于运算符前置,所以变量a先加1,然后再被使用。此处的使用是参与赋值运算,所以把已经加过1的变量a 赋值给变量m 。整个过程相当于顺序执行以下两个语句:a=a+1; m=a;自增、自减运算符:例#include int main( ){int a = 5, m;m = ++ a;printf("a=%d, m=%d\n", a, m);m = a - -;printf("a=%d, m=%d\n", a, m);++ a; m - -;printf("a=%d, m=%d\n", a, m);printf("a=%d, m=%d\n", a - -, ++ m );}由于运算符后置,所以先使用,即把a赋值给m ,然后再减1。整个过程相当于顺序执行以下两个语句:m=a; a=a-1;自增、自减运算符:例#include int main( ){int a = 5, m;m = ++ a;printf("a=%d, m=%d\n", a, m);m = a - -;printf("a=%d, m=%d\n", a, m);++ a; m - -;printf("a=%d, m=%d\n", a, m);printf("a=%d, m=%d\n", a - -, ++ m );}由于这两个表达式是单一的自增自减表达式,并没有参与其它的运算,所以其作用只是将变量加1或减1。自增、自减运算符:例#include int main( ){int a = 5, m;m = ++ a;printf("a=%d, m=%d\n", a, m);m = a - -;printf("a=%d, m=%d\n", a, m);++ a; m - -;printf("a=%d, m=%d\n", a, m);printf("a=%d, m=%d\n", a - -, ++ m );}这个地方的两个自增自减表达式也要被使用:去参与输出操作。所以同样存在着先加减还是先使用的问题。#include int main( ){int a = 5, m;m = ++ a;printf("a=%d, m=%d\n", a, m);m = a - -;printf("a=%d, m=%d\n", a, m);++ a; m - -;printf("a=%d, m=%d\n", a, m);printf("a=%d, m=%d\n", a - -, ++ m );}自增、自减运算符:例运行结果:a=6, m=6a=5, m=6a=6, m=5a=6, m=6自增、自减运算符使用注意事项注意:自增、自减运算符的运算量只能是变量,不可能是常量或表达式。 为什么?因为自增、自减运算符实际上具有赋值功能,是C语言中除了赋值运算符之外唯一可以改变变量值的运算符。++ ii = i + 1- - ii = i - 1而可以被赋值的量只能是变量,常量和表达式不可能被赋值。自增、自减运算符使用注意事项像下面这样的用法就是错误的:++ 10 (x+y) ++◆用途:①常用于循环结构中循环变量的自动增减②常用于指针变量的自动增减,使其指向相邻的内存单元3.9 赋值运算符和赋值表达式赋值运算符赋值表达式赋值运算符◆赋值运算符(双目)运算符: =优先级: 只比逗号运算符级别高,位于整个运算符优先级表的倒数第二位;低于算术运算符。结合性: 右结合性作 用: 将一个数据赋值给一个变量,这个数据可以是常量、变量或表达式。赋值表达式如:num=40 把常量40赋值给变量num如:aver=total/num 把表达式total/num的值赋给变量aver如:temp=aver 把变量aver的值赋给变量temp◆用赋值运算符把一个变量和一个表达式连接起来的式子。一般形式: 变量=表达式赋值表达式的注意事项注意:赋值表达式的左边只能是变量,而右边可以是常量、变量或任何合法的C表达式。像下面这样的用法就是错误的:10 = x x+y = 1赋值表达式的求解过程◆赋值表达式的求解过程:由于赋值运算符是“右结合性”,所以先求解赋值运算符右边的表达式,再将该表达式的值赋给左边的变量。最后,整个赋值表达式的值就等于被赋值后的左边变量的值。可见,求解赋值表达式值的过程,伴随着给变量赋值的作用。赋值表达式的求解过程我们写一个赋值表达式,系统会求解它并得到一个值。而我们的目的通常都是为了给一个变量赋值(这个目的是在系统求解赋值表达式的过程当中实现的),因此往往并不关心最后求出的赋值表达式的值(就是说,不会去使用这个值)。赋值表达式的求解过程x = - b / (2 * a )x = - 1为了求解整个赋值表达式的值 先求解=号右边的表达式的值再把求解出的=号右边的表达式 的值赋给=号左边的变量- 1x最后,原来的赋值表达式的值就等于被赋值后的变量的值:x = -b/(2*a)的值就等于- 1x赋值表达式:例#include void main( ){int a, b, c;/* int a = b = c = 10; */a = b = c = 10;printf(“a=%d, b=%d, c=%d\n”, a, b, c);/* c = ( a * 3 ) = b; */c = ( a = 3 ) * b;printf(“a=%d, b=%d, c=%d\n”, a, b, c);}是错误的。因为当一次定义多个变量时,不能用连续=号的方式给多个变量赋同一初值.赋值表达式:例#include int main( ){int a, b, c;/* int a = b = c = 10; */a = b = c = 10;printf(“a=%d, b=%d, c=%d\n”, a, b, c);/* c = ( a * 3 ) = b; */c = ( a = 3 ) * b;printf(“a=%d, b=%d, c=%d\n”, a, b, c);}是正确的。因为这是一个赋值表达式。因式分解的结果就相当于: a=(b=(c=10))赋值表达式:例#include int main( ){int a, b, c;/* int a = b = c = 10; */a = b = c = 10;printf(“a=%d, b=%d, c=%d\n”, a, b, c);/* c = ( a * 3 ) = b; */c = ( a = 3 ) * b;printf(“a=%d, b=%d, c=%d\n”, a, b, c);}是错误的。因为=号左边只能是变量。赋值表达式:例#include int main( ){int a, b, c;/* int a = b = c = 10; */a = b = c = 10;printf(“a=%d, b=%d, c=%d\n”, a, b, c);/* c = ( a * 3 ) = b; */c = ( a = 3 ) * b;printf(“a=%d, b=%d, c=%d\n”, a, b, c);}是正确的。#include int main( ){int a, b, c;/* int a = b = c = 10; */a = b = c = 10;printf(“a=%d, b=%d, c=%d\n”, a, b, c);/* c = ( a * 3 ) = b; */c = ( a = 3 ) * b;printf(“a=%d, b=%d, c=%d\n”, a, b, c);}赋值表达式:例运行结果:a=10, b=10, c=10a=3, b=10, c=303.10 逗号运算符和逗号表达式逗号运算符逗号表达式逗号运算符◆逗号运算符(多目)运算符: ,用 法: 表达式1,表达式2,表达式3,……,表达式n优先级: 整个运算符优先级表中最低的结合性: 自左向右作 用: 用逗号把若干个合法的C表达式连接起来形成一个表达式,让系统依次求解逗号表达式◆逗号表达式:用逗号运算符把若干个合法的C表达式连接起来形成的一个式子。如: i=1, j=1, sum=0如: x+y, 10, z=x%y, z逗号表达式的求解过程◆逗号表达式的求解过程逗号运算符又称“顺序求解运算符”,即对于一个逗号表达式,其求解过程是:先从左到右依次求解用逗号连接的每个表达式,而整个逗号表达式的值就等于最后那个表达式的值。逗号表达式的求解过程◆逗号表达式的用途从上述求解逗号表达式的过程可以看出,为了求出整个逗号表达式的值,必须先依次求出逗号表达式中每个表达式的值,因此,写逗号表达式的目的往往不是为了整个表达式的值,而是利用求解逗号表达式的过程来求解其中每个表达式。1, 1, 0逗号表达式的求解过程i=1, j=1, sum=0逗号表达式:由3个赋值表达式构成依次求解3个赋值表达式的值0逗号表达式的值是最后那个赋值表达式的值。这个值往往不被使用。逗号表达式:例#include void main( ){int x, b;x = 0, b = 0 ;x = ( b = 3, 2 * b ) ;printf(“x=%d, b=%d\n”, x, b);x = b = 3, 2 * b ;printf(“x=%d, b=%d\n”, x, b);}是逗号表达式。求解它就要依次求解两个赋值表达式,而求解赋值表达式的过程又具有给变量赋值的作用。逗号表达式:例#include void main( ){int x, b;x = 0, b = 0 ;x = ( b = 3, 2 * b ) ;printf(“x=%d, b=%d\n”, x, b);x = b = 3, 2 * b ;printf(“x=%d, b=%d\n”, x, b);}所以这个逗号表达式的作用就如同表达式 x=b=0 一样,但是和x=0,b=0 两者的本质是不一样的。逗号表达式:例#include void main( ){int x, b;x = 0, b = 0 ;x = ( b = 3, 2 * b ) ;printf(“x=%d, b=%d\n”, x, b);x = b = 3, 2 * b ;printf(“x=%d, b=%d\n”, x, b);}是赋值表达式。是把右边逗号表达式的值赋给变量x。逗号表达式:例#include void main( ){int x, b;x = 0, b = 0 ;x = ( b = 3, 2 * b ) ;printf(“x=%d, b=%d\n”, x, b);x = b = 3, 2 * b ;printf(“x=%d, b=%d\n”, x, b);}是逗号表达式。依次求解x=b=3和2*b两个表达式。逗号表达式:例#include void main( ){int x, b;x = 0, b = 0 ;x = ( b = 3, 2 * b ) ;printf(“x=%d, b=%d\n”, x, b);x = b = 3, 2 * b ;printf(“x=%d, b=%d\n”, x, b);}运行结果:x=6, b=3x=3, b=3课堂练习◆1. 以下各组标识符中,全部是合法C 标识符的是( )(A) string,sum,Double,1st(B) D56,bStatus,strTemp,Limit(C) name,float,month,stu_n1(D) class1,day,string_1,Score.1答案:(B)课堂练习◆2. 下列变量说明语句中,正确的是( )(A) char a = b = c = 0;(B) char a; b; c;(C) int x, char c;(D) int x, y, z = 0;答案:(D)课堂练习◆3. 要将字符 A 赋给字符变量ch,下列语句中正确的是( )(A) ch = A (B) ch = ‘a’ - 32(C) ch = “A” (D) ch = “65”答案:(B)课堂练习◆4.设a为整型变量, 下列表达式中,不正确的是 ( )a = a + 1(B) a ++(C) (++ a) + 1(D) a + (1 ++)答案:(D)字符输出函数putchar字符输入函数getchar格式输出函数printf格式输入函数scanf各种格式控制字符的含义和用法简单的程序设计第四章 顺序结构程序设计4.1 C的语句C 程序的结构C 程序 (project 项目或工程)源程序文件1源程序文件2……源程序文件n预处理命令全局变量声明……函数n函数1函数首部函数体局部变量声明执行语句C 语句的分类控制语句:9种,实现对程序流程的控制① if( ) … else … (条件分支语句)② for( ) … (循环控制语句)③ while( ) … (循环控制语句)④ do … while( ) (循环控制语句)⑤ continue (结束本次循环语句)⑥ break (终止switch或循环语句)⑦ switch (多分支选择语句)⑧ goto (无条件跳转语句)⑨ return (从函数返回语句)C 语句的分类表达式语句:任何合法的C表达式加上一个分号如:num=1; num++;函数调用语句:任何合法的C函数调用加上一个分号如:printf(”num=%d\n” , num);C 语句的分类空语句:只有一个分号复合语句:用一对大括弧 { } 把若干语句括起来就成为一个复合语句,又称为分程序。如:{int a=1; a++; printf(“a=%d”, a);}4.3 输入输出的概念及在C语言中的实现输入输出的概念及在C 语言中的实现输入是指从外部设备(如键盘,扫描仪,文件等)向计算机主机送入数据;输出是从计算机主机向外部设备(如显示器,打印机,文件等)送出数据。C语言本身没有输入输出语句。要实现这些操作,必须调用相应的系统库函数。比如 printf 函数用于输出,scanf 函数用于输入,等等。输入输出的概念及在C 语言中的实现不同版本的C 语言(C 编译器)均带有不同数量不同用途的系统库函数,以 .obj 文件或 .lib 文件的形式提供,供用户与自己的 .obj 文件链接以形成 .exe 的可执行文件 。C 语言中的头文件要想正确成功的调用C 系统库函数,就必须在源程序文件开头用预编译命令“#include”将有关的“头文件”包括进来。如:要想调用输出库函数printf或键盘输入库函数scanf,在源程序文件开头必须有如下预编译命令#include C 语言中的头文件C语言中的头文件(header file)是指后缀名习惯为“.h”的一些文本文件。在头文件中,包含有关系统库函数的原型、有关系统全局变量的声明、有关系统符号常量的宏定义、有关数据类型的定义等等信息。C 语言中的头文件其中,系统库函数的原型就告诉编译系统该函数的名称、返回值类型、形参数量及类型等信息,这样编译系统才能正确编译,否则会给出出错信息。int Max(int a, int b);返回值类型函数名形参数量及类型C 语言中的头文件stdio.h头文件中包含了与标准I/O操作有关的系统库函数的声明,所以调用如printf、scanf等标准输入输出函数时,必须在源程序文件中包含该头文件。否则,编译时会给出错误信息如下:Function ‘printf’ should have a prototype函数 ‘printf’ 应该有一个原型C 语言中的头文件提示:要想知道某个版本的C语言提供哪些系统库函数以及各种库函数所对应的头文件的名称,必须参考该版本C语言的系统库函数手册或联机帮助。库函数类别 头文件名 库函数类别 头文件名标准输入输出 stdio.h 字符串处理 string.h数学 math.h 图形处理 graphics.h字符处理 ctype.h4.4 字符数据的输入输出字符输出函数 putchar字符输入函数 getchar字符输出函数putchar原型:int putchar (int ch);参数:一个字符型或整型的数据功能:在默认的输出设备(显示器)上输出一个字符用法:putchar (字符型或整型数据);返回值:所输出的字符。绝大多数情况下不使用该返回值。字符输出函数putchar :例#include int main( ){char a, b; int c, d;a = 'B'; b = 'O';c = 65; d = 66;putchar(a); putchar(b);putchar('Y');putchar(‘\n’);putchar(c); putchar(d+32);return(0);}参数可以是int或char型变量字符输出函数putchar :例#include int main( ){char a, b; int c, d;a = 'B'; b = 'O';c = 65; d = 66;putchar(a); putchar(b);putchar('Y');putchar(‘\n’);putchar(c); putchar(d+32);return(0);}参数也可以是int或char型常量字符输出函数putchar :例#include int main( ){char a, b; int c, d;a = 'B'; b = 'O';c = 65; d = 66;putchar(a); putchar(b);putchar('Y');putchar(‘\n’);putchar(c); putchar(d+32);return(0);}参数还可以是int或char型表达式字符输出函数putchar :例#include int main( ){char a, b; int c, d;a = 'B'; b = 'O';c = 65; d = 66;putchar(a); putchar(b);putchar('Y');putchar(‘\n’);putchar(c); putchar(d+32);return(0);}运行结果:BOY Ab字符输入函数getchar原型:int getchar (void) ;参数:无功能:从默认的输入设备(键盘)上接收用户键入的一个字符,并把该字符作为函数值返回用法:ch=getchar();返回值:用户从键盘键入的字符。绝大多数情况该字符都会被赋值给某个字符型或整型的变量保存起来,或者作为表达式的一部分参与其它的运算。字符输入函数getchar注意:getchar 函数只能接收并返回一个字符。执行过程:当程序运行到包含有getchar函数调用的语句时,会暂时停下来,此时屏幕上有一个闪烁的光标,等待用户从键盘上的输入。用户必须键入有关信息并按Enter键后程序才能继续向下执行。字符输入函数getchar :例#include int main( ){char ch;ch = getchar( );putchar(ch);return(0);}由于 getchar 函数要接收用户从键盘输入的字符,所以程序流程执行到这儿会暂时停下来等待用户的输入。字符输入函数getchar :例#include int main( ){char ch;ch = getchar( );putchar(ch);return(0);}当用户键入一个字符并按下Enter键后,该字符被getchar函数接收并作为函数值返回。此处,将这个返回值同时赋值给一个字符型的变量ch,保存起来以作后用。字符输入函数getchar :例#include int main( ){char ch;ch = getchar( );putchar(ch);return(0);}运行结果:B(Enter)(用户键入字符B后,再键入“回车”键)B(putchar函数再在屏幕上输出一个字符B)4.5 格式输入输出函数格式输出函数 printf格式输入函数 scanf格式输出函数printf原型:int printf(const char *format [,argument, ...]);参数:两部分第一部分:为字符型常指针,大多数情况下都是字符串常量第二部分:若干个待输出的数据的列表注:[ ] 中的参数表示是可选的。格式输出函数printf功能:一次函数调用可以在默认的输出设备上(显示器)输出若干个不同类型的经过格式化后的数据。用法:printf(格式控制字符串,输出数据列表);返回值:输出的所有数据的字节数。绝大多数情况下不使用该返回值。格式输出函数printf的参数printf(格式控制字符串,输出数据列表);⒈ 格式控制字符串:常字符指针,既可以是字符串常量,也可以是字符数组或字符指针。⒉ 输出数据列表:将要输出的数据,用逗号分隔。可以是常量,变量、函数调用或任何合法的C表达式。printf 函数中的格式控制字符串格式控制字符:由%和规定的格式字符组成,一个格式控制字符串中可以有一个或多个格式控制字符。如: %d %c %f格式控制字符串中通常包含两类字符:格式字符的作用是:把后面输出项目列表中对应位置上的数据格式化成该格式字符所规定的数据类型。printf 函数中的格式控制字符串普通字符:除格式字符以外的所有其它字符。它们不起任何格式作用,只简单被printf函数原样输出到显示器上。通常用于说明输出数据或给用户有关的提示说明信息。格式控制字符串中通常包含两类字符:int num = 15;printf(“Twice %d is %d .\n” , num, 2*num);格式输出函数printf的执行过程Twice普通字符格式字符普通字符格式字符普通字符15is30._待输出的数据普通字符:原样输出 格式字符:格式化要输出的数据printf函数中的格式字符格式字符 格式作用整型 d, i 格式化为有符号的十进制整数u 格式化为无符号的十进制整数o 格式化为无符号的八进制整数x, X 格式化为无符号的十六进制整数字符型 c 格式化为一个字符s 格式化为一个字符串printf函数中的格式字符格式字符 格式作用实型 f 格式化为小数形式的实数,隐含输出6位小数e, E 格式化为指数形式的实数,数字部分有6位小数g, G 选用%f或%e格式中输出宽度较短的一种格式,不输出无意义的0printf函数中的附加格式字符在格式说明时,还可以在%和上述格式字符之间插入几种附加的格式字符。(又叫修饰符)。% [附加格式字符] 格式字符% [-][m][.n][l] 格式字符printf函数中的附加格式字符附加 格式字符 格式作用l 用于格式化长整型数据,可加在格式符d、o、x、u的前面m 用于确定数据输出时所占最小宽度,即列数.n 对实型数,表示输出n位小数;对字符串,表示截取的字符个数- 输出的数字或字符在输出域内向左对齐(默认是右对齐的)printf函数使用中的注意事项一定要分清哪些是格式字符,哪些是普通字符;同时也要注意区分普通字符中的转义字符常量。如:%d 是格式字符,%D 不是格式字符如:%cf 中%c是格式字符,f是普通字符如:\n是转义字符,\a就不是转义字符printf函数使用中的注意事项如果作为第一部分参数的字符串中,全部是普通字符,则具有输出字符串的效果,这通常用于在程序运行时给使用者有关提示信息,或对输出信息作有关的注释和说明。如:printf(“Welcome!\n”);printf(“Author: LiChangsong”);printf函数使用中的注意事项待输出数据的个数不能少于格式说明字符的个数;如果多于格式说明字符的个数,则多出的数据不被输出。如:printf(“%d, %d\n”, a, b, c, d, e);用%%输出一个%如:printf(“percent=%d%%\n”, pcn);printf函数的使用:例#include void main( ){unsigned int a = 65535; int b = -2;printf("a = %d, %o, %x, %u\n", a, a, a, a);printf("b = %d, %o, %x, %u\n", b, b, b, b);}运行结果:a = -1, 177777, ffff, 65535b = -2, 177776, fffe, 65534a 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1b 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0printf函数的使用:例#include void main( ){long c = 4000000;float f1 = 12345.67;printf("c = %ld, %d\n", c, c);printf("f1 = %f, %e\n", f1, f1);}运行结果:c = 4000000, 2304f1 = 12345.669922, 1.234567e+04printf函数的使用:例#include void main( ){printf("%c%c%c", 'B', 'O', 'Y');printf("%c", '\n');printf("%s", "China");printf("%s", "\n");printf("Please input three data:");}运行结果: BOYChinaPlease input three data:格式输入函数scanf1. 原型:int scanf(const char *format [,address, ...]);2. 参数:两部分第一部分:为字符型常指针,大多数情况下都是字符串常量第二部分:若干个内存单元地址的列表注:[ ] 中的参数表示是可选的。格式输入函数scanf3. 功能:一次函数调用可以从默认的输入设备上(键盘)扫描用户的输入并经格式化为若干个不同类型的数据后依次保存到指定的地址列表中。4. 用法:scanf(格式控制字符串,地址列表);5. 返回值:扫描、格式化后保存的数据的个数。绝大多数情况下不使用该返回值。格式输入函数scanf的参数scanf(格式控制字符串,地址列表);⒈ 格式控制字符串:常字符指针,既可以是字符串常量,也可以是字符数组或字符指针。⒉ 地址列表:一些已经分配给某些变量的内存单元的地址的列表,scanf 函数将把扫描用户输入并格式化后的数据按顺序依次送到这些地址所指向的单元中去。scanf 函数中的格式控制字符串格式控制字符:由%和规定的格式字符组成,一个格式控制字符串中可以有一个或多个格式控制字符。格式控制字符串中可能包含两类字符:格式字符的作用是:把扫描用户键盘输入后得到的数据格式化成该格式字符所规定的类型的数据。如: %d %c %fscanf 函数中的格式控制字符串普通字符:除格式字符以外的所有其它字符。它们不但不起任何格式作用,也不被显示,反而要求使用者在输入信息时照原样输入,否则函数将不能扫描到正确的数据。因此,通常在scanf函数的第一部分参数中,不要包含除必要的分隔符号之外的任何字符。格式控制字符串中可能包含两类字符:注:人们一般约定俗成在输入数据时,用逗号作为分隔符号取变量地址运算符&◆ 取地址运算符(单目)运算符: &用 法: & 变量优先级: 处于整个运算符优先级表中的第二级,高于所有的双目运算符结合性: 自右向左作 用: 得到变量所占内存单元的地址取变量地址运算符&注意:& 运算符的运算量只能是变量,即是说只能取变量的地址,不能取常量或表达式的地址。如:int a; float fnum;&a; /*取变量a的地址*/&fnum; /*取变量fnum的地址*/&10 &(x+y)像下面这样的用法就是错误的:int num1, num2;scanf(“num1=%d ,num2=%d” , &num1, &num2);格式输入函数scanf的执行过程num1=普通 字符格式字符普通 字符格式字符15,num2=30↙地址列表1530num1num2普通字符:原样输入 格式字符:格式化扫描到的数据scanf函数中的格式字符格式字符 格式作用整型 d, i 把扫描到的用户的输入格式化为有符号的十进制整数u 把扫描到的用户的输入格式化为无符号的十进制整数o 把扫描到的用户的输入格式化为无符号的八进制整数x, X 把扫描到的用户的输入格式化为无符号的十六进制整数scanf函数中的格式字符格式字符 格式作用字符型 c 把扫描到的用户的输入格式化为一个字符s 把扫描到的用户的输入格式化为一个字符串实型 f 把扫描到的用户的输入格式化为实数,用户可以用小数形式或指数形式输入e, E 与 f 的作用相同g, G 与 f 的作用相同scanf函数使用中的注意事项一定要记住第二部分参数是变量地址的列表,而不是变量的列表。如:下面是正确的scanf(“%d, %d”, &num1, &num2);如:下面是错误的scanf(“%d, %d”, num1, num2);scanf函数使用中的注意事项2.在scanf中,作为第一部分参数的字符串中出现的普通字符,不但不会在执行该函数的时候显示出来,反而是要求使用者按照原样输入,否则无法扫描到需要的数据并送到相应的地址单元去。而实际上,使用者是无法确切知道这些普通字符的,所以也就无法正确输入。因此,在scanf函数的格式控制字符串中,一般不要出现普通字符,除了必要的数据分隔符号(一般是逗号)。scanf函数使用中的注意事项例如:有如下scanf语句scanf(“num=%d”, &num);程序设计者原以为在执行到这一句时,屏幕上会出现 num= 的提示信息,然后由用户输入需要的数据。但事实上是:屏幕上不但不会显示这样的提示信息,反而要求用户在此时原样输入这些字符,然后再输入需要的数据,即必须输入 num=15(回车) , 这样才能把正确的数据送到变量 num 中。scanf函数使用中的注意事项如果程序中需要在要求用户输入前给用户以相应的提示,通常是在调用scanf函数之前用printf函数输出有关信息。如:printf(“Please input 2 integer ”);scanf(“%d, %d”, &num1, &num2);课堂练习◆ 1. 已知字母a的ASCII十进制码为97,则执行以下语句后的输出是( )char a = ‘a’;a -- ;printf(“%d\n”, a + ‘2’ - ‘0’);printf(“%c\n”, a + ‘2’ - ‘0’);答案: 98b#include void main(){float ft, ct;scanf(“%f” , ft);ct = 5 / 9 (ft – 32);printf(‘ft=%f, ct=%f’ , ft , ct);return;}课堂练习◆ 2. 以下程序输入一个华氏温度,求出摄氏温度。请改错。(共17张PPT)格式: putchar( c )参数: c为字符常量、变量或表达式功能:把字符c输出到显示器上返值:正常,为显示的代码值;出错,为EOF(-1)第4章(1) 数据输入与输出C语言无I/O语句,I/O操作由函数实现#include 4.1 数据输出字符输出函数/*ch3_1.c*/#include main(){ int c;char a;c=65; a='B';putchar(c); putchar('\n'); putchar(a);}运行结果:AB例格式:printf(“格式控制串”,输出表)功能:按指定格式向显示器输出数据返值:正常,返回输出字节数;出错,返回EOF(-1)格式输出函数输出表:要输出的数据(可以没有,多个时以“,”分隔)格式控制串:包含两种信息格式说明: %[修饰符]格式字符 ,用于指定输出格式普通字符或转义序列:原样输出格式字符d,ix,Xoucse,Efg%%格式字符:十六进制无符号整数不带符号十进制整数十进制整数指数形式浮点小数单一字符字符串八进制无符号整数小数形式浮点小数e和f中较短一种百分号本身int a=567;printf ( “%d”,a);int a=255;printf(“%x”,a);int a=65;printf(“%o”,a);int a=567;printf(“%u”,a);char a=65;printf(“%c”,a);printf(“%s”,“ABC”);float a=567.789;printf(“%e”,a);float a=567.789;printf(“%f”,a);float a=567.789;printf(“%g”,a);printf(“%%”);567ff101567AABC5.677890e+02567.789000567.789%说明格式字符要用小写格式字符与输出项个数应相同,按先后顺序一一对应输出转换:格式字符与输出项类型不一致,自动按指定格式输出例 main(){ unsigned int u=65535;printf(”u=%d\n",u);}输出结果:u=-1例 int a=3,b=4;printf(“%d %d\n”,a,b);printf(“a=%d , b=%d\n”,a,b);例 int a=3,b=4;printf(“%d %d\n”,a,b);printf(“a=%d , b=%d\n”,a,b);输出结果: 3 4a=3, b=411 11 11 11 11 11 11 1165535附加格式说明符(修饰符).n对实数,指定小数点后位数(四舍五入)对字符串,指定实际输出位数修饰符功 能m输出数据域宽,数据长度输出数据在域内左对齐(缺省右对齐)-指定在有符号数的正数前显示正号(+)+输出数值时指定左面不使用的空位置自动填00在八进制和十六进制数前显示前导0,0x#在d,o,x,u前,指定输出精度为long型在e,f,g前,指定输出精度为double型l例 int a=1234;float f=123.456;char ch=‘a’;printf(“%8d,%2d\n”,a,a);printf(“%f,%8f,%8.1f,%.2f,%.2e\n”,f,f,f,f,f);printf(“%3c\n”,ch);运行 1234,1234结果: 123.456000,123.456000, 123.5,123.46,1.23e+02a例 static char a[]=“Hello,world!”printf(“%s\n%15s\n%10.5s\n%2.5s\n%.3s\n”,a,a,a,a,a);运行结果:Hello,world!Hello,world!HelloHelloHel例 m.n例 int a=1234;float f=123.456;static char c[]=“Hello,world!”;printf(“%8d,%-8d\n”,a,a);printf(“%10.2f,%-10.1f\n”,f,f);printf(“%10.5s,%-10.3s\n”,c,c);运行结果:1234,1234123.46,123.5Hello,Hel例 -例 int a=1234;float f=123.456;printf(“%08d\n”,a);printf(“%010.2f\n”,f);printf(“%0+8d\n”,a);printf(“0+10.2f\n”,f);例 0 、+例 int a=123;printf(“%o,%#o,%X,%#X\n”,a,a,a,a);例 #例 long a=65536;printf(“%d,%8ld\n”,a, a);例 l00 00 00 00 00 00 00 0100 00 00 00 00 00 00 00//00001234//0000123.46//000+1234//000+123.56//173,0173,7B,0X7B//0, 65536格式:getchar( )功能:从键盘读一字符返值:正常,返回读取的代码值;出错,返回EOF(-1)4.2 数据输入字符输入函数例/*ch3_4.c*/#include main(){ int c;printf("Enter a character:");c=getchar();printf("%c--->hex%x\n",c,c);}运行结果:Enter a character:A A--->hex41格式输入函数格式: scanf(“格式控制串”,地址表)功能:按指定格式从键盘读入数据,存入地址表指定的存储单元中,并按回车键结束返值:正常,返回输入数据个数地址表:变量的地址,常用取地址运算符&格式字符:d,i,o,x,u,c,s,f,e例 scanf(“%d”,&a);输入:10 则 a=10例 scanf(“%x”,&a);输入:11 则 a=17附加格式说明符(修饰符)l修饰符功 能hm*用于d,o,x前,指定输入为short型整数用于d,o,x前,指定输入为long型整数用于e,f前,指定输入为double型实数指定输入数据宽度,遇空格或不可转换字符则结束抑制符,指定输入项读入后不赋给变量例 scanf(“%4d%2d%2d”,&yy,&mm,&dd);输入 19991015 则1999 yy, 10 mm, 15 dd例 scanf(“%3d%*4d%f”,&k,&f);输入 12345678765.43 则123 k, 8765.43 f例 scanf(“%2d%*3d%2d”,&a,&b);输入 1234567 则12 a, 67 b例 scanf(“%3c%2c”,&c1,&c2);输入 abcde 则‘a’ c1, ‘d’ c2输入分隔符的指定一般以空格、TAB或回车键作为分隔符其它字符做分隔符:格式串中两个格式符间字符例 scanf(“%d%o%x”,&a,&b,&c);printf(“a=%d,b=%d,c=%d\n”,a,b,c);输入 123 123 123 输出 a=123,b=83,c=291例 scanf(“%d:%d:%d”,&h,&m,&s);输入 12:30:45 则12 h, 30 m, 45 s例 scanf(“%d,%d”,&a,&b)输入 3,4 则3 a, 4 b例 scanf(“a=%d,b=%d,c=%d”,&a,&b,&c);输入 a=12,b=24,c=36 说明:用“%c”格式符时,空格和转义字符作为有效字符输入如 scanf(“%c%c%c”,&c1,&c2,&c3);若输入abc 则a c1, c2, b c3输入数据时,遇以下情况认为该数据结束:遇空格、TAB、或回车遇宽度结束遇非法输入如 scanf(“%d%c%f”,&a,&b,&c);若输入1234a123o.26 则 1234 a, ‘a’ b, 123 c输入函数留下的“垃圾”:例 int x;char ch;scanf(“%d”,&x);ch=getchar();printf(“x=%d,ch=%d\n”,x,ch);执行:123 输出:x=123,ch=10例 int x;char ch;scanf(“%d”,&x);scanf(“%c”,&ch);printf(“x=%d,ch=%d\n”,x,ch);执行:123 输出:x=123,ch=10解决方法:(1)用getchar()清除(2)用函数fflush(stdin)清除全部剩余内容(3) 用格式串中空格或“%*c”来“吃掉”例 int x;char ch;scanf(“%d”,&x);scanf(“ %c”,&ch);或 scanf(“%*c%c”,&ch);4.3 程序举例/*ch3_12.c*/#include #include main(){ float a,b,c,s,area;scanf("%f,%f,%f",&a,&b,&c);s=1.0/2*(a+b+c);area=sqrt(s*(s-a)*(s-b)*(s-c));printf("a=%7.2f, b=%7.2f, c=%7.2f, s=%7.2f\n",a,b,c,s);printf("area=%7.2f\n",area);}例 输入三角形边长,求面积输入:3,4,6 输出:a= 3.00, b= 4.00, c= 6.00 s= 6.50area= 5.33文件包含预处理命令变量定义输入数据输出数据例 从键盘输入大写字母,用小写字母输出/*ch3_13.c*/#include "stdio.h"main(){ char c1,c2;c1=getchar();printf("%c,%d\n",c1,c1);c2=c1+32;printf("%c,%d\n",c2,c2);}输入:A 输出:A,65a,97/*ch3_14.c*/#include #include main(){ float a,b,c,disc,x1,x2,p,q;scanf("a=%f,b=%f,c=%f",&a,&b,&c);disc=b*b-4*a*c;p=-b/(2*a); q=sqrt(disc)/(2*a);x1=p+q; x2=p-q;printf("\n\nx1=%5.2f\nx2=%5.2f\n",x1,x2);}输入:a=1,b=3,c=2 输出:x1=-1.00x2=-2.00(共16张PPT)第12章 位运算为了节省内存空间,在系统软件中常将多个标志状态简单地组合在一起,存储到一个字节(或字)中。C语言是为研制系统软件而设计的,所以她提供了实现将标志状态从标志字节中分离出来的位运算功能。所谓位运算是指,按二进制位进行的运算。12.1 数值在计算机中的表示 12.2 位运算 12.3 位段[Return]12.1 数值在计算机中的表示1.二进制位与字节 计算机系统的内存储器,是由许多称为字节的单元组成的,1个字节由8个二进制位(bit)构成,每位的取值为0/1。最右端的那1位称为“最低位”,编号为0;最左端的那1位称为“最高位”,而且从最低位到最高位顺序,依次编号。图11-1是1个字节各二进制位的编号。图11-1 1个字节各二进制位的编号2.数值的原码表示数值的原码表示是指,将最高位用作符号位(0表示正数,1表示负数),其余各位代表数值本身的绝对值(以二进制形式表示)的表示形式。为简化描述起见,本节约定用1个字节表示1个整数。7 6 5 4 3 2 1 0例如,+9的原码是00001001└→符号位上的0表示正数-9的原码是10001001。└→符号位上的1表示负数3.数值的反码表示数值的反码表示分两种情况:(1)正数的反码:与原码相同。例如,+9的反码是00001001。(2)负数的反码:符号位为1,其余各位为该数绝对值的原码按位取反(1变0、0变1)。例如,-9的反码:因为是负数,则符号位为“1”;其余7位为-9的绝对值+9的原码0001001按位取反为1110110,所以-9的反码是11110110。4.数值的补码表示数值的补码表示也分两种情况:(1)正数的补码:与原码相同。例如,+9的补码是00001001。(2)负数的补码:符号位为1,其余位为该数绝对值的原码按位取反;然后整个数加1。例如,-9的补码:因为是负数,则符号位为“1”;其余7位为-9的绝对值+9的原码0001001按位取反为1110110;再加1,所以-9的补码是11110111。已知一个数的补码,求原码的操作分两种情况:(1)如果补码的符号位为“0”,表示是一个正数,所以补码就是该数的原码。(2)如果补码的符号位为“1”,表示是一个负数,求原码的操作可以是:符号位不变,其余各位取反,然后再整个数加1。例如,已知一个补码为11111001,则原码是10000111(-7):因为符号位为“1”,表示是一个负数,所以该位不变,仍为“1”;其余7位1111001取反后为0000110;再加1,所以是10000111。5.数值在计算机中的表示──补码在计算机系统中,数值一律用补码表示(存储),原因在于:使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。[Return]12.2 位 运 算12.2.1 位运算及其运算符 1.按位与──& (1)格式:x&y (2)规则:对应位均为1时才为1,否则为0:3&9=1。例如,3&9=1: 0011& 1001────0001=1(3)主要用途:取(或保留)1个数的某(些)位,其余各位置0。 2.按位或──| (1)格式:x|y (2)规则:对应位均为0时才为0,否则为1:3|9=11。例如,3|9=11: 0011| 1001────1011=11 (3)主要用途:将1个数的某(些)位置1,其余各位不变。3.按位异或──^(1)格式:x^y(2)规则:对应位相同时为0,不同时为1:3^9=10。(3)主要用途:使1个数的某(些)位翻转(即原来为1的位变为0,为0的变为1),其余各位不变。4.按位取反──~(1)格式:~x(2)规则:各位翻转,即原来为1的位变成0,原来为0的位变成1:在IBM-PC机中,~0=0xffff,~9=0xfff6。(3)主要用途:间接地构造一个数,以增强程序的可移植性。5.按位左移──<<(1)格式:x<< 位数(2)规则:使操作数的各位左移,低位补0,高位溢出:5<<2=20。6.按位右移──>>(1)格式:x>>位数(2)规则:使操作数的各位右移,移出的低位舍弃;高位:1)对无符号数和有符号中的正数,补0;2)有符号数中的负数,取决于所使用的系统:补0的称为“逻辑右移”,补1的称为“算术右移”。例如,20 >> 2=5。说明:(1)x、y和“位数”等操作数,都只能是整型或字符型数据。除按位取反为单目运算符外,其余均为双目运算符。(2)参与运算时,操作数x和y,都必须首先转换成二进制形式,然后再执行相应的按位运算。例如,5<<2=20:0101 → 10100,20 >> 2=5:10100 → 00101。(3)实现&、|、^运算主要用途的方法1)构造1个整数:该数在要取(或保留)的位、或要置1的位、或要翻转的位上为1,其余均为0。2)进行按位与、或按位或、或按位异或操作。(4)实现按位取反主要用途的方法1)求~0,间接地构造一个全1的数;2)按需要进行左移或右移操作,构造出所需要的数。例如,直接构造一个全1的数,在IBM-PC机中为0xffff(2字节),而在VAX-11/780上,却是0xffffffff(4字节)。如果用~0来构造,系统可以自动适应。具体应用,请参见[案例11.1]。12.2.2 应用举例[案例11.1] 从键盘上输入1个正整数给int变量num,输出由8~11位构成的数(从低位、0号开始编号)。基本思路:(1)使变量num右移8位,将8~11位移到低4位上。(2)构造1个低4位为1、其余各位为0的整数。(3)与num进行按位与运算。/*案例代码文件名:AL11_1.C*//*程序功能:输出一个整数中由8~11位构成的数*/main(){ int num, mask;printf("Input a integer number: ");scanf("%d",&num);num >>= 8; /*右移8位,将8~11位移到低4位上*/mask = ~ ( ~0 << 4); /*间接构造1个低4位为1、其余各位为0的整数*/printf("result=0x%x\n", num & mask);} [程序演示]程序运行情况:Input a integer number:1000 ←┘result=0x3程序说明:~ ( ~0 << 4)按位取0的反,为全1;左移4位后,其低4位为0,其余各位为1;再按位取反,则其低4位为1,其余各位为0。这个整数正是我们所需要的。[案例11.2] 从键盘上输入1个正整数给int变量num,按二进制位输出该数。/*案例代码文件名:AL11_2.C*//*程序功能:按二进制位输出一个整数*/#include "stdio.h"main(){ int num, mask, i;printf("Input a integer number: ");scanf("%d",&num);mask = 1<<15; /*构造1个最高位为1、其余各位为0的整数(屏蔽字)*/printf("%d=" , num);for(i=1; i<=16; i++){ putchar(num&mask ’1’ : ‘0’); /*输出最高位的值(1/0)*/num <<= 1; /*将次高位移到最高位上*/if( i%4==0 ) putchar(‘,’); /*四位一组,用逗号分开*/}printf("\bB\n");} [程序演示]程序运行情况:Input a integer number:1000 ←┘1000=0000,0011,1110,1000B12.2.3 说明1.复合赋值运算符除按位取反运算外,其余5个位运算符均可与赋值运算符一起,构成复合赋值运算符: &=、|+、^=、<<=、>>=2.不同长度数据间的位运算──低字节对齐,短数的高字节按最高位补位:(1)对无符号数和有符号中的正数,补0;(2)有符号数中的负数,补1。[Return]12.3 位段简介有时,存储1个信息不必占用1个字节,只需二进制的1个(或多个)位就够用。如果仍然使用结构类型,则造成内存空间的浪费。为此,C语言引入了位段类型。1. 位段的概念与定义 所谓位段类型,是一种特殊的结构类型,其所有成员均以二进制位为单位定义长度,并称成员为位段。例如,CPU的状态寄存器,按位段类型定义如下:struct status{ unsigned sign: 1; /*符号标志*/unsigned zero: 1; /*零标志*/unsigned carry: 1; /*进位标志*/unsigned parity: 1; /*奇偶/溢出标志*/unsigned half_carry: 1; /*半进位标志*/unsigned negative: 1; /*减标志*/} flags;显然,对CPU的状态寄存器而言,使用位段类型(仅需1个字节),比使用结构类型(需要6个字节)节省了5个字节。2.说明(1)因为位段类型是一种结构类型,所以位段类型和位段变量的定义,以及对位段(即位段类型中的成员)的引用,均与结构类型和结构变量一样。(2)对位段赋值时,要注意取置范围。一般地说,长度为n的位段,其取值范围是:0~(2n-1)。(3)使用长度为0的无名位段,可使其后续位段从下1个字节开始存储。例如,struct status{ unsigned sign: 1; /*符号标志*/unsigned zero: 1; /*零标志*/unsigned carry: 1; /*进位标志*/unsigned : 0; /*长度为0的无名位段*/unsigned parity: 1; /*奇偶/溢出标志*/unsigned half_carry: 1; /*半进位标志*/unsigned negative: 1; /*减标志*/} flags;原本6个标志位是连续存储在1个字节中的。由于加入了1个长度为0的无名位段,所以其后的3个位段,从下1个字节开始存储,一共占用2个字节。(4)1个位段必须存储在1个存储单元(通常为1字节)中,不能跨2个。如果本单元不够容纳某位段,则从下1个单元开始存储该位段。(5)可以用%d、%x、%u和%o等格式字符,以整数形式输出位段。(6)在数值表达式中引用位段时,系统自动将位段转换为整型数。[Return](共21张PPT)第1章 C语言概述本章主要介绍C语言程序的结构和书写规则。1.1 C语言的发展简史和特点1.2 C语言程序的结构与书写规则1.3 C语言的语句和关键字[Return]1.1 C语言的发展简史和特点1.C语言的诞生与发展(1)在C语言诞生以前,系统软件主要是用汇编语言编写的。由于汇编语言程序依赖于计算机硬件,其可读性和可移植性都很差;但一般的高级语言又难以实现对计算机硬件的直接操作(这正是汇编语言的优势),于是人们盼望有一种兼有汇编语言和高级语言特性的新语言。(2)C语言是贝尔实验室于70年代初研制出来的,后来又被多次改进,并出现了多种版本。80年代初,美国国家标准化协会(ANSI),根据C语言问世以来各种版本对C语言的发展和扩充,制定了ANSI C标准(1989年再次做了修订)。本书以ANSI C新标准来介绍。(3)目前,在微机上广泛使用的C语言编译系统有Microsoft Visual C++、Microsoft C、Turbo C 、Borland C等。虽然它们的基本部分都是相同的,但还是有一些差异, 所以请大家注意自己所使用的C编译系统的特点和规定(参阅相应的手册)。2.C语言的特点C语言同时具有汇编语言和高级语言的优势。(1)语言简洁、紧凑,使用方便、灵活。(2)运算符极其丰富。(3)生成的目标代码质量高,程序执行效率高。(4)可移植性好(较之汇编语言)。(5)可以直接操纵硬件。3. 在C语言中,除实现顺序、选择和循环三种基本结构等的9条控制语句外,输入输出操作均由标准库函数(不是C语言的组成部分)来实现。所以学习C语言,不仅要学习这9条控制语句和各种运算符,而且要学习并掌握常用标准库函数的使用。[Return]1.2 C语言程序的结构与书写规则1.2.1 C语言程序的总体结构一个完整的C语言程序,是由一个main( )函数(又称主函数)和若干个其它函数结合而成的,或仅由一个main( )函数构成。[案例1.1] 仅由main( )函数构成的C语言程序。/*案例代码文件名:AL1_1.C*//*功能:仅由main( )函数构成的C语言程序示例*/main( ){printf(“This is a C program.\n”);}程序运行结果:This is a C program.例 Hello,World!/* The first C Program*/#include main(){printf(“Hello,World!”);}注释编译预处理函数语句输出:Hello,World!例/* calculate the sum of a and b*/#include /* This is the main program */main(){ int a,b,sum;a=10;b=24;sum=add(a,b);printf(”sum= %d\n",sum);}/* This function calculates the sum of x and y */int add(int x,int y){ int z;z=x+y;return(z);}运行结果:sum=34函数语句预处理命令注释格式特点习惯用小写字母,大小写敏感不使用行号,无程序行概念可使用空行和空格常用锯齿形书写格式main( ){ ……………….…………………………..…………..……………….………………………….……………….………………..}main( ){int i , j , sum;sum=0;for(i=1; i<10;i++){for(j=1;j<10;j++){sum+=i*j ;}}printf(“%d\n”,sum);}优秀程序员的素质之一:使用TAB缩进{}对齐有足够的注释有合适的空行结构特点函数与主函数程序由一个或多个函数组成必须有且只能有一个主函数main()程序执行从main开始,在main中结束,其它函数通过嵌套调用得以执行。程序语句C程序由语句组成用“;”作为语句终止符注释/* */为注释,不能嵌套不产生编译代码例: /*This is the main /* of example1.1*/ */非法编译预处理命令[案例1.2] 由main( )函数和1个其它函数max( )构成的C语言程序。/*案例代码文件名:AL1_2.C*//*功能:由main( )函数和1个其它函数max( )构成的C语言程序示例*/int max(int x, int y){ return( x>y x : y ); }main( ){ int num1,num2;printf(“Input the first integer number: ”);scanf(“%d”, &num1);printf(“Input the second integer number: ”);scanf(“%d”, &num2);printf(“max=%d\n”, max(num1, num2));}程序运行情况:Input the first integer number:6←┘Input the second integer number:9←┘max=9[案例1.3] 改写[案例1.2],交换main( )函数和max( )函数的前后位置。源程序略。程序运行情况:Input the first integer number:6←┘Input the second integer number:9←┘max=91.函数是C语言程序的基本单位。main( )函数的作用,相当于其它高级语言中的主程序;其它函数的作用,相当于子程序。2.C语言程序总是从main( )函数开始执行。一个C语言程序,总是从main( )函数开始执行,而不论其在程序中的位置。当主函数执行完毕时,亦即程序执行完毕。习惯上,将主函数main( )放在最前头。1.2.2 函数的一般结构任何函数(包括主函数main( ))都是由函数说明和函数体两部分组成。其一般结构如下:[函数类型] 函数名(函数参数表) 函数说明部分{说明语句部分;执行语句部分; 函数体部分}1. 使用的语法符号约定[...]──方括号表示可选(即可以指定,也可以缺省)……──省略号表示前面的项可以重复| ──多(含2)中选12.函数说明由函数类型(可缺省)、函数名和函数参数表三部分组成,其中函数参数表的格式为:数据类型 形参[, 数据类型 形参2……]例如,[案例1.2]中的函数max( ),其函数说明各部分如图1-1所示。函数类型 函数名 函数参数表↓ ↓ ↓int max ( int x , int y )图1-1 函数说明部分结构图注意:在旧标准中,函数可以缺省参数表。3.函数体在函数说明部分的下面、大括号(必须配对使用)内的部分。函数体一般由说明语句和可执行语句两部分构成:(1)说明语句部分说明语句部分由变量定义、自定义类型定义、自定义函数说明、外部变量说明等组成。(2)可执行语句一般由若干条可执行语句构成。图1-2是[案例1.2]的main( )函数体的示意图。/*主函数main( )*/main( ){int num1,num2; 变量定义部分printf(“Input the first integer number: ”);scanf(“%d”, &num1);printf(“Input the second integer number: ”); 可执行语句部分 函数体scanf(“%d”, &num2);printf(“max=%d\n”, max(num1, num2));}图1-2 函数体结构示意图4.说明(1)函数体中的变量定义语句,必须在所有可执行语句之前。下面程序中变量定义语句“int max;”的位置是非法的:main( ){int x,y; /*变量定义语句:定义2个整型变量x、y*/x=3; /*可执行的赋值语句:将3赋值给变量x*/y=6; /*可执行的赋值语句:将6赋值给变量y*/int max; /*变量定义语句:出现在可执行的赋值语句“x=3;”和“ y=6;”之后,非法!*/max=x>y x:y;printf(“max=%d\n”,max);}解决办法很简单,请读者自己思考。(2)如果不需要,也可以缺省变量定义语句。1.2.3 源程序书写格式1.所有语句都必须以分号“;”结束,函数的最后一个语句也不例外。2.程序行的书写格式自由,既允许1行内写几条语句,也允许1条语句分写在几行上。例如,[案例1.2]的主函数main( ),也可改写成如下所示的格式:……main( ){ int num1,num2;printf(“Input the first integer number: ”); scanf(“%d”, &num1);printf(“Input the second integer number: ”); scanf(“%d”, &num2);printf(“max=%d\n”, max(num1, num2));}如果某条语句很长,一般需要将其分写在几行上。3.允许使用注释。C语言的注释格式为: /* …… */例如,在[案例1.1]和[案例1.2]中,以及本节其它部分给出的源程序中,凡是用“/*”和“*/”括起来的文字,都是注释。(1) “/*”和“*/” 必须成对使用,且“/”和“*”、以及“*”和“/”之间不能有空格,否则都出错。技巧:为避免遗漏必须配对使用的符号,例如注释符号、函数体的起止标识符(花括号)、圆括号等等,在输入时,可连续输入这些起止标识符,然后再在其中进行插入来完成内容的编辑。在起止标识符嵌套时,以及相距较远时,这样做更有必要。(2)注释的位置,可以单占1行,也可以跟在语句的后面。(3)如果1行写不下,可另起1行继续写。(4)注释中允许使用汉字。在非中文操作系统下,看到的是一串乱码,但不影响程序运行。[Return]1.3 C语言的语句和关键字1.3.1 C语言的语句与其它高级语言一样,C语言也是利用函数体中的可执行语句,向计算机系统发出操作命令。按照语句功能或构成的不同,可将C语言的语句分为五类。1.控制语句控制语句完成一定的控制功能。C语言只有9条控制语句,又可细分为三种:( 1)选择结构控制语句if()~else~, switch()~(2)循环结构控制语句do~while(), for()~, while()~, break, continue(3)其它控制语句goto, return2. 函数调用语句函数调用语句由一次函数调用加一个分号(语句结束标志)构成。例如,printf("This is a C function statement.");3. 表达式语句表达式语句由表达式后加一个分号构成。最典型的表达式语句是,在赋值表达式后加一个分号构成的赋值语句。例如,“num=5 ”是一个赋值表达式,而“num=5;”却是一个赋值语句。4. 空语句空语句仅由一个分号构成。显然,空语句什么操作也不执行。例如,下面就是一个空语句:;5. 复合语句复合语句是由大括号括起来的一组(也可以是1条)语句构成。例如:main(){ ……{……} /*复合语句。注意:右括号后不需要分号。*/……}复合语句的性质:(1)在语法上和单一语句相同,即单一语句可以出现的地方,也可以使用复合语句。(2)复合语句可以嵌套,即复合语句中也可出现复合语句。1.3.2 关键字C语言的关键字共有32个,根据关键字的作用,可分其为数据类型关键字、控制语句关键字、存储类型关键字和其它关键字四类。(1)数据类型关键字(12个):char, double, enum, float, int, long, short, signed, struct, union, unsigned, void(2)控制语句关键字(12个):break, case, continue, default, do, else, for, goto, if, return, switch, while(3)存储类型关键字(4个):auto, extern, register, static(4)其它关键字(4个):const, sizeof, typedef, volatile[Return](共32张PPT)第5章 选择结构程序设计要设计选择结构程序,要考虑两个方面的问题:一是在C语言中如何来表示条件,二是在C语言中实现选择结构用什么语句。在C语言中表示条件,一般用关系表达式或逻辑表达式,实现选择结构用if语句或switch语句。5.1 关系运算及其表达式5.2 逻辑运算及其表达式5.3 if语句5.4 switch语句5.5 选择结构程序举例良好的源程序书写风格──注释[Return]5.1 关系运算及其表达式所谓“关系运算”实际上就是“比较运算”,即将两个数据进行比较,判定两个数据是否符合给定的关系。例如,“a > b”中的“>”表示一个大于关系运算。如果a的值是5,b的值是3,则大于关系运算“>”的结果为“真”,即条件成立;如果a的值是2,b的值是3,则大于关系运算“>”的结果为“假”,即条件不成立。5.1.1 关系运算符及其优先次序1.关系运算符C语言提供6种关系运算符:<(小于), <=(小于或等于), >(大于),>=(大于或等于), ==(等于), !=(不等于)注意:在C语言中,“等于”关系运算符是双等号“= =”,而不是单等号“= ”(赋值运算符)。2.优先级(1)在关系运算符中,前4个优先级相同,后2个也相同,且前4个高于后2个。(2)与其它种类运算符的优先级关系关系运算符的优先级,低于算术运算符,但高于赋值运算符。5.1.2 关系表达式1.关系表达式的概念所谓关系表达式是指,用关系运算符将两个表达式连接起来,进行关系运算的式子。例如,下面的关系表达式都是合法的:a>b,a+b>c-d,(a=3)<=(b=5),'a'>='b',(a>b)= =(b>c)2.关系表达式的值——逻辑值(非“真”即“假”)。由于C语言没有逻辑型数据,所以用整数“1”表示“逻辑真”,用整数“0”表示“逻辑假”。例如,假设num1=3,num2=4,num3=5,则:(1)num1>num2的值=0。(2)(num1>num2)!=num3的值=1。(3)num1思考题:任意改变num1或num2的值,会影响整个表达式的值吗?为什么?(4)(num1再次强调:C语言用整数“1”表示“逻辑真”,用整数“0”表示“逻辑假”。所以,关系表达式的值,还可以参与其它种类的运算,例如算术运算、逻辑运算等。[Return]5.2 逻辑运算及其表达式关系表达式只能描述单一条件,例如“x>=0”。如果需要描述“x>=0”、同时“x<10”,就要借助于逻辑表达式了。5.2.1 逻辑运算及其优先次序1.逻辑运算符及其运算规则(1)C语言提供三种逻辑运算符:&& 逻辑与(相当于“同时”)|| 逻辑或(相当于“或者”)! 逻辑非(相当于“否定”)例如,下面的表达式都是逻辑表达式:(x>=0) && (x<10) ,(x<1) || (x>5) ,! (x= =0),(year%4==0)&&(year%100!=0)||(year%400==0)(2)运算规则1)&&:当且仅当两个运算量的值都为“真”时,运算结果为“真”,否则为“假”。2) || :当且仅当两个运算量的值都为“假”时,运算结果为“假”,否则为“真”。3) ! :当运算量的值为“真”时,运算结果为“假”;当运算量的值为“假”时,运算结果为“真”。例如,假定x=5,则(x>=0) && (x<10)的值为“真”,(x<-1) || (x>5)的值为“假”。2.逻辑运算符的运算优先级(1)逻辑非的优先级最高,逻辑与次之,逻辑或最低,即:!(非) → &&(与) → ||(或)(2)与其它种类运算符的优先关系!→ 算术运算 → 关系运算 → &&→ || → 赋值运算5.2.2 逻辑表达式1.逻辑表达式的概念所谓逻辑表达式是指,用逻辑运算符将1个或多个表达式连接起来,进行逻辑运算的式子。在C语言中,用逻辑表达式表示多个条件的组合。例如,(year%4==0)&&(year%100!=0)||(year%400==0)就是一个判断一个年份是否是闰年的逻辑表达式。逻辑表达式的值也是一个逻辑值(非“真”即“假”)。2.逻辑量的真假判定──0和非0C语言用整数“1”表示“逻辑真”、用“0”表示“逻辑假”。但在判断一个数据的“真”或“假”时,却以0和非0为根据:如果为0,则判定为“逻辑假”;如果为非0,则判定为“逻辑真”。例如,假设num=12,则: !num的值=0 ,num>=1 && num<=31的值=1 ,num || num>31的值=1。3.说明(1)逻辑运算符两侧的操作数,除可以是0和非0的整数外,也可以是其它任何类型的数据,如实型、字符型等。(2)在计算逻辑表达式时,只有在必须执行下一个表达式才能求解时,才求解该表达式(即并不是所有的表达式都被求解)。换句话说:1)对于逻辑与运算,如果第一个操作数被判定为“假”,系统不再判定或求解第二操作数。2)对于逻辑或运算,如果第一个操作数被判定为“真”,系统不再判定或求解第二操作数。例如,假设n1、n2、n3、n4、x、y的值分别为1、2、3、4、1、1,则求解表达式“(x=n1>n2)&&(y=n3>n4)”后,x的值变为0,而y的值不变,仍等于1![Return]5.3 if语句和条件运算符5.3.1 if语句[案例4.1] 输入任意三个整数num1、num2、num3,求三个数中的最大值。 /*案例代码文件名:AL4_1.C。*/ /*功能:说明if 语句的格式。*/ main() {int num1,num2,num3,max; printf("Please input three numbers:"); scanf("%d,%d,%d",&num1,&num2,&num3); if (num1>num2) max=num1; else max=num2; if (num3>max) max=num3; printf("The three numbers are:%d,%d,%d\n",num1,num2,num3); printf("max=%d\n",max); } [程序演示]程序运行情况如下: Please input three numbers:11,22,18↙The three numbers are:11,22,18max=22本案例中的第1个if语句,可优化为如下不带else子句的形式:max=num1;if(num2>max) max=num2;这种优化形式的基本思想是:首先取一个数预置为max(最大值),然后再用max依次与其余的数逐个比较,如果发现有比max大的,就用它给max重新赋值,比较完所有的数后,max中的数就是最大值。这种方法,对从3个或3个以上的数中找最大值的处理,非常有效。请读者仔细体会。[案例4.2]输入任意三个数num1、num2、num3,按从小到大的顺序排序输出。/*案例代码文件名:AL4_2.C。*/main(){int num1,num2,num3,temp;printf("Please input three numbers:");scanf("%d,%d,%d",&num1,&num2,&num3);if (num1>num2) {temp=num1;num1=num2;num2=temp;}if (num2>num3) {temp=num2;num2=num3;num3=temp;}if (num1>num2) {temp=num1;num1=num2;num2=temp;}printf("Three numbers after sorted: %d,%d,%d\n",num1,num2,num3);} [程序演示]程序运行情况如下:Please input three numbers:11,22,18↙Three numbers after sorted: 11,18,221.if语句的一般格式if(表达式){语句组1;}[else{语句组2;} ](1)if语句中的“表达式”必须用“(”和“)”括起来。(2)else子句(可选)是if语句的一部分,必须与if配对使用,不能单独使用。(3)当if和else下面的语句组,仅由一条语句构成时,也可不使用复合语句形式(即去掉花括号)。2.if语句的执行过程(1)缺省else子句时当“表达式”的值不等于0(即判定为“逻辑真”)时,则执行语句组1,否则直接转向执行下一条。如图4-1(a)所示。(2)指定else子句时当“表达式”的值不等于0(即判定为“逻辑真”)时,则执行语句组1,然后转向下一条语句;否则,执行语句组2。如图4-1(b)所示。3.if语句的嵌套与嵌套匹配原则if语句允许嵌套。所谓if语句的嵌套是指,在“语句组1”或(和)“语句组2”中,又包含有if语句的情况。if语句嵌套时,else子句与if的匹配原则:与在它上面、距它最近、且尚未匹配的if配对。为明确匹配关系,避免匹配错误,强烈建议:将内嵌的if语句,一律用花括号括起来。[案例4.3] 写一程序,从键盘上输入1年份year(4位十进制数),判断其是否闰年。闰年的条件是:能被4整除、但不能被100整除,或者能被400整除。算法设计要点:(1)如果X能被Y整除,则余数为0,即如果X%Y的值等于0,则表示X能被Y整除!(2)首先将是否闰年的标志leap预置为0(非闰年),这样仅当year为闰年时将leap置为1即可。这种处理两种状态值的方法,对优化算法和提高程序可读性非常有效,请读者仔细体会。参考程序如下:/*案例代码文件名:AL4_3.C。*//*功能:说明if语句的嵌套格式和用法。*/main(){int year,leap=0; /* leap=0:预置为非闰年*/printf("Please input the year:");scanf("%d",&year);if (year % 4==0) {if (year % 100 != 0) leap=1;}else {if (year%400==0) leap=1; }if (leap) printf("%d is a leap year.\n",year);else printf("%d is not a leap year.\n",year);} [程序演示]利用逻辑运算能描述复杂条件的特点,可将上述程序优化如下:main(){int year;printf("Please input the year:");scanf("%d",&year);if ((year%4==0 && year%100!=0)||(year%400==0))printf("%d is a leap year.\n",year);elseprintf("%d is not a leap year.\n",year);}4.说明(1)if后面的“表达式”,除常见的关系表达式或逻辑表达式外,也允许是其它类型的数据,如整型、实型、字符型等。(2)if语句允许嵌套,但嵌套的层数不宜太多。在实际编程时,应适当控制嵌套层数(2~3层)。(3)“语句组1”和“语句组2”,可以只包含一个简单语句,也可以是复合语句。务必牢记:不管是简单语句,还是复合语句中的各个语句,每个语句后面的分号必不可少!例如,[案例4.1]中的:if (num1>num2) max=num1;else max=num2;语句:if行后面的赋值语句“max=num1;”分号不能省略。但不要误认为if和else是2个独立的语句,它们都属于if语句中的一部分,else是if语句的子句。5.3.2 条件运算符1.一般格式: 表达式1?表达式2:表达式3条件表达式中的“表达式1”、“表达式2”、“表达式3”的类型,可以各不相同。2.运算规则如果“表达式1”的值为非0(即逻辑真), 则运算结果等于“表达式2”的值;否则,运算结果等于“表达式3”的值。如图4-2所示。3.运算符的优先级与结合性条件运算符的优先级,高于赋值运算符,但低于关系运算符和算术运算符。其结合性为“从右到左”(即右结合性)。[例4.4] 从键盘上输入一个字符,如果它是大写字母,则把它转换成小写字母输出;否则,直接输出。/*案例文件名:AL4_4.C*/main(){ char ch;printf("Input a character: ");scanf("%c",&ch);ch=(ch>='A' && ch<='Z') (ch+32) : ch;printf("ch=%c\n",ch);}[Return]5.4 switch语句C语言提供了switch语句直接处理多分支选择。[案例4.5] 从键盘上输入一个百分制成绩score,按下列原则输出其等级:score≥90,等级为A;80≤score<90,等级为B;70≤score<80,等级为C;60≤score<70,等级为D;score<60,等级为E。 /*案例代码文件名:AL4_5.C。*/ main() {int score, grade; printf(“Input a score(0~100): ”); scanf(“%d”, &score); grade = score/10; /*将成绩整除10,转化成switch语句中的case标号*/ switch (grade){case 10:case 9: printf(“grade=A\n”); break;case 8: printf("grade=B\n"); break;case 7: printf("grade=C\n"); break;case 6: printf("grade=D\n"); break;case 5:case 4:case 3:case 2:case 1:case 0: printf(“grade=E\n”); break;default: printf(“The score is out of range!\n”);} }程序运行情况如下:Input a score(0~100): 85↙grade=B1.switch语句的一般形式switch(表达式){ case 常量表达式1:语句组;break;case 常量表达式2:语句组;break;......case 常量表达式n:语句组;break;[default:语句组;[break; ]]}2.执行过程(1)当switch后面“表达式”的值,与某个case后面的“常量表达式”的值相同时,就执行该case后面的语句(组);当执行到break语句时,跳出switch语句,转向执行switch语句的下一条。(2)如果没有任何一个case后面的“常量表达式”的值,与“表达式”的值匹配,则执行default 后面的语句(组)。然后,再执行switch语句的下一条。3.说明(1)switch后面的“表达式”,可以是int、char和枚举型中的一种。(2)每个case后面“常量表达式”的值,必须各不相同,否则会出现相互矛盾的现象(即对表达式的同一值,有两种或两种以上的执行方案)。(3)case后面的常量表达式仅起语句标号作用,并不进行条件判断。系统一旦找到入口标号,就从此标号开始执行,不再进行标号判断,所以必须加上break语句,以便结束switch语句。思考题:如果去掉[案例4.5]程序中的所有break语句,且输入的成绩为75,输出会如何?(4)各case及default子句的先后次序,不影响程序执行结果。(5)多个case子句,可共用同一语句(组)。例如,在[案例4.5]中的“case 10: ”和“case 9: ”共用语句“printf("grade=A\n"); break;”,“case 5: ”~“case 0: ”共用语句“printf("grade=E\n"); break;”。(6)用switch语句实现的多分支结构程序,完全可以用if语句或if语句的嵌套来实现。[Return]5.5 选择结构程序设计举例[案例4.6] 求一元二次方程ax2+bx+c=0的解(a≠0)。 /*案例代码文件名:AL4_6.C。*/ /*功能:求一元二次方程的解。*/ #include "math.h" main() {float a,b,c,disc,x1,x2,p,q; scanf(“%f,%f,%f”, &a, &b, &c); disc=b*b-4*a*c;if (fabs(disc)<=1e-6) /*fabs():求绝对值库函数*/ printf(“x1=x2=%7.2f\n”, -b/(2*a)); /*输出两个相等的实根*/ else { if (disc>1e-6) {x1=(-b+sqrt(disc))/(2*a); /*求出两个不相等的实根*/ x2=(-b-sqrt(disc))/(2*a); printf("x1=%7.2f,x2=%7.2f\n", x1, x2); }else {p=-b/(2*a); /*求出两个共轭复根*/ q=sqrt(fabs(disc))/(2*a); printf(“x1=%7.2f + %7.2f i\n“, p, q); /*输出两个共轭复根*/ printf(”x2=%7.2f - %7.2f i\n“, p, q); } } }说明:由于实数在计算机中存储时,经常会有一些微小误差,所以本案例判断disc是否为0的方法是:判断disc的绝对值是否小于一个很小的数(例如10-6)。思考题:如果将系数a、b、c定义成整数,能否直接判断disc是否等于0?[案例4.7] 已知某公司员工的保底薪水为500,某月所接工程的利润profit(整数)与利润提成的关系如下(计量单位:元):profit≤1000 没有提成;1000<profit≤2000 提成10%;2000<profit≤5000 提成15%;5000<profit≤10000 提成20%;10000<profit 提成25%。算法设计要点:为使用switch语句,必须将利润profit与提成的关系,转换成某些整数与提成的关系。分析本题可知,提成的变化点都是1000的整数倍(1000、2000、5000、……),如果将利润profit整除1000,则当:profit≤1000 对应0、11000<profit≤2000 对应1、22000<profit≤5000 对应2、3、4、55000<profit≤10000 对应5、6、7、8、9、1010000<profit 对应10、11、12、……为解决相邻两个区间的重叠问题,最简单的方法就是:利润profit先减1(最小增量),然后再整除1000即可:profit≤1000 对应01000<profit≤2000 对应12000<profit≤5000 对应2、3、45000<profit≤10000 对应5、6、7、8、910000<profit 对应10、11、12、……/*案例代码文件名:AL4_7.C。*/main(){long profit;int grade;float salary=500;printf("Input profit: ");scanf("%ld", &profit);grade= (profit – 1) / 1000; /*将利润-1、再整除1000,转化成 switch语句中的case标号*/switch(grade){ case 0: break; /*profit≤1000 */case 1: salary += profit*0.1; break; /*1000<profit≤2000 */case 2:case 3:case 4: salary += profit*0.15; break; /*2000<profit≤5000 */case 5:case 6:case 7:case 8:case 9: salary += profit*0.2; break; /*5000<profit≤10000 */default: salary += profit*0.25; /*10000<profit */}printf("salary=%.2f\n", salary);} [Return]良好的源程序书写风格──注释必要的注释,可有效地提高程序的可读性,从而提高程序的可维护性。在C语言源程序中,注释可分为三种情况:(1)在函数体内对语句的注释;(2)在函数之前对函数的注释;(3)在源程序文件开始处,对整个程序的总体说明。函数体内的语句,是由顺序结构、选择结构和循环结构等三种基本结构构成的。在什么地方加以注释的原则是:如果不加注释,理解起来就会有困难,或者虽无困难、但浪费时间。 (1)顺序结构在每个顺序程序段(由若干条语句构成)之前,用注释说明其功能。除很复杂的处理外,一般没有必要每条语句都加以注释。(2)选择结构在C语言中,选择结构是由if语句和switch语句来实现的。一般地说,要在前面说明其作用,在每个分支条件语句行的后面,说明该分支的含义,如下所示:1)if语句 /*……(说明功能)*/if(条件表达式) /*条件成立时的含义*/ {……} else /*入口条件含义*/ {……}2)switch语句 /*……(说明功能) */switch(表达式){ case 常量表达式1: /*该入口值的含义*/ 语句组; …… case 常量表达式n: /*该入口值的含义*/ 语句组; default: /*该入口值的含义*/ 语句组; }如果条件成立时(或入口值)的含义,已经很明确了,也可不再加以注释。[Return](共32张PPT)第3章 数据类型、运算符与表达式3.1 程序设计概述3.2 C语言的数据类型 3.3 常量和变量 3.4 整型数据 3.5 实型数据 3.6 字符型数据 3.7 算术运算与算术表达式 3.8 赋值运算与赋值表达式 3.9 C语言特有的运算和运算符[Return]3.1 程序设计概述一个程序应包括对数据的描述和对数据处理的描述。 1.对数据的描述,即数据结构。数据结构是计算机学科的核心课程之一,有许多专门著作论述,本课程就不再赘述。 在C语言中,系统提供的数据结构,是以数据类型的形式出现的。 2.对数据处理的描述,即计算机算法。算法是为解决一个问题而采取的方法和步骤,是程序的灵魂。为此,著名计算机科学家沃思(Nikiklaus Wirth)提出一个公式: 数据结构 + 算法 = 程序 实际上,一个程序除了数据结构和算法外,还必须使用一种计算机语言,并采用结构化方法来表示。[Return]3.2 C语言的数据类型类型是计算机科学的核心概念之一。C基本数据类型包括字符/整数/实数类型等。同一类型的所有数据对象性质相同,采用统一书写形式,同样编码方式,能做同样操作。程序里的数据都属于特定类型。硬件也把数据分成类型:整数、浮点数类型等,对每个类型有一组指令。各种基本类型用定长二进制编码表示。这确定了该类型数据的可能表示范围。例:一个整数类型中的整数是数学中整数的一个有穷子集;只包含有穷个不同整数值,有最小/最大整数;超出该子集的整数无法表示。本章介绍几个基本类型(完整介绍见后)。字面量(常量):程序里直接写出的数据。整数类型的字面量(简称整数):写在程序里的整数类型的数据。其他情况类似。类型名:基本类型的类型名由一个或几个关键字组成。C语言提供的数据结构,是以数据类型形式出现的。具体分类如下:1.基本类型分为整型、实型(又称浮点型)、字符型和枚举型四种。2.构造类型分为数组类型、结构类型和共用类型三种。3.指针类型。在第10章中介绍。4.空类型C语言中的数据,有常量和变量之分,它们分别属于上述这些类型。本章将介绍基本类型中的整型、实型和字符型三种数据。[Return]3.3 常量和变量3.3.1 常量 1.常量的概念 在程序运行过程中,其值不能被改变的量称为常量。 2.常量的分类 (1)整型常量(2)实型常量(3)字符常量(4)符号常量常量的类型,可通过书写形式来判别。3.3.2 变量1.变量的概念在程序运行过程中,其值可以被改变的量称为变量。2.变量的两个要素(1)变量名。每个变量都必须有一个名字──变量名,变量命名遵循标识符命名规则。(2)变量值。在程序运行过程中,变量值存储在内存中。在程序中,通过变量名来引用变量的值。3.标识符命名规则(1)有效字符:只能由字母、数字和下划线组成,且以字母或下划线开头。(2)有效长度:随系统而异,但至少前8个字符有效。如果超长,则超长部分被舍弃。例如,由于student_name和student_number的前8个字符相同,有的系统认为这两个变量,是一回事而不加区别。在TC V2.0中,变量名(标识符)的有效长度为1~32个字符,缺省值为32。(3)C语言的关键字不能用作变量名。注意:C语言对英文字母的大小敏感,即同一字母的大小写,被认为是两个不同的字符。习惯上,变量名和函数名中的英文字母用小写,以增加可读性。思考题:在C语言中,变量名total与变量名TOTAL、ToTaL、tOtAl等是同一个变量吗?标识符命名的良好习惯──见名知意:所谓“见名知意”是指,通过变量名就知道变量值的含义。通常应选择能表示数据含义的英文单词(或缩写)作变量名,或汉语拼音字头作变量名。例如,name/xm(姓名)、sex/xb(性别)、age/nl(年龄)、salary/gz(工资)。4.变量的定义与初始化在C语言中,要求对所有用到的变量,必须先定义、后使用;且称在定义变量的同时进行赋初值的操作为变量初始化。(1)变量定义的一般格式[存储类型] 数据类型 变量名[, 变量名2……];例如,float radius, length, area;(2)变量初始化的一般格式[存储类型] 数据类型 变量名[=初值][, 变量名2[=初值2]……];例如,float radius=2.5, length, area;[Return]3.4 整型数据3.4.1 整型变量 1.分类 根据占用内存字节数的不同,整型变量又分为4类:(1)基本整型(类型关键字为int)。(2)短整型(类型关键字为short [int])。(3)长整型(类型关键字为long [int])。(4)无符号整型。无符号型又分为无符号基本整型(unsigned [int])、无符号短整型(unsigned short)和无符号长整型(unsigned long)三种,只能用来存储无符号整数。2.占用内存字节数与值域 上述各类型整型变量占用的内存字节数,随系统而异。在16位操作系统中,一般用2字节表示一个int型变量,且long型(4字节)≥int型(2字节)≥short型(2字节)。显然,不同类型的整型变量,其值域不同。占用内存字节数为n的(有符号)整型变量,其值域为:-2n*8-1~(2n*8-1-1);无符号整型变量的值域为:0~(2n*8-1)。 例如,PC机中的一个int型变量,其值域为-22*8-1~(22*8-1-1),即-32768~32767;一个unsigned型变量的值域为:0~(22*8-1),即0~65535。 3.4.2 整型常量1.三种表示形式整型常量即整常数,在C语言中可用三种形式表示:(1)十进制。例如10、36。(2)八进制(以数字0开头)。例如012。(3)十六进制(以数字0+小写字母x开头)。例如0x36。2.分类(1)基本整型。在16位机中,用2字节存储,其数据范围与int型变量一样。(2)长整型(在数值后面加“L(l)”)。对超出基本整型值域的整型常量,可使用长整型常量表示,其取值范围可达-231-(231-1)。例如,123l、315L等。3.类型匹配规则类型匹配规则为:一个整型常量,可以赋给能容纳下其值的整型变量。例如,其值在-215--(215-1)的整型常量,可以赋给int型变量和long int型变量;其值在-231--(231-1)的整型常量,就只能赋给long int型变量。注意:常量无unsigned型。但一个非负整型常量,只要它的值不超过相应变量的值域(即取值范围),也可以赋给unsigned型变量。[Return]3.5 实型数据3.5.1 实型变量 C语言的实型变量,分为两种: (1)单精度型。类型关键字为float,一般占4字节(32位)、提供7位有效数字。 (2)双精度型。类型关键字为double,一般占8个字节、提供15~16位有效数字。3.5.2 实型常量 1.表示形式 实型常量即实数,在C语言中又称浮点数,其值有两种表达形式: (1)十进制形式。例如3.14、9.8。(2)指数形式:<尾数>E(e)<整型指数>。例如3.0E+5等。 2.关于类型 实型常量不分float型和double型。一个实型常量,可以赋给一个实型变量(float型或double型)。[Return]3.6 字符型数据3.6.1 字符常量1.字符常量的定义用一对单引号括起来的单个字符,称为字符常量。例如,‘A’、‘1’、‘+’等。2.转义字符C语言还允许使用一种特殊形式的字符常量,就是以反斜杠“\ ”开头的转义字符。 注意:如果反斜杠或单引号本身作为字符常量,必须使用转义字符:‘\\’、‘\’‘。[案例2.1] 用转义字符输出可打印字符和不可打印字符。 /*案例代码文件名:AL2_1.C*/ /*程序功能:用转义字符输出可打印字符和不可打印字符*/main() { printf(“\x4F\x4B\x21\n”); /* 等价于printf(“OK!\n”); */ printf(“\x15 \xAB\n”); }程序运行结果如下: OK! § 3.6.2 字符变量字符变量的类型关键字为char,一般占用1字节内存单元。1.变量值的存储 字符变量用来存储字符常量。将一个字符常量存储到一个字符变量中,实际上是将该字符的ASCII码值(无符号整数)存储到内存单元中。例如,char ch1, ch2; /*定义两个字符变量:ch1,ch2*/ch1=’a’; ch2=’b’; /*给字符变量赋值*/2.特性字符数据在内存中存储的是字符的ASCII码── 一个无符号整数,其形式与整数的存储形式一样(如图2-4所示),所以C语言允许字符型数据与整型数据之间通用。(1)一个字符型数据,既可以字符形式输出,也可以整数形式输出。 [案例2.2] 字符变量的字符形式输出和整数形式输出。 /*案例代码文件名:AL2_2.C*/ /*程序功能:用字符形式和整数形式输出字符变量*/ main() { char ch1,ch2; ch1='a'; ch2='b';printf(“ch1=%c,ch2=%c\n”,ch1,ch2); printf(“ch1=%d,ch2=%d\n”,ch1,ch2); } 程序运行结果: ch1=a,ch2=b ch1=97,ch2=98(2)允许对字符数据进行算术运算,此时就是对它们的ASCII码值进行算术运算。[案例2.3] 字符数据的算术运算。 /*案例代码文件名:AL2_3.C*/ /*程序功能:字符数据的算术运算*/ main() { char ch1,ch2; ch1=‘a’; ch2=‘B’; /*字母的大小写转换*/ printf(“ch1=%c,ch2=%c\n”,ch1-32,ch2+32); /*用字符形式输出一个大于256的数值*/ printf("ch1+200=%d\n", ch1+200); printf("ch1+200=%c\n", ch1+200); printf("ch1+256=%d\n", ch1+256); printf("ch1+256=%c\n", ch1+256); }程序运行结果:ch1=A,ch2=bch1+200=297ch1+200=)ch1+256=353ch1+256=a [程序演示]思考题:用字符形式输出一个大于256的数值,会得到什么结果?3.6.3 字符串常量1.字符串常量的概念和字符串长度字符串常量是用一对双引号括起来的若干字符序列。字符串中字符的个数称为字符串长度。长度为0的字符串(即一个字符都没有的字符串)称为空串,表示为“ ” (一对紧连的双引号)。例如,“How do you do.”、“Good morning.”等,都是字符串常量,其长度分别为14和13(空格也是一个字符)。 如果反斜杠和双引号作为字符串中的有效字符,则必须使用转义字符。例如:(1)C:\msdos\v6.22 → "C:\\msdos\\v6.22" (2)I say:"Goodbye!" → "I say:\"Goodbye!\"“2.字符串的存储C语言规定:在存储字符串常量时,由系统在字符串的末尾自动加一个‘\0’作为字符串的结束标志。注意:在源程序中书写字符串常量时,不必加结束字符‘\0’,否则画蛇添足。 如果有一个字符串为“CHINA”,则它在内存中的实际存储如下所示:最后一个字符'\0'是系统自动加上的,它占用6字节而非5字节内存空间。C H I N A \0综上所述,字符常量'A'与字符串常量"A"是两回事:(1)定界符不同:字符常量使用单引号,而字符串常量使用双引号;(2)长度不同:字符常量的长度固定为1,而字符串常量的长度,可以是0,也可以是某个整数;(3)存储要求不同:字符常量存储的是字符的ASCII码值,而字符串常量,除了要存储有效的字符外,还要存储一个结束标志’\0’。[Return]3.7 算术运算与算术表达式在C语言中,除控制语句和输入输出函数外,其它所有基本操作都作为运算符处理。1.五种基本算术运算符 +、-(减法/取负)、*、/、%(求余数)(1)关于除法运算/C语言规定:两个整数相除,其商为整数,小数部分被舍弃。例如,5 / 2 = 2。 (2)关于求余数运算%要求两侧的操作数均为整型数据,否则出错。 2.表达式和算术表达式(1)表达式的概念用运算符和括号将运算对象(常量、变量和函数等)连接起来的、符合C语言语法规则的式子,称为表达式。单个常量、变量或函数,可以看作是表达式的一种特例。将单个常量、变量或函数构成的表达式称为简单表达式,其它表达式称之为复杂表达式。(2)算术表达式的概念表达式中的运算符都是算术运算符。例如,3 + 6 * 9、(x + y) / 2 - 1等,都是算术表达式。良好的源程序书写习惯:在表达式中,在双目运算符的左右两侧各加一个空格,可增强程序的可读性。请比较表达式“ (x + y) / 2 – 1”与“(x+y)/2–1”,您认为哪个的可读性更好一些?3.运算符的优先级与结合性(1)C语言规定了运算符的优先级和结合性。所谓结合性是指,当一个操作数两侧的运算符具有相同的优先级时,该操作数是先与左边的运算符结合,还是先与右边的运算符结合。自左至右的结合方向,称为左结合性。反之,称为右结合性。结合性是C语言的独有概念。除单目运算符、赋值运算符和条件运算符是右结合性外,其它运算符都是左结合性。(2)表达式求值1)按运算符的优先级高低次序执行。例如,先乘除后加减。2)如果在一个运算对象(或称操作数)两侧的运算符的优先级相同,则按C语言规定的结合方向(结合性)进行。例如,算术运算符的结合方向是“自左至右”,即:在执行“a – b + c”时,变量b先与减号结合,执行“a - b”;然后再执行加c的运算。4.数据类型转换(1) 在C语言中,整型、实型和字符型数据间可以混合运算(因为字符数据与整型数据可以通用)。如果一个运算符两侧的操作数的数据类型不同,则系统按“先转换、后运算”的原则,首先将数据自动转换成同一类型,然后在同一类型数据间进行运算。转换规则如图2-5所示。1)横向向左的箭头,表示必须的转换。char和short 型必须转换成 int 型,float型必须转换成double型。2)纵向向上的箭头,表示不同类型的转换方向。例如,int型与double型数据进行混合运算,则先将int型数据转换成double型,然后在两个同类型的数据间进行运算,结果为double型。注意:箭头方向只表示数据类型由低向高转换,不要理解为int型先转换成unsigned型,再转换成long型,最后转换成double型。(2)除自动转换外,C语言也允许强制转换。数据类型强制转换的一般格式为:(要转换成的数据类型)(被转换的表达式)当被转换的表达式是一个简单表达式时,外面的一对圆括号可以缺省。例如,(double)a (等价于(double)(a)) /*将变量a的值转换成double型*/(int)(x + y) /*将x+y的结果转换成int型*/(float)5 / 2(等价于(float)(5)/2) /*将5转换成实型,再除以2(=2.5)*/(float)(5 / 2) /*将5整除2的结果(2)转换成实型(2.0)*/注意:强制转换类型得到的是一个所需类型的中间量,原表达式类型并不发生变化。例如,(double)a 只是将变量a的值转换成一个double型的中间量,其数据类型并未转换成double型。[Return]3.8 赋值运算与赋值表达式1.赋值运算赋值符号“=”就是赋值运算符,它的作用是将一个表达式的值赋给一个变量。赋值运算符的一般形式为: 变量 = 赋值表达式例如,x = 5 y = (float)5 / 2如果表达式值的类型,与被赋值变量的类型不一致,但都是数值型或字符型时,系统自动地将表达式的值转换成被赋值变量的数据类型,然后再赋值给变量。思考题:假设变量num的数据类型为float,其值为2.5,则执行“num = (int)num”后,num的值等于多少?2.复合赋值运算复合赋值运算符是由赋值运算符之前再加一个双目运算符构成的。复合赋值运算的一般格式为:变量 双目运算符 = 表达式└──┬──┘复合赋值运算符它等价于:变量 = 变量 双目运算符 (表达式)。当表达式为简单表达式时,表达式外的一对圆括号才可缺省,否则可能出错。例如,x += 3 /* 等价于x=x+3 */y *= x + 6 /* 等价于y=y*(x+6),而不是y=y*x+6 */C语言规定的10种复合赋值运算符如下:+=,-=,*=,/=,%=; /*复合算术运算符(5个)*/&=,^=,|=,<<=,>>=; /*复合位运算符(5个)*/3.赋值表达式由赋值运算符或复合赋值运算符,将一个变量和一个表达式连接起来的表达式,称为赋值表达式。(1)一般格式变量 (复合)赋值运算符 表达式(2)赋值表达式的值任何一个表达式都有一个值,赋值表达式也不例外。被赋值变量的值,就是赋值表达式的值。例如,“a = 5”这个赋值表达式,变量a的值“5”就是它的值。[Return]3.9 C语言特有的运算和运算符3.9.1 自增(++)、自减(--)运算 1.作用 自增运算使单个变量的值增1,自减运算使单个变量的值减1。 2.用法与运算规则 自增、自减运算符都有两种用法: (1)前置运算──运算符放在变量之前:++变量、--变量 先使变量的值增(或减)1,然后再以变化后的值参与其它运算,即先增减、后运算。 (2)后置运算──运算符放在变量之后:变量++、变量-- 变量先参与其它运算,然后再使变量的值增(或减)1,即先运算、后增减。[案例2.4] 自增、自减运算符的用法与运算规则示例。/*案例代码文件名:AL2_4.C*//*程序功能:自增、自减运算符的用法与运算规则示例*/main(){ int x=6, y;printf("x=%d\n",x); /*输出x的初值*/y = ++x; /*前置运算*/printf("y=++x: x=%d,y=%d\n",x,y);y = x--; /*后置运算*/printf("y=x--: x=%d,y=%d\n",x,y);}程序运行结果:x=6y=++x: x=7,y=7y=x--: x=6,y=7 [程序演示]思考题:如果将案例“y=++x;”语句中的前置运算改为后置(y=x++;),“y=x--;”语句中的后置运算改为前置(y=--x;),程序运行结果会如何?3.说明(1)自增、自减运算,常用于循环语句中,使循环控制变量加(或减)1,以及指针变量中,使指针指向下(或上)一个地址。(2)自增、自减运算符,不能用于常量和表达式。例如,5++、--(a+b)等都是非法的。(3)在表达式中,连续使同一变量进行自增或自减运算时,很容易出错,所以最好避免这种用法。3.8.2 逗号运算(,)及其表达式C语言提供一种用逗号运算符“,”连接起来的式子,称为逗号表达式。逗号运算符又称顺序求值运算符。1.一般形式表达式1, 表达式2, ……, 表达式n2.求解过程自左至右,依次计算各表达式的值,“表达式n” 的值即为整个逗号表达式的值。例如,逗号表达式“a = 3 * 5, a * 4”的值=60:先求解a = 3 * 5,得a=15;再求a * 4 = 60,所以逗号表达式的值=60。又例如,逗号表达式“(a = 3 * 5, a * 4), a + 5”的值=20:先求解a = 3 * 5,得a=15;再求a * 4=60;最后求解a + 5=20,所以逗号表达式的值=20。注意:并不是任何地方出现的逗号,都是逗号运算符。很多情况下,逗号仅用作分隔符。[Return](共56张PPT)第8章 函数8.1 概述模块化程序设计基本思想:将一个大的程序按功能分割成一些小模块,特点:各模块相对独立、功能单一、结构清晰、接口简单控制了程序设计的复杂性提高元件的可靠性缩短开发周期避免程序开发的重复劳动易于维护和功能扩充开发方法: 自上向下,逐步分解,分而治之C是模块化程序设计语言C程序结构C是函数式语言必须有且只能有一个名为main的主函数C程序的执行总是从main函数开始,在main中结束函数不能嵌套定义,可以嵌套调用函数分类从用户角度标准函数(库函数):由系统提供用户自定义函数从函数形式无参函数有参函数使用库函数应注意:1、函数功能2、函数参数的数目和顺序,及各参数意义和类型3、函数返回值意义和类型4、需要使用的包含文件8.2 函数的定义一般格式合法标识符函数返回值类型缺省int型无返回值void函数体函数类型 函数名(形参类型说明表){说明部分语句部分}现代风格:例 有参函数(现代风格)int max(int x,int y){ int z;z=x>y x:y;return(z);}例 有参函数(现代风格)int max(int x, y){ int z;z=x>y x:y;return(z);}例 空函数dummy( ){ }函数体为空例 无参函数printstar( ){ printf(“**********\n”); }或printstar(void ){ printf(“**********\n”); }函数类型 函数名(形参表)形参类型说明{说明部分语句部分}传统风格:例 有参函数(传统风格)int max(x,y)int x,y;{ int z;z=x>y x:y;return(z);}8.3 函数的返回值返回语句形式: return(表达式);或 return 表达式;或 return;功能:使程序控制从被调用函数返回到调用函数中,同时把返值带给调用函数说明:函数中可有多个return语句若无return语句,遇}时,自动返回调用函数若函数类型与return语句中表达式值的类型不一致,按前者为准,自动转换------函数调用转换void型函数例 无返回值函数void swap(int x,int y ){ int temp;temp=x;x=y;y=temp;}printstar(){ printf("**********");}main(){ int a;a=printstar();printf("%d",a);}例 函数带回不确定值输出:10void printstar(){ printf("**********");}main(){ int a;a=printstar();printf("%d",a);}编译错误!例 函数返回值类型转换main(){ float a,b;int c;scanf("%f,%f",&a,&b);c=max(a,b);printf("Max is %d\n",c);}max(float x, float y){ float z;z=x>y x:y;return(z);}8.4 函数的调用调用形式函数名(实参表);说明:实参与形参个数相等,类型一致,按顺序一一对应实参表求值顺序,因系统而定(Turbo C 自右向左) ( P.150)main(){ int i=2,p;p=f(i,++i);printf("%d",p);}int f(int a, int b){ int c;if(a>b) c=1;else if(a==b) c=0;else c=-1;return(c);}例 参数求值顺序 ( P.150)main(){ int i=2,p;p=f(i, i++);printf("%d",p);}int f(int a, int b){ int c;if(a>b) c=1;else if(a==b) c=0;else c=-1;return(c);}运行结果:0运行结果:1调用方式函数语句:例 printstar();printf(“Hello,World!\n”);函数表达式:例 m=max(a,b)*2;函数参数:例 printf(“%d”,max(a,b));m=max(a,max(b,c));函数说明对被调用函数要求:必须是已存在的函数库函数: #include <*.h>用户自定义函数: 函数类型说明函数说明一般形式: 函数类型 函数名(形参类型 [形参名],….. );或 函数类型 函数名();作用:告诉编译系统函数类型、参数个数及类型,以便检验函数定义与函数说明不同函数说明位置:程序的数据说明部分(函数内或外)下列情况下,可不作函数说明若函数返值是char或int型,系统自动按int型处理被调用函数定义出现在主调函数之前有些系统(如Borland C++)要求函数说明指出函数返值类型和形参类型,并且对void 和 int 型函数也要进行函数说明例 函数说明举例main(){ float a,b;int c;scanf("%f,%f",&a,&b);c=max(a,b);printf("Max is %d\n",c);}max(float x, float y){ float z;z=x>y x:y;return(z);}int型函数可不作函数说明(Borland C++不行)/*ch7_5.c*/float add(float x, float y){ float z;z=x+y;return(z);}main(){ float a,b,c;scanf("%f,%f",&a,&b);c=add(a,b);printf("sum is %f",c);}被调函数出现在主调函数之前,不必函数说明/*ch7_5.c*/main(){ float add(float,float); /*function declaration*/float a,b,c;scanf("%f,%f",&a,&b);c=add(a,b);printf("sum is %f",c);}float add(float x, float y){ float z;z=x+y;return(z);}float add();8.5 函数参数及其传递方式形参与实参形式参数:定义函数时函数名后面括号中的变量名实际参数:调用函数时函数名后面括号中的表达式c=max(a,b);(main 函数)(max 函数)max(int x, int y){ int z;z=x>y x:y;return(z);}例 比较两个数并输出大者main(){ int a,b,c;scanf("%d,%d",&a,&b);c=max(a,b);printf("Max is %d",c);}max(int x, int y){ int z;z=x>y x:y;return(z);}形参实参说明:实参必须有确定的值形参必须指定类型形参与实参类型一致,个数相同若形参与实参类型不一致,自动按形参类型转换———函数调用转换形参在函数被调用前不占内存;函数调用时为形参分配内存;调用结束,内存释放8.5 函数参数及其传递方式形参与实参形式参数:定义函数时函数名后面括号中的变量名实际参数:调用函数时函数名后面括号中的表达式例 计算x的立方#include float cube(float x){ return(x*x*x);}main(){ float a, product;printf("Please input value of a:");scanf("%f",&a);product=cube(a);printf(”Cube of %.4f is %.4f\n",a,product);}xaproduct××××1.21.21.728参数传递方式值传递方式方式:函数调用时,为形参分配单元,并将实参的值复制到形参中;调用结束,形参单元被释放,实参单元仍保留并维持原值特点:形参与实参占用不同的内存单元单向传递711x:y:调用前:调用结束:711x:y:例 交换两个数/*ch7_2.c*/#include main(){ int x=7,y=11;printf("x=%d,\ty=%d\n",x,y);printf("swapped:\n");swap(x,y);printf("x=%d,\ty=%d\n",x,y);}swap(int a,int b){ int temp;temp=a; a=b; b=temp;}调用:711a:b:711x:y:swap:711x:y:117a:b:temp地址传递方式:函数调用时,将数据的存储地址作为参数传递给形参特点:形参与实参占用同样的存储单元“双向”传递实参和形参必须是地址常量或变量/*ch9_3.c*/swap(p1,p2)int *p1,*p2;{ int p;p=*p1;*p1=*p2;*p2=p;}main(){ int a,b;scanf("%d,%d",&a,&b);printf(“a=%d,b=%d\n”,a,b);printf(“swapped:\n”);swap(&a,&b);printf(”a=%d,b=%d\n",a,b);}例 交换两个数a59b调前:a59b调swap:p1&a&bp2a95b交换:p1&a&bp2a95b返回:#include long sum(int a, int b);long factorial(int n);main(){ int n1,n2;long a;scanf("%d,%d",&n1,&n2);a=sum(n1,n2);printf("a=%1d",a);}long sum(int a,int b){long c1,c2;c1=factorial(a);c2=factorial(b);return(c1+c2);}long factorial(int n){ long rtn=1;int i;for(i=1;i<=n;i++)rtn*=i;return(rtn);}long sum(int a, int b);long factorial(int n);文件包含编译预处理命令函数类型说明函数定义函数调用函数调用函数返回值形参实参8.6 函数的嵌套与递归调用嵌套调用C规定:函数定义不可嵌套,但可以嵌套调用函数main( )调用函数a结束a函数b函数调用函数b 例 求三个数中最大数和最小数的差值#include int dif(int x,int y,int z);int max(int x,int y,int z);int min(int x,int y,int z);void main(){ int a,b,c,d;scanf("%d%d%d",&a,&b,&c);d=dif(a,b,c);printf("Max-Min=%d\n",d);}int dif(int x,int y,int z){ return max(x,y,z)-min(x,y,z); }int max(int x,int y,int z){ int r;r=x>y x:y;return(r>z r:z);}int min(int x,int y,int z){ int r;r=xreturn(r}main( )调用函数dif输出结束dif函数max函数调用函数max调用函数minmin函数例 用弦截法求方程根 ( P.155 )xyf(x)0x1x2xf(x1)f(x2)求f(x1)与f(x2)连线与x轴的交点x输入x1,x2,求f(x1),f(x2)直到f(x1)与f(x2)异号y=f(x),y1=f(x1)y与y1同号真假x1=xy1=yx2=x直到 |y|< root=x 输出 rootroot函数运行情况:Input x1,x2:2,6 A root of equation is 5.0000main( )调用函数root输出根 x结束root函数xpoint函数调用函数xpoint调用函数ff函数递归调用 ( P.158 )定义:函数直接或间接的调用自身叫函数的递归调用f( )调f调f2调f1f1( )f2( )说明C编译系统对递归函数的自调用次数没有限制每调用函数一次,在内存堆栈区分配空间,用于存放函数变量、返回值等信息,所以递归次数过多,可能引起堆栈溢出int f(int x){ int y,z;……z=f(y);…….return(2*z);}int f1(int x){ int y,z;……z=f2(y);…….return(2*z);}int f2(int t){ int a,c;……c=f1(a);…….return(3+c);}例 求n的阶乘/*ch7_8.c*/#include int fac(int n){ int f;if(n<0) printf("n<0,data error!");else if(n==0||n==1) f=1;else f=fac(n-1)*n;return(f);}main(){ int n, y;printf("Input a integer number:");scanf("%d",&n);y=fac(n);printf("%d! =%15d",n,y);}例 Hanoi问题 (P.161)void move(char getone, char putone){ printf("%c--->%c\n",getone,putone); }void hanoi(int n,char one,char two,char three){ if(n==1) move(one,three);else{ hanoi(n-1,one,three,two);move(one,three);hanoi(n-1,two,one,three);}}main(){ int m;printf("Input the number of disks:");scanf("%d",&m);printf("The steps to moving %3d disks:\n",m);hanoi(m,'A','B','C');}ABC8.7 数组作为函数参数数组元素作函数实参——值传递例 两个数组大小比较432105a562312107688432105b212343986654n=0m=0k=0in=0m=0k=1in=0m=1k=1in=1m=1k=1in=1m=1k=2in=2m=1k=2in=3m=1k=2a和b为有10个元素的整型数组比较两数组对应元素变量n,m,k记录a[i]>b[i], a[i]==b[i],a[i]最后 若n>k,认为数组a>b若n若n==k,认为数组a==b#include main(){ int a[10],b[10],i,n=0,m=0,k=0;printf("Enter array a:\n");for(i=0;i<10;i++)scanf("%d",&a[i]);printf("Enter array b:\n");for(i=0;i<10;i++)scanf("%d",&b[i]);for(i=0;i<10;i++){ if(large(a[i],b[i])==1) n=n+1;else if(large(a[i],b[i])==0) m=m+1;else k=k+1;}/* Output */}int large(int x,int y){ int flag;if(x>y) flag=1;else if(xelse flag=0;return(flag);}数组名作函数参数地址传递在主调函数与被调函数分别定义数组,且类型应一致形参数组大小(多维数组第一维)可不指定形参数组名是地址变量例 求学生的平均成绩#include float average(int stu[10], int n);void main(){ int score[10], i;float av;printf("Input 10 scores:\n");for( i=0; i<10; i++ )scanf("%d", &score[i]);av=average(score,10);printf("Average is:%.2f", av);}float average(int stu[10], int n){ int i;float av,total=0;for( i=0; itotal += stu[i];av = total/n;return av;}实参用数组名形参用数组定义, int stu[ ]..2109score562312….….88stu例 数组元素与 数组名作函数参数比较12a调用前a[0]a[1]12a调用a[0]a[1]12xy21xy交换12a返回#include void swap2(int x,int y){ int z;z=x; x=y; y=z;}main(){ int a[2]={1,2};swap2(a[0],a[1]);printf("a[0]=%d\na[1]=%d\n",a[0],a[1]);}值传递12a调用前12ax调用21ax交换21a返回#include void swap2(int x[]){ int z;z=x[0]; x[0]=x[1]; x[1]=z;}main(){ int a[2]={1,2};swap2(a);printf("a[0]=%d\na[1]=%d\n",a[0],a[1]);}地址传递例 数组元素与 数组名作函数参数比较例 数组排序----简单选择排序void sort(int array[],int n){ int i,j,k,t;for(i=0;i{ k=i;for(j=i+1;jif(array[j]if(k!=i){ t=array[i];array[i]=array[k];array[k]=t;}}}main(){ int a[10],i;for(i=0;i<10;i++)scanf("%d",&a[i]);sort(a,10);for(i=0;i<10;i++)printf("%d ",a[i]);printf("\n");}0123456789a4968573299927137688arraykjjjkjkjjjjj949i=0例 数组排序----简单选择排序void sort(int array[],int n){ int i,j,k,t;for(i=0;i{ k=i;for(j=i+1;jif(array[j]if(k!=i){ t=array[i];array[i]=array[k];array[k]=t;}}}main(){ int a[10],i;for(i=0;i<10;i++)scanf("%d",&a[i]);sort(a,10);for(i=0;i<10;i++)printf("%d ",a[i]);printf("\n");}kjjkjkjjjjj0123456789a4968573299927137688array949kk1368i=10123456789a9132732495768768899arrayi=8例 数组排序----简单选择排序void sort(int array[],int n){ int i,j,k,t;for(i=0;i{ k=i;for(j=i+1;jif(array[j]if(k!=i){ t=array[i];array[i]=array[k];array[k]=t;}}}main(){ int a[10],i;for(i=0;i<10;i++)scanf("%d",&a[i]);sort(a,10);for(i=0;i<10;i++)printf("%d ",a[i]);printf("\n");}例 求二维数组中最大元素值1 3 5 72 4 6 815 17 34 12ijmax=11 3 5 72 4 6 815 17 34 12ijmax=31 3 5 72 4 6 815 17 34 12ijmax=5j1 3 5 72 4 6 815 17 34 12imax=7j1 3 5 72 4 6 815 17 34 12imax=7j1 3 5 72 4 6 815 17 34 12imax=34int max_value(int array[3][4]){ int i,j,k,max;max=array[0][0];for(i=0;i<3;i++)for(j=0;j<4;j++)if(array[i][j]>max)max=array[i][j];return(max);}main(){ int a[3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}};printf("max value is %d\n",max_value(a));}多维形参数组第一维维数可省略,第二维必须相同 int array[][4]例 求二维数组中各行元素之和get_sum_row(int x[][3], int result[] ,int row, int col){ int i,j;for(i=0;i{ result[i]=0;for(j=0;jresult[i]+=x[i][j];}}main(){ int a[2][3]={3,6,9,1,4,7};int sum_row[2],row=2,col=3,i;get_sum_row(a,sum_row,row,col);for(i=0;iprintf("The sum of row[%d]=%d\n",i+1,sum_row[i]);}314679asum_rowxresult18128.8 变量的存储属性概述变量是对程序中数据的存储空间的抽象内存…….main(){ int a;a=10;printf(“%d”,a);}编译或函数调用时为其分配内存单元1020002001程序中使用变量名对内存操作变量的属性数据类型:变量所持有的数据的性质(操作属性)存储属性存储器类型:寄存器、静态存储区、动态存储区生存期:变量在某一时刻存在-------静态变量与动态变量作用域:变量在某区域内有效-------局部变量与全局变量变量的存储类型auto -----自动型register-----寄存器型static ------静态型extern -----外部型变量定义格式: [存储类型] 数据类型 变量表;8.8 变量的存储属性概述变量是对程序中数据的存储空间的抽象如: int sum;auto int a,b,c;register int i;static float x,y;局部变量与全局变量局部变量---内部变量定义:在函数内定义,只在本函数内有效说明:main中定义的变量只在main中有效不同函数中同名变量,占不同内存单元形参属于局部变量可定义在复合语句中有效的变量局部变量可用存储类型:auto register static (默认为auto)float f1(int a){ int b,c;…….}char f2(int x,int y){ int i,j;……}main(){ int m,n;…….}a,b,c有效x,y,i,j有效m,n有效例 不同函数中同名变量main(){ int a,b;a=3;b=4;printf("main:a=%d,b=%d\n",a,b);sub();printf("main:a=%d,b=%d\n",a,b);}sub(){ int a,b;a=6;b=7;printf("sub:a=%d,b=%d\n",a,b);}例 复合语句中变量#define N 5main(){ int i;int a[N]={1,2,3,4,5};for(i=0;i{ int temp;temp=a[i];a[i]=a[N-i-1];a[N-i-1]=temp;}for(i=0;iprintf("%d ",a[i]);}运行结果:5 4 3 2 1例 复合语句中变量#define N 5main(){ int i;int a[N]={1,2,3,4,5};for(i=0;i{ int temp;temp=a[i];a[i]=a[N-i-1];a[N-i-1]=temp;}for(i=0;iprintf("%d ",a[i]);}例 不同函数中同名变量main(){ int a,b;a=3;b=4;printf("main:a=%d,b=%d\n",a,b);sub();printf("main:a=%d,b=%d\n",a,b);}sub(){ int a,b;a=6;b=7;printf("sub:a=%d,b=%d\n",a,b);}运行结果:main:a=3,b=4sub:a=6,b=7main:a=3,b=4全局变量---外部变量定义:在函数外定义,可为本文件所有函数共用有效范围:从定义变量的位置开始到本源文件结束,及有extern说明的其它源文件应尽量少使用全局变量,因为:全局变量在程序全部执行过程中占用存储单元降低了函数的通用性、可靠性,可移植性降低程序清晰性,容易出错定义 说明次数: 只能1次 可说明多次位置: 所有函数之外 函数内或函数外分配内存: 分配内存,可初始化 不分配内存,不可初始化外部变量说明: extern 数据类型 变量表;外部变量定义与外部变量说明不同若外部变量与局部变量同名,则外部变量被屏蔽外部变量可用存储类型:缺省 或 staticfloat max,min;float average(float array[], int n){ int i; float sum=array[0];max=min=array[0];for(i=1;i{ if(array[i]>max) max=array[i];else if(array[i]sum+=array[i];}return(sum/n);}main(){ int i; float ave,score[10];/*Input */ave=average(score,10);printf("max=%6.2f\nmin=%6.2f\naverage=%6.2f\n",max,min,ave);}作用域maxminint p=1,q=5;float f1(int a){ int b,c;…….}int f3(){…..}char c1,c2;char f2(int x,int y){ int i,j;……}main(){ int m,n;…….}c1,c2的作用范围p,q的作用范围extern char c1,c2;extern char c1,c2;c1,c2的作用范围扩展后c1,c2的作用范围扩展后例 外部变量定义与说明int max(int x, int y){ int z;z=x>y x:y;return(z);}main(){ extern int a,b;printf("max=%d",max(a,b));}int a=13,b=-8;运行结果:max=13extern int a,b;int max(){ int z;z=a>b a:b;return(z);}main(){ printf("max=%d",max());}int a=13,b=-8;/*ch7_17.c*/int a=3,b=5;max(int a, int b){ int c;c=a>b a:b;return(c);}main(){ int a=8;printf("max=%d",max(a,b));}例 外部变量与局部变量运行结果:max=8int i;main(){ void prt();for(i=0;i<5;i++)prt();}void prt(){ for(i=0;i<5;i++)printf(“%c”,’*’);printf(“\n”);}例 外部变量副作用运行结果:*****动态变量与静态变量存储方式静态存储:程序运行期间分配固定存储空间动态存储:程序运行期间根据需要动态分配存储空间内存用户区程序区静态存储区动态存储区全局变量、局部静态变量形参变量局部动态变量(auto register)函数调用现场保护和返回地址等生存期静态变量:从程序开始执行到程序结束动态变量:从包含该变量定义的函数开始执行至函数执行结束变量存储类型静态动态存储方式程序整个运行期间函数调用开始至结束生存期编译时赋初值,只赋一次每次函数调用时赋初值自动赋初值0或空字符不确定未赋初值静态存储区动态区存储区寄存器局部变量外部变量作用域定义变量的函数或复合语句内本文件其它文件局部变量默认为auto型register型变量个数受限,且不能为long, double, float型局部static变量具有全局寿命和局部可见性局部static变量具有可继承性extern不是变量定义,可扩展外部变量作用域register局部staticauto外部static外部存储类别例 文件file1.cint a;main( ){ …….…….f2;…….f1;…….}f1( ){ auto int b;………f2;……..}f2( ){ static int c;………}C作用域b作用域a作用域mainf2f1mainf1f2maina生存期:b生存期:c生存期:例 auto 变量的作用域main(){ int x=1;void prt(void);{ int x=3;prt();printf(“2nd x=%d\n”,x);}printf(“1st x=%d\n”,x);}void prt(void){ int x=5;printf(“3th x=%d\n”,x);}运行结果:3th x=52nd x=31st x=1x=1作用域x=1作用域x=3作用域x=5作用域main(){ void increment(void);increment();increment();increment();}void increment(void){ int x=0;x++;printf(“%d\n”,x);}例 局部静态变量值具有可继承性运行结果:111main(){ void increment(void);increment();increment();increment();}void increment(void){ static int x=0;x++;printf(“%d\n”,x);}运行结果:123例 变量的寿命与可见性#include int i=1;main(){ static int a;register int b=-10;int c=0;printf("-----MAIN------\n");printf("i:%d a:%d \b:%d c:%d\n",i,a,b,c);c=c+8;other();printf("-----MAIN------\n");printf("i:%d a:%d \b:%d c:%d\n",i,a,b,c);i=i+10;other();}other(){ static int a=2;static int b;int c=10;a=a+2; i=i+32; c=c+5;printf("-----OTHER------\n");printf("i:%d a:%d \b:%d c:%d\n",i,a,b,c);b=a;}-------Main------i:1 a:0 b:-10 c:0------Other------i:33 a:4 b:0 c:15-------Main-----i:33 a:0 b:-10 c:8-------Other-------i:75 a:6 b:4 c:15全局i1main: a0b:-10registermain:c0静态存储区动态存储区other: a2other: b0other: c10843315443other: c10675156main(){ void gx(),gy();extern int x,y;printf(“1: x=%d\ty=%d\n”,x,y);y=246;gx();gy();}void gx(){ extern int x,y;x=135;printf(“2: x=%d\ty=%d\n”,x,y);}int x,y;void gy(){ printf(“3: x=%d\ty=%d\n”,x,y);}例 用extern扩展外部变量作用域运行结果:1: x=0 y=02: x=135 y=2463: x=135 y=246例 引用其它文件中的外部变量int global;extern float x;main(){ int local;...}extern int global;static int number;func2(){ ...}float x;static int number;func3(){ extern int global;...}file1.cfile2.cfile3.c例 引用其它文件中的变量,输出a b和a的m次方int a;main(){ int power(int n);int b=3,c,d,m;printf("Enter the number a and its power:\n");scanf("%d,%d",&a,&m);c=a*b;printf("%d*%d=%d\n",a,b,c);d=power(m);printf("%d**%d=%d",a,m,d);}extern int a;int power(int n){ int i,y=1;for(i=1;i<=n;i++)y*=a;return(y);}(共114张PPT)关系运算符和关系表达式逻辑运算符和逻辑表达式if 语句条件运算符和条件表达式switch 语句第五章 选择结构程序设计选择结构(分支结构)◆“选择结构”也可以叫做“分支结构”。在一个合理的“选择结构”中,总是先进行某个条件的判断,然后根据判断的结果选择执行预先设定的操作。AB YesNoab◆ 要想实现“选择结构”,需要2个条件:① 能表达条件判断的运算符;② 能实现判断并选择的控制语句。5.1 关系运算符和关系表达式关系运算符关系表达式关系表达式的值关系运算符和关系表达式◆关系运算符(双目):也叫“比较运算符”6种: < 小于 <= 小于等于> 大于 >= 大于等于== 等于 != 不等于◆关系表达式:按规定用关系运算符把运算量连接起来构成的式子就是关系表达式。其中,运算量可以是常量、变量或任何合法的表达式。如:a*a <= 100如:NowIndex == MaxIndex关系运算符和关系表达式关系运算符的优先级:关系运算符的结合性:自左向右<、<=、>、>= 同级==、!= 同级高低算术运算符= 赋值运算符关系表达式的值◆关系表达式进行的是关系运算,也就是“比较运算”。比较的结果只可能有两个:“真”或“假” 。对应于实际生活中,就是“成立”还是“不成立” ,“正确”还是“不正确” ,“对”还是“不对” 这样一些比较和判断的结果。◆关系运算的结果的特点是:只有两种可能的结果;任何时候答案只可能是其中的一个;两种结果是相互对立不可能同时出现的。这样的特点正对应于数学上的“逻辑值”:“真”或“假” 。关系表达式的值因此:C语言中关系表达式的值是“逻辑值”,即“真”或“假”,分别以整数1和0来代表。就是说,如果关系表达式的值为“真”,则得到整数1;如果值为“假”,则得到整数0。#include void main( ){int a=3, b=2, c=1, d;printf(“%d\n”, a > b);d = b < c;printf(“d=%d\n”, d);d = a == b + c;printf(“d=%d\n”, d);d = a > b > c;printf(“d=%d\n”, d);return ;}关系表达式:例a>b的判断成立,所以其值为“真”;因此得到整数1#include void main( ){int a=3, b=2, c=1, d;printf(“%d\n”, a > b);d = b < c;printf(“d=%d\n”, d);d = a == b + c;printf(“d=%d\n”, d);d = a > b > c;printf(“d=%d\n”, d);return ;}关系表达式:例相当于d=(b#include void main( ){int a=3, b=2, c=1, d;printf(“%d\n”, a > b);d = b < c;printf(“d=%d\n”, d);d = a == b + c;printf(“d=%d\n”, d);d = a > b > c;printf(“d=%d\n”, d);return ;}关系表达式:例相当于d=(a==(b+c));#include void main( ){int a=3, b=2, c=1, d;printf(“%d\n”, a > b);d = b < c;printf(“d=%d\n”, d);d = a == b + c;printf(“d=%d\n”, d);d = a > b > c;printf(“d=%d\n”, d);return ;}关系表达式:例相当于d=((a>b)>c));#include void main( ){int a=3, b=2, c=1, d;printf(“%d\n”, a > b);d = b < c;printf(“d=%d\n”, d);d = a == b + c;printf(“d=%d\n”, d);d = a > b > c;printf(“d=%d\n”, d);return ;}关系表达式:例运行结果:1 d=0 d=1 d=05.2 逻辑运算符和逻辑表达式逻辑运算符逻辑表达式逻辑表达式的求解过程逻辑运算符◆逻辑运算实际上是复合的关系运算,即要判断一个大命题的成立与否,不仅要判断其中的各个小命题是否成立,还取决于每个小命题影响大命题成立的方式。逻辑运算符:3种&& 逻辑与(双目)|| 逻辑或(双目)! 逻辑非(单目)逻辑运算符逻辑运算符的优先级:逻辑运算符的结合性:&& 、|| 自左向右关系运算符&& 逻辑与高低算术运算符|| 逻辑或! 逻辑非= 赋值运算符! 自右向左逻辑运算符的运算规则◆逻辑运算是关系比较运算结果之间进行的运算,所以参与逻辑运算的运算量都是逻辑量 (即值只为“真”或“假”的量) ,逻辑运算的结果也是逻辑值(“真”或“假”,也可以用“True”或“False”来表示) 。逻辑运算符的运算规则&&逻辑与:全部运算量都为“T”,逻辑与运算才为“T”;只要有一个运算量为“F”,逻辑与运算即为“F”-5+5x > -5x < +5&&逻辑运算符的运算规则|| 逻辑或:全部运算量都为“F”,逻辑或运算才为“F”;只要有一个运算量为“T”,逻辑或运算即为“T”x < -5x > +5||-5+5逻辑运算符的运算规则! 逻辑非:运算量为“T” ,逻辑非运算则为“F” ;运算量为“F” ,逻辑非运算则为“T”x < 0! (x=> 0)或0逻辑运算符的运算规则逻辑运算规则表a b ! a ! b a && b a || bT T F F T TT F F T F TF T T F F TF F T T F F注:a、b代表逻辑量。T代表“真”值,F代表“假”值逻辑表达式◆逻辑表达式就是用逻辑运算符把运算量连接起来的式子。◆参与逻辑运算的运算量都是逻辑量,而逻辑表达式的值也是个“逻辑值”,即“真”或“假”,在C语言中分别以“1”和“0” 代表。C编译系统在给出逻辑运算结果时,以整数1代表“真”,整数 0代表“假”;而在求解逻辑表达式过程中当需要判断一个表达式的值是“真”是“假”时,则认为该表达式的值为非整数0时是“真”,而等于整数0时是“假”。逻辑表达式所以,逻辑表达式的值不是整数0就是整数1,而参与逻辑运算的运算量可以是任何类型的数据。既可以使常量、变量,也可以是表达式。如:x > -5 && x < +5等价于: (x > -5) && (x < +5)如:x && y + z如:5 && 3 && ! 0逻辑运算符的运算规则逻辑运算规则表a b ! a ! b a && b a || b非0 非0 0 0 1 1非0 0 0 1 0 10 非0 1 0 0 10 0 1 1 0 0注:a、b代表任何类型的数据(可以是常量、变量或表达式)逻辑表达式的求解过程◆逻辑表达式的求解顺序按照其结合性进行。由于参与逻辑运算的量必须是逻辑量,所以系统求的是每一个参与逻辑运算的运算量(可能是常量、变量或表达式)的逻辑值。对于“逻辑与&&”表达式和“逻辑或||”表达式,系统按从左向右顺序求解时,如果当求出某个运算量后整个逻辑表达式的值就能够确定时,则余下的运算量不被求解。◆特别注意:逻辑表达式的求解过程x < -5 || x > +5假设: x = -61 || x < +5没有被求解1x < -5 || x > +5假设: x = 610 || x > +50 || 1 逻辑表达式的求解过程x > -5 && x < +5假设: x = 601 && x < +51 && 0 x > -5 && x < +5假设: x = -60 && x < +5没有被求解0#include void main( ){int a = 3, b = 4, c = 5, d = 0, x = 0, y = 0;d = a + b > c && b == c;printf(“d = %d\n”, d);d = a ++ | | c ++;printf(“d = %d\n”, d);d = ! (x = a) && (y = b);printf(“d = %d\n”, d);printf(“a = %d, c = %d\n”, a, c);printf(“x = %d, y = %d\n”, x, y);return ;}逻辑表达式:例#include void main( ){int a = 3, b = 4, c = 5, d = 0, x = 0, y = 0;d = a + b > c && b == c;printf(“d = %d\n”, d);d = a ++ | | c ++;printf(“d = %d\n”, d);d = ! (x = a) && (y = b);printf(“d = %d\n”, d);printf(“a = %d, c = %d\n”, a, c);printf(“x = %d, y = %d\n”, x, y);return ;}逻辑表达式:例((a+b)>c)&&(b==c)(7>c) && (b==c) 1 && (b==c) 1 && 0 → 0#include void main( ){int a = 3, b = 4, c = 5, d = 0, x = 0, y = 0;d = a + b > c && b == c;printf(“d = %d\n”, d);d = a ++ | | c ++;printf(“d = %d\n”, d);d = ! ( x = a ) && ( y = b );printf(“d = %d\n”, d);printf(“a = %d, c = %d\n”, a, c);printf(“x = %d, y = %d\n”, x, y);return ;}逻辑表达式:例(a ++) || (c ++)(3) || (c ++) → 1 a = a + 1 → a=4 c ++ 没有被求解,就是说c 的值没有变#include void main( ){int a = 3, b = 4, c = 5, d = 0, x = 0, y = 0;d = a + b > c && b == c;printf(“d = %d\n”, d);d = a ++ | | c ++ ;printf(“d = %d\n”, d);d = ! (x = a) && (y = b);printf(“d = %d\n”, d);printf(“a = %d, c = %d\n”, a, c);printf(“x = %d, y = %d\n”, x, y);return ;}逻辑表达式:例4 → x ! (4) && (y = b) 0 && (y = b) → 0y = b 没有被求解,就是说y 的值没有变#include void main( ){int a = 3, b = 4, c = 5, d = 0, x = 0, y = 0;d = a + b > c && b == c;printf(“d = %d\n”, d);d = a ++ | | c ++;printf(“d = %d\n”, d);d = ! (x = a) && (y = b);printf(“d = %d\n”, d);printf(“a = %d, c = %d\n”, a, c);printf(“x = %d, y = %d\n”, x, y);return ;}逻辑表达式:例运行结果:d=0 d=1 d=0 a=4, c=5 x=4, y=0= 运算符与== 运算符的区别= 是“赋值运算符”。赋值表达式中,赋值运算符的左边只能是变量。注意:千万不要混淆或误用 = 与 == 运算符。== 是“等于关系运算符”。关系表达式中,等于运算符的两边可以是常量、变量或任何合法的表达式。=运算符与==运算符的区别假设 a = 1,则:(a = 1)==(b = 2)这个赋值表达式的数值是2,但逻辑值是 1是正确的关系表达式(a = 1) = (b = 2)是错误的赋值表达式a = 2这个关系表达式的逻辑值是 0a == 2课堂复习◆ 1. 表达式( )是表示整数 x 的值为奇数时值为“真”,x 为偶数时值为“假”的表达式。(A) x % 2 == 0(B) ! x % 2 != 0(C) ( x / 2 * 2 – x ) == 0! ( x % 2 )(E) X%2==1答案: (E)课堂复习◆ 2. 如果表达式 E 的值为“真”,则以下表达式中,( )的值是“假”。(A) ! E == 0(B) E > 0 || E < 0(C) E == 0(D) E != 0答案: (C)课堂复习◆ 3. 表达式( )是满足:当 a 的值大于 0,小于 n 时值为“真”,否则值为“假”的表达式。(a>0) && (a(B) a && (a(C) ! ((a<=0) || (a>=n))! (a<=0) && ! (a>=n)答案: (A) (C) (D)5.3 if 语句if 语句的三种形式if 语句的嵌套条件运算符及条件表达式if 语句if 语句是一种选择控制语句,而且是条件分支控制语句,即总是先进行一个条件的判断,然后根据判断的结果(成立或不成立)选择执行预先设定的操作。if 语句中的条件判断都是用一个表达式来表示的。在执行if 语句时,会求解这个表达式的逻辑值,因为这个表达式的逻辑值代表了判断的结果:值为“T”,判断成立,值为“F”,判断不成立。因此,if 语句根据判断的结果(成立或不成立)选择执行预先设定的操作,实际上是根据条件判断表达式的逻辑值是“真”是“假”来选择的。if 语句if 语句的三种形式1. if (表达式) 语句;如:if (x>0) sign=1;语句表达式TFif 语句的三种形式2. if (表达式) 语句1; else 语句2;如:if (x>0) sign=1; else sign=-1;语句1表达式TF语句2if 语句的三种形式3. if (表达式1) 语句1; else if (表达式2) 语句2; else if (表达式3) 语句3; …… else 语句n;如:if (score>89) putchar(‘A’); else if (score>79) putchar(‘B’); else if (score>69) putchar(‘C’); else if (score>59) putchar(‘D’); else putchar(‘E’);if 语句的三种形式3. if (表达式1) 语句1; else if (表达式2) 语句2; else if (表达式3) 语句3; …… else 语句n;实际上,第3种形式是属于第2种形式的一种特殊情况,是if嵌套语句的一种形式。2. if (表达式) 语句1; else 语句2;等价于:if (score>89) putchar(‘A’); else if (score>79) putchar(‘B’); else if (score>69) putchar(‘C’); else if (score>59) putchar(‘D’); else putchar(‘E’);if 语句的三种形式如:if (score>89) putchar(‘A’); else if (score>79) putchar(‘B’); else if (score>69) putchar(‘C’); else if (score>59) putchar(‘D’); else putchar(‘E’);if 语句的三种形式语句1表达式1TF表达式2……语句2……F语句n表达式n语句n+1FFTTTif语句第3种形式的执行流程图:if 语句的说明if语句中,if后面的表达式可以是任何类型的合法表达式,甚至可以是常量或变量。不管它是什么形式的数据,程序在执行if 语句时,首先求解该表达式,而且是求该表达式的“逻辑值” ,得到整数1或整数0,然后再选择执行相应的操作。如:if (x>0) sign=1; 关系表达式如:if (x) sign=1; 变量如:if (x+y) sign=1; 算术表达式如:if (1) sign=1; 常量if 语句的说明再来看看误用=和==运算符的结果:if (score == 100)printf(“Highest score!\n”);if (score = 100)printf(“Highest score!\n”);结果:只有score的值等于100时,才会执行printf语句。结果:不管score原先的值等于多少,都会执行printf语句。if 语句的说明if语句的第2、3种形式中,else关键字不能单独使用。一个else 必须有一个if 和它对应配对。同时,要理解在什么条件下才执行else 的内嵌语句。if 语句的说明if (num > 0)sign = 1;elsesign = -1;等价if (! (num > 0))sign = -1;elsesign = 1;等价if (num <= 0)sign = -1;elsesign = 1;可见,同样的问题,可以用不同的判断表达式,相应分支流程的操作也不同,但结果却是一样的。if 语句的说明当满足if或else中的条件时要执行的操作不只一个语句时(即if或else的内嵌语句多于一个时),必须用{ }把它们括起来形成一个复合语句才能保证流程的正确执行。if (num > 0)printf(“是正数。\n”);sign = 1;if 语句的说明此处不管num>0是否成立,都会执行sign=1; 语句,因为这一句不属于if的内嵌语句,显然流程出现了错误。原因在于if语句或else语句的内嵌语句只能包括一个语句。if 语句的说明正确的做法是:当if或else的内嵌语句多于一个时,用{ }把它们括起来形成一个复合语句。if (num > 0){printf(“是正数。\n”);sign = 1 ;}if 语句:例例:接收用户键入的一个字符,如果是大写字母就将其转换成小写字母,如果是小写字母就转换成大写字母输出。#include void main( ){char ch,ch1;scanf(“%c”, &ch);ch1=ch;printf(“ch=%c\n”, ch);if(ch >= ‘A’ && ch <= ‘Z’) ch1= ch + 32;if(ch >= ‘a’ && ch <= ‘z’) ch1= ch - 32;printf(“ch=%c\n”, ch1) ;return ;}if 语句:例首先使用scanf函数加%c格式字符从键盘接收一个字符#include void main( ){char ch,ch1;scanf(“%c”, &ch);ch1=ch;printf(“ch=%c\n”, ch);if(ch >= ‘A’ && ch <= ‘Z’) ch1= ch + 32;if(ch >= ‘a’ && ch <= ‘z’) ch1= ch - 32;printf(“ch=%c\n”, ch1) ;return ;}if 语句:例判断是否大写字母的逻辑表达式。也可用ch>=65&&ch<=90#include void main( ){char ch,ch1;scanf(“%c”, &ch);ch1=ch;printf(“ch=%c\n”, ch);if(ch >= ‘A’ && ch <= ‘Z’) ch1= ch + 32;if(ch >= ‘a’ && ch <= ‘z’) ch1= ch - 32;printf(“ch=%c\n”, ch1) ;return ;}if 语句:例如果是大写字母,就把它转换成对应的小写字母#include void main( ){char ch,ch1;scanf(“%c”, &ch);ch1=ch;printf(“ch=%c\n”, ch);if(ch >= ‘A’ && ch <= ‘Z’) ch1= ch + 32;if(ch >= ‘a’ && ch <= ‘z’) ch1= ch - 32;printf(“ch=%c\n”, ch1) ;return ;}if 语句:例判断是否小写字母的逻辑表达式。也可用ch>=97&&ch<=122#include void main( ){char ch,ch1;scanf(“%c”, &ch);ch1=ch;printf(“ch=%c\n”, ch);if(ch >= ‘A’ && ch <= ‘Z’) ch1= ch + 32;if(ch >= ‘a’ && ch <= ‘z’) ch1= ch - 32;printf(“ch=%c\n”, ch1) ;return ;}if 语句:例如果是小写字母,就把它转换成对应的大写字母if (表达式)语句1;else语句2;if 语句的嵌套在if或else的内嵌语句中又包含一个或多个if语句时,称为if语句的嵌套。常用于多重条件,多个分支的选择判断控制。if (表达式)else常规的 if 语句嵌套的 if 语句if (表达式) 语句1;else 语句2;if (表达式) 语句3;else 语句4;if (表达式)语句1;else语句2;if 语句的嵌套在if或else的内嵌语句中又包含一个或多个if语句时,称为if语句的嵌套。常用于多重条件,多个分支的选择判断控制。if (表达式)else常规的 if 语句嵌套的 if 语句语句1;if (表达式) 语句3;else 语句4;if (表达式)语句1;else语句2;if 语句的嵌套在if或else的内嵌语句中又包含一个或多个if语句时,称为if语句的嵌套。常用于多重条件,多个分支的选择判断控制。if (表达式)else常规的 if 语句嵌套的 if 语句if (表达式) 语句1;else 语句2;语句3;if (表达式)语句1;else语句2;if 语句的嵌套在if或else的内嵌语句中又包含一个或多个if语句时,称为if语句的嵌套。常用于多重条件,多个分支的选择判断控制。if (表达式)else常规的 if 语句嵌套的 if 语句if (表达式) 语句1;语句3;if (表达式)语句1;else语句2;if 嵌套语句中if和else的配对问题由于else不能单独出现,因此必须和if配对。那么,在if嵌套语句中就存在else和if如何配对的问题。if (表达式)else常规的 if 语句嵌套的 if 语句if (表达式) 语句1;语句3; if与else的配对原则是什么 if (表达式)语句1;else语句2;if 嵌套语句中if和else的配对问题注意:C 编译系统总是从if 嵌套的最里层开始向外,依次将else与在它前面的最近的未配对的if 配对。if (表达式)else常规的 if 语句嵌套的 if 语句if (表达式) 语句1;语句3;很显然:编译系统的理解和编程者的本意出现了偏差,程序结果当然就不对了。if 语句的嵌套:例例:下面4个语句段中,哪些能正确表示出以下的函数关系?(A)if(x < 0) y = -1;if(x > 0) y = 1;if(x == 0) y = 0;y =-1(x < 0)0 (x = 0)1 (x > 0)(A) 正确。使用了三个独立的if 语句分别判断三种情况。if 语句的嵌套:例例:下面4个语句段中,哪些能正确表示出以下的函数关系?(B)if(x != 0)if(x > 0) y = 1;else y = -1;else y = 0;y =-1(x < 0)0 (x = 0)1 (x > 0)(B) 正确。使用了一个if 嵌套语句分别判断三种情况。if和else数目相同,没有配对问题。if 语句的嵌套:例例:下面4个语句段中,哪些能正确表示出以下的函数关系?(C)y = 0;if(x >= 0)if(x > 0) y = 1;else y = -1;y =-1(x < 0)0 (x = 0)1 (x > 0)(C) 错误。算法:首先假设一种情况成立,然后使用一个if 嵌套语句再判断剩下的两种情况。但是,if和else的实际配对和设计不同。if 语句的嵌套:例例:下面4个语句段中,哪些能正确表示出以下的函数关系?(D)if(x < 0) y = -1;elseif(x > 0) y = 1;else y = 0;y =-1(x < 0)0 (x = 0)1 (x > 0)(D) 正确。使用了一个if 嵌套语句分别判断三种情况。else前面只有一个if,因此没有配对问题。这是最常用的if嵌套语句的形式。解决if 嵌套语句中if和else的配对问题为了避免在if 嵌套语句中出现实际的配对和所设计的配对发生误差,常采用如下方法:①只把要嵌套的语句放在else后面,这样由于每个else前面只有一个if,所以不会出错。(C)y = 0;if(x >= 0)if(x > 0) y = 1;else y = -1;容易出错(D)if(x < 0) y = -1;elseif(x > 0) y = 1;else y = 0;不易出错解决if 嵌套语句中if和else的配对问题为了避免在if 嵌套语句中出现实际的配对和所设计的配对发生误差,常采用如下方法:②如果嵌套语句必须放在if 后面,就把它用大括号括起来,或给每个if语句都加上else。(C)y = 0;if(x >= 0)if(x > 0) y = 1;else y = -1;容易出错(C)y = 0;if(x >= 0){if(x > 0) y = 1;}else y = -1;解决if 嵌套语句中if和else的配对问题为了避免在if 嵌套语句中出现实际的配对和所设计的配对发生误差,常采用如下方法:②如果嵌套语句必须放在if 后面,就把它用大括号括起来,或给每个if语句都加上else。(C)y = 0;if(x >= 0)if(x > 0) y = 1;else y = -1;容易出错(C)y = 0;if(x >= 0)if(x > 0) y = 1;else ;else y = -1;条件运算符和条件表达式符 号: : (唯一的三目运算符)用 法:表达式1 表达式2 : 表达式3优先级:只比赋值运算符和逗号运算符级别高;低于逻辑运算符结合性:自右向左条件表达式:用条件运算符把三个表达式连接起来构成的式子。如:x > y max = x : max = y;如:x > 0 sign = 1 : sign = -1;条件表达式的求解过程求解过程:首先求解表达式1的逻辑值;若值为“T”,则求解表达式2,表达式2的值即为整个条件表达式的值;若值为“F”,则求解表达式3,表达式3的值即为整个条件表达式的值。表达式1 表达式2 : 表达式3表达式2表达式3表达式1TF条件表达式的求解过程x > y max = x : max = y;if(x > y)max = x;elsemax = y;max=x;max=y;x>yTF从对条件表达式的求解过程来看,完全等同于一个if…else…语句。可见,本来需要用一个语句完成的操作,用一个表达式就可以了。条件表达式:例例:接收用户键入的一个字符,如果是大写字母就将其转换成小写字母,如果是小写字母就转换成大写字母输出。同样的题目,前面用if语句完成,现在用条件表达式来完成。#include void main( ){char ch,ch1;scanf(“%c”, &ch);ch1=ch;printf(“ch=%c\n”, ch);ch>=‘A’ && ch<=‘Z’ ch1=ch+32 : ch;ch>=‘a’ && ch<=‘z’ ch1=ch-32 : ch;printf(“ch=%c\n”, ch1) ;return ;}条件表达式:例用两个条件表达式同样能实现题目的要求条件表达式:例用条件表达式和用if 语句虽然实现了同样的功能,但本质不一样。用表达式来实现的好处是:既然它是个表达式,那么此表达式就可以继续参与其它的运算,从而形成更复杂的表达式来完成更复杂的操作。丰富的C表达式x > y max = x : max = y;条件表达式max = ( x > y x : y );赋值表达式等 价由于C语言运算符丰富,所以,要达到同样的目的可以使用不同的表达式。丰富的C表达式max = x > y (x > z x : z) 表达式 : (y > z y : z);if(x > y) 语句if(x > z) max = x; else max = z;elseif(y > z) max = y; else max = z;等 价同样,由于C语言运算符丰富,原本需要用语句才能执行的操作可以使用表达式完成。5.4 switch 语句switch语句switch语句是多条件判断多分支选择语句,类似于if 语句的第三种形式,即if嵌套语句。switch语句中使用了3个关键字:switch case defaultswitch语句的一般形式switch 语句的一般形式是:switch(表达式){case 常量表达式1 : 语句1;case 常量表达式2 : 语句2;……case 常量表达式n : 语句n;default : 语句n+1;}switch语句一般形式的执行过程执行过程:首先求解switch后表达式具体的值(不是逻辑值),然后将其值与各个case后的常量表达式的值对比;若有相等的,则从那个case后的语句开始往下执行;若无相等的,则执行default后的语句。如果表达式的具体值等于常量表达式1 的值,则执行语句1。 执行完语句1 后应该结束 switch语句的执行了,可是按照流程图会继续执行语句2,……,直到语句n+1,显然和设计的流程不同。switch语句一般形式的执行流程图语句1表达式的具体值F语句2……语句n语句n+1=常量表达式1的值?=常量表达式2的值?……=常量表达式n的值?TTTTFFF 流程好像 有问题?switch语句的完整形式switch语句还可以使用关键字 break 。它的作用是结束它所在的switch语句的执行。switch(表达式){case 常量表达式1 : 语句1; break;case 常量表达式2 : 语句2; break;……case 常量表达式n : 语句n; break;default : 语句n+1;}switch语句的完整形式如下:switch语句完整形式的执行流程图语句1表达式的具体值F语句2……语句n语句n+1=常量表达式1的值?=常量表达式2的值?……=常量表达式n的值?TTTTFFF有breakYN有breakN有breakN有breakNYYYswitch语句的有关注意事项1switch语句中,switch后的表达式可以是任何类型的表达式,系统求解它的具体值而不是逻辑值。绝大多数情况下这个表达式都是整型或字符型的表达式,即表达式的值为整型或字符型数据。switch语句的有关注意事项1每一个case后面的表达式也 只能是常量表达式,即能求解出具体值的表达式,而绝不能是关系表达式或逻辑表达式。通常其类型是和switch后的表达式相对应的。同时,每一个常量表达式的值必须互不相同,否则会出现互相矛盾的现象。正确的switch语句char grade;switch (grade){case ‘E’ : printf(“Excellent\n”);break;case ‘G’ : printf(“Good\n”);break;case ‘B’ : printf(“Bad\n”); break;default : printf(“God knows\n”);}grade是char型变量每个case后对应着是char型常量错误的switch语句int score;switch (score){case >89 : grade=‘E’; break;case >79 : grade=‘G’; break;case <60 : grade=‘B’; break;default : printf(“God knows\n”);}score是int型变量,case后就应该是int型的常量表达式。此处case后出现关系表达式是完全错误的。switch语句的有关注意事项2如果严格按照switch语句的完整形式,即在每个case和default后均加上break语句,则每个case和default的出现顺序并不影响程序的流程和执行结果。case和default的出现顺序switch (grade){case ‘E’ : printf(“Excellent\n”);break;case ‘G’ : printf(“Good\n”);break;case ‘B’ : printf(“Bad\n”);break;default : printf(“God knows\n”);}最后的case或default可以不用break语句。case和default的出现顺序switch (grade){case ‘B’ : printf(“Bad\n”);break;case ‘G’ : printf(“Good\n”);break;case ‘E’ : printf(“Excellent\n”);break;default : printf(“God knows\n”);}最后的case或default可以不用break语句。case和default的出现顺序switch (grade){default : printf(“God knows\n”);break;case ‘B’ : printf(“Bad\n”);break;case ‘G’ : printf(“Good\n”);break;case ‘E’ : printf(“Excellent\n”);}最后的case或default可以不用break语句。switch语句的有关注意事项3如果在满足多个case的情况下要执行相同的操作,则只需在最后一个有关的case后写上相应的语句,无需在每个case后面都写相同的语句。多个case执行相同的操作#define KEY_F2 0x3C00#define Ctrl_S 0x1F13#define ESC 0x011B#define Alt_X 0x2D00……switch (key){case KEY_F2 :case Ctrl_S : SaveFile; break;case ESC :case Alt_X : QuitSystem;}当用户按下F2功能键或Ctrl_S快捷键时,都是想执行保存文件的操作,所以只需写一次相应的语句。5.5 选择结构程序举例选择结构程序设计要想正确进行选择结构的程序设计,有三点很重要:① 要深刻理解C语言关于求解表达式的逻辑值的规定;② 要能根据具体问题写出正确的判断表达式。在大多数情况下,判断表达式都是关系表达式或逻辑表达式;③ 要掌握if语句和switch语句的执行流程,从而在已经形成的判断表达式基础上,用它们去正确实现具体问题中的流程。选择结构程序设计:例1例1:判断一个数是否是水仙花数。水仙花数是一个三位的正数,它各位数的立方和等于该数本身。题目信息:水仙花数是一个三位的正数它各位数的立方和等于该数本身。问题分析: 如何求出一个三位数三个位上的数,即个、十、百位数 求出三个位上的数之后,如何写判断表达式和设计相应的执行流程#include void main( ){int num, a, b, c;printf(“Please input a integer \n”);scanf(“%d”, &num);a = num / 100;b = num / 10 % 10;c = num % 10;if (num == a*a*a + b*b*b + c*c*c)printf(“%d is a 水仙花 number\n”, num);elseprintf(“%d isnt a 水仙花 number\n”, num);}选择结构程序设计:例1在要求用户输入数据前,一定要给出友好的提示#include void main( ){int num, a, b, c;printf(“Please input a integer \n”);scanf(“%d”, &num);a = num / 100;b = num / 10 % 10;c = num % 10;if (num == a*a*a + b*b*b + c*c*c)printf(“%d is a 水仙花 number\n”, num);elseprintf(“%d isnt a 水仙花 number\n”, num);}选择结构程序设计:例1用scanf函数接收用户输入的数据并保存#include void main( ){int num, a, b, c;printf(“Please input a integer \n”);scanf(“%d”, &num);a = num / 100;b = num / 10 % 10;c = num % 10;if (num == a*a*a + b*b*b + c*c*c)printf(“%d is a 水仙花 number\n”, num);elseprintf(“%d isnt a 水仙花 number\n”, num);}选择结构程序设计:例1求一个正数各个位上的数的一种方法#include void main( ){int num, a, b, c;printf(“Please input a integer \n”);scanf(“%d”, &num);a = num / 100;b = num / 10 % 10;c = num % 10;if (num == a*a*a + b*b*b + c*c*c)printf(“%d is a 水仙花 number\n”, num);elseprintf(“%d isnt a 水仙花 number\n”, num);}选择结构程序设计:例1写出判断表达式,并根据结果按设计的流程给出有关信息。#include void main( ){int num, a, b, c;printf(“Please input a integer \n”);scanf(“%d”, &num);a = num / 100;b = num / 10 % 10;c = num % 10;if (num != a*a*a + b*b*b + c*c*c)printf(“%d isnt a 水仙花 number\n”, num);elseprintf(“%d is a 水仙花 number\n”, num);}选择结构程序设计:例1可以使用不同的判断表达式,并设计相应的执行流程。#include void main( ){int num, a, b, c;printf(“Please input a integer \n”);scanf(“%d”, &num);a = num / 100;b = num / 10 % 10;c = num % 10;if (! (num == a*a*a + b*b*b + c*c*c))printf(“%d isnt a 水仙花 number\n”, num);elseprintf(“%d is a 水仙花 number\n”, num);}选择结构程序设计:例1可以使用不同的判断表达式,并设计相应的执行流程。选择结构程序设计:例2例2:输入三个正数,求由这三个数代表三边的三角形面积。问题分析: 如何在已知三边情况下求三角形面积area = s (s-a) (s-b) (s-c )其中s=(a+b+c)/2 C语言没有“开方”运算符,如何求平方根。利用系统提供的数学库函数double sqrt(double number) 如何判断用户键入的三个数代表的三边能否构成一个三角形。否则计算结果将无意义。#include #include void main( ){int a, b, c;float s, t;printf(“Please input 3 integer \n”);scanf(“%d, %d, %d”, &a, &b, &c);if ( a + b > c && a + c > b && b+ c > a ){t = (a + b + c) / 2.0;s = sqrt(t*(t – a)*(t – b)*(t – c));printf(“area = %7.2f \n”, s);}else printf(“Data Error\n”);}选择结构程序设计:例2由于使用了数学库函数sqrt,所以要包含相应的头文件。#include #include void main( ){int a, b, c;float s, t;printf(“Please input 3 integer \n”);scanf(“%d, %d, %d”, &a, &b, &c);if ( a + b > c && a + c > b && b+ c > a ){t = (a + b + c) / 2.0;s = sqrt(t*(t – a)*(t – b)*(t – c));printf(“area = %7.2f \n”, s);}else printf(“Data Error\n”);}选择结构程序设计:例2在要求用户输入数据前,一定要给出友好的提示#include #include void main( ){int a, b, c;float s, t;printf(“Please input 3 integer \n”);scanf(“%d, %d, %d”, &a, &b, &c);if ( a + b > c && a + c > b && b+ c > a ){t = (a + b + c) / 2.0;s = sqrt(t*(t – a)*(t – b)*(t – c));printf(“area = %7.2f \n”, s);}else printf(“Data Error\n”);}选择结构程序设计:例2用scanf函数接收用户输入的3个数据并保存#include #include void main( ){int a, b, c;float s, t;printf(“Please input 3 integer \n”);scanf(“%d, %d, %d”, &a, &b, &c);if ( a + b > c && a + c > b && b+ c > a ){t = (a + b + c) / 2.0;s = sqrt(t*(t – a)*(t – b)*(t – c));printf(“area = %7.2f \n”, s);}else printf(“Data Error\n”);}选择结构程序设计:例2这就是判断给定三边的三角形能否成立的判断表达式。#include #include void main( ){int a, b, c;float s, t;printf(“Please input 3 integer \n”);scanf(“%d, %d, %d”, &a, &b, &c);if ( a + b > c && a + c > b && b+ c > a ){t = (a + b + c) / 2.0;s = sqrt(t*(t – a)*(t – b)*(t – c));printf(“area = %7.2f \n”, s);}else printf(“Data Error\n”);}选择结构程序设计:例2如果表达式为“T”,表示三角形成立,则进行计算并输出。#include #include void main( ){int a, b, c;float s, t;printf(“Please input 3 integer \n”);scanf(“%d, %d, %d”, &a, &b, &c);if ( a + b > c && a + c > b && b+ c > a ){t = (a + b + c) / 2.0;s = sqrt(t*(t – a)*(t – b)*(t – c));printf(“area = %7.2f \n”, s);}else printf(“Data Error\n”);}选择结构程序设计:例2如果表达式值为“F”,表示三角形不成立,则给出出错信息。课后作业1.课本P85习题4.7 ~ 4.10P104习题 5.4 ~ 5.102. 编程判断一个正数是否是同构数。同构数是这样一个数,它出现在它的平方数的右边。比如,5就是同构数,因为5的平方数是25,25右边的数正好是5。同理,25也是同构数,因为25也出现在其平方数625的右边。(题目中假设该数小于10000)(共30张PPT)第4章 顺序结构程序设计为了让计算机处理各种数据,首先就应该把源数据输入到计算机中;计算机处理结束后,再将目标数据信息以人能够识别的方式输出。C语言中的输入输出操作,是由C语言编译系统提供的库函数来实现。4.1 格式化输出——printf()函数4.2 格式化输入——scanf()函数4.3 单个字符输入输出——getchar()和putchar()函数4.4 顺序结构程序设计良好的源程序书写风格──顺序程序段左对齐4.1 格式化输出——printf()函数printf()函数的作用:向计算机系统默认的输出设备(一般指终端或显示器)输出一个或多个任意类型的数据。4.1.1 printf()函数的一般格式[案例3.1] 已知圆半径radius=1.5,求圆周长和圆面积。 /*案例代码文件名:AL3_1.C。*/ main() {float radius,length,area,pi=3.1415926;radius=1.5;length=2*pi*radius; /*求圆周长*/area=pi*radius*radius; /*求圆面积*/printf(“radius=%f\n”,radius); /*输出圆半径*/printf(“length=%7.2f,area=%7.2f\n”,length,area); /*输出圆周长、面积*/ }程序运行结果如下:radius=1.500000length= 9.42,area= 7.07printf()函数的一般格式如下:printf("格式字符串" [,输出项表]);1.格式字符串。“格式字符串”也称“转换控制字符串”,可以包含三种字符:(1)格式指示符。格式指示符的一般形式如下:%[标志][宽度][.精度][F|N|h|L][类型]常用的标志字符如表4-1所示(P.77—P.78),常用的宽度指示符如表4-2所示(P.78) 。(2)转义字符例如,[案例3.1]中printf()函数中的'\n'就是转义字符,输出时产生一个“换行”操作。(3)普通字符──除格式指示符和转义字符之外的其它字符。格式字符串中的普通字符,原样输出。例如,[案例3.1]中“printf("radius=%f\n", radius);”语句中的“radius=”,“printf("length=%7.2f,area=%7.2f\n", length,area);”语句中的“length=”、“area=”等都是普通字符。2.输出项表输出项表是可选的。如果要输出的数据不止1个,相邻2个之间用逗号分开。下面的printf()函数都是合法的:(1)printf("I am a student.\n");(2)printf("%d",3+2);(3)printf("a=%f b=%5d\n", a, a+3);必须强调:“格式字符串”中的格式指示符,必须与“输出项表”中、输出项的数据类型一致,否则会引起输出错误。4.1.2 格式指示符输出不同类型的数据,要使用不同的类型转换字符。1.类型转换字符d──以带符号的十进制整数形式输出。[案例3.2] 类型转换字符d的使用。/*案例代码文件名:AL3_2.C。*/main(){ int num1=123;long num2=123456;/*用3种不同格式,输出int型数据num1的值*/printf("num1=%d,num1=%5d,num1=%-5d,num1=%2d\n",num1,num1,num1,num1);/*用3种不同格式,输出long型数据num2的值*/printf("num2=%ld,num2=%8ld,num2=%5ld\n",num2,num2,num2);printf("num1=%ld\n",num1);}程序运行结果如下:num1=123,num1=□□123,num1=123□□,num1=123num2=123456,num2=□□123456,num2=123456num1=16908411对于整数,还可用八进制、无符号形式(%o(小写字母o))和十六进制、无符号形式(%x)输出。对于unsigned型数据,也可用%u格式符,以十进制、无符号形式输出。所谓无符号形式是指,不论正数还是负数,系统一律当作无符号整数来输出。例如,printf("%d,%o,%x\n",-1,-1,-1);2.类型转换字符f──以小数形式、按系统默认的宽度,输出单精度和双精度实数。[案例3.3] 类型转换字符f的使用。/*案例代码文件名:AL3_3.C。*/main( ){float f=123.456;double d1,d2;d1=1111111111111.111111111;d2=2222222222222.222222222;printf("%f,%12f,%12.2f,%-12.2f,%.2f\n",f,f,f,f,f);printf("d1+d2=%f\n",d1+d2);}程序运行结果如下:123.456001,□□123.456001,□□□□□□123.46,123.46□□□□□□,123.46d1+d2=3333333333333.333010本案例程序的输出结果中,数据123.456001和3333333333333.333010中的001和010都是无意义的,因为它们超出了有效数字的范围。对于实数,也可使用格式符%e,以标准指数形式输出:尾数中的整数部分大于等于1、小于10,小数点占一位,尾数中的小数部分占5位;指数部分占4位(如e-03),其中e占一位,指数符号占一位,指数占2位,共计11位。也可使用格式符%g,让系统根据数值的大小,自动选择%f或%e格式、且不输出无意义的零。3.类型转换字符c──输出一个字符(只占一列宽度)。[案例3.4] 类型转换字符c的使用。/*案例代码文件名:AL3_4.C。*/main(){char c='A';int i=65;printf("c=%c,%5c,%d\n",c,c,c);printf("i=%d,%c",i,i);}程序运行结果如下:c=A,□□□□A,65i=65,A需要强调的是:在C语言中,整数可以用字符形式输出,字符数据也可以用整数形式输出。将整数用字符形式输出时,系统首先求该数与256的余数,然后将余数作为ASCII码,转换成相应的字符输出。4.类型转换字符s──输出一个字符串。[案例3.5] 类型转换字符s的使用。/*案例代码文件名:AL3_10.C。*/main(){printf("%s,%5s,%-10s","Internet","Internet","Internet");printf("%10.5s,%-10.5s,%4.5s\n","Internet","Internet","Internet");}程序运行结果如下:Internet,Internet,Internet□□,□□□□□Inter,Inter□□□□□,Inter注意:系统输出字符和字符串时,不输出单引号和双引号。4.1.3 使用说明(1)printf()可以输出常量、变量和表达式的值。但格式控制中的格式说明符,必须按从左到右的顺序,与输出项表中的每个数据一一对应,否则出错。例如,printf("str=%s, f=%d, i=%f\n", "Internet", 1.0 / 2.0, 3 + 5, "CHINA");是错误的。(2)格式字符x、e、g可以用小写字母,也可以用大写字母。使用大写字母时,输出数据中包含的字母也大写。除了x、e、g格式字符外,其它格式字符必须用小写字母。例如,%f不能写成%F。(3)格式字符紧跟在“%”后面就作为格式字符,否则将作为普通字符使用(原样输出)。例如,“printf(”c=%c, f=%f\n“, c, f);”中的第一个c和f,都是普通字符。[Return]4.2 格式化输入——scanf()函数scanf()函数是用来从外部输入设备向计算机主机输入数据的。4.2.1 scanf()函数的一般格式[案例3.6] 已知圆柱体的底半径radius=1.5,高high=2.0,求其体积。 /*案例代码文件名:AL3_6.C。*/ main() { float radius=1.5, high=2.0, pi=3.14159, vol; vol=pi*radius*radius*high; /*求体积*/ printf(“vol=%7.2f\n”,vol); /*输出求出的体积*/ }[案例3.7] 已知圆柱体的底半径为radius,高为high,求其体积。/*案例代码文件名:AL3_7.C。*//*功能:说明函数scanf()的格式及作用。*/main(){float radius,high,vol,pi=3.1415926;printf("Please input radius & high: ");scanf("%f%f",&radius,&high); /*从键盘输入两个实数赋给变量r,h*/vol=pi*radius*radius*high;printf("radius=%7.2f, high=%7.2f, vol=%7.2f\n",radius,high,vol);}程序运行结果如下:Please input radius & high: 1.5□2.0↙radius=□□□1.50,high=□□□2.00,vol=□□14.14在程序中给计算机提供数据,可以用赋值语句,也可以用输入函数。在C语言中,可使用scanf()函数,通过键盘输入,给计算机同时提供多个、任意的数据。1. scanf()函数的一般格式scanf("格式字符串", 输入项首地址表);(1)格式字符串。格式字符串可以包含3种类型的字符:格式指示符、空白字符(空格、Tab键和回车键)和非空白字符(又称普通字符)。格式指示符与printf()函数的相似,空白字符作为相邻2个输入数据的缺省分隔符,非空白字符在输入有效数据时,必须原样一起输入。(2)输入项首地址表──由若干个输入项首地址组成,相邻2个输入项首地址之间,用逗号分开。输入项首地址表中的地址,可以是变量的首地址,也可以是字符数组名或指针变量。变量首地址的表示方法: &变量名其中“&”是地址运算符。例如,[案例3.7]中的“&radius”是指变量radius在内存中的首地址。2.scanf()函数的功能:从键盘上接收格式化输入。运行[案例3.7]的程序时,从键盘上输入2个实数,分别存入&radius、&high起始的存储单元中,即输入两个实数分别赋给radius和high。4.2.2 格式指示符格式指示符的一般形式为: % [*] [宽度] [F|N] [h|l] 类型字符1.类型字符类型字符如表3-6所示。例如,在[案例3.7]的scanf()函数语句中,格式字符串“%f%f”。2.宽度n指定该项输入数据所占列数为n。换句话说,读取输入数据中相应的n位,但按需要的位数赋给相应的变量,多余部分被舍弃。例如,scanf("%3c%3c",&ch1,&ch2);printf("ch1=%c,ch2=%c\n",ch1,ch2);假设输入“abcdefg”,则系统将读取的“abc”中的“a”赋给变量ch1;将读取的“def”中的“d”赋给变量ch2,所以printf()函数的输出结果为:ch1=a,ch2=d。3.赋值抑制字符*表示本输入项对应的数据读入后,不赋给相应的变量(该变量由下一个格式指示符输入)。例如,scanf("%2d%*2d%3d",&num1,&num2);printf("num1=%d,num2=%d\n",num1,num2);假设输入“123456789”,则系统将读取“12”并赋值给num1;读取“34”、但舍弃掉(“*”的作用);读取“567”并赋值给num2。所以,printf()函数的输出结果为:num1=12,num2=567。4.类型修饰符──F、N、h、l。其含义与printf()中的一样,分别为远指针、近指针、短整型和长整型。4.2.3 数据输入操作1.如果相邻2个格式指示符之间,不指定数据分隔符(如逗号、冒号等),则相应的2个输入数据之间,至少用一个空格分开,或者用Tab键分开,或者输入1个数据后,按回车,然后再输入下1个数据。例如,scanf("%d%d",&num1,&num2);假设给num1输入12,给num2输入36,则正确的输入操作为:12□36↙或者:12↙36↙注:使用“↙”符号表示按回车键操作,在输入数据操作中的作用是,通知系统输入操作结束。2.“格式字符串”中出现的普通字符(包括转义字符形式的字符),务必原样输入。例如,scanf("%d,%d",&num1,&num2);假设给num1输入12,给num2输入36,正确的输入操作为:12,36↙另外,scanf()函数中、格式字符串内的转义字符(如\n),系统并不把它当转义字符来解释,从而产生一个控制操作,而是将其视为普通字符,所以也要原样输入。例如:scanf("num1=%d,num2=%d\n",&num1,&num2);假设给num1输入12,给num2输入36,正确的输入操作为:num1=12,num2=36\n↙提高人机交互性建议:为改善人机交互性,同时简化输入操作,在设计输入操作时,一般先用printf()函数输出一个提示信息,再用scanf()函数进行数据输入。例如,将scanf("num1=%d,num2=%d\n",&num1,&num2);改为:printf("num1="); scanf("%d",&num1);printf("num2="); scanf("%d",&num2);3.输入数据时,遇到以下情况,系统认为该数据结束:(1)遇到空格,或者回车键,或者Tab键。(2)遇到输入域宽度结束。例如“%3d”,只取3列。(3)遇到非法输入。例如,在输入数值数据时,遇到字母等非数值符号(数值符号仅由数字字符0-9、小数点和正负号构成)。4.使用格式说明符“%c”输入单个字符时,空格和转 义字符均作为有效字符被输入。例如,scanf("%c%c%c",&ch1,&ch2,&ch3);printf("ch1=%c,ch2=%c,ch3=%c\n",ch1,ch2,ch3);假设输入:A□B□C↙,则系统将字母'A'赋值给ch1,空格'□'赋值给ch2,字母'B'赋值给ch3。[Return]4.3 单个字符输入输出——getchar()和putchar()函数4.3.1 单个字符的输出──putchar()函数[案例3.8] putchar() 函数的格式和使用方法。/*案例代码文件名:AL3_8.C*//*功能:说明putchar()函数的格式和使用方法。*/#include "stdio.h" /*编译预处理命令:文件包含*/main(){char ch1='N', ch2='E', ch3='W';putchar(ch1); putchar(ch2); putchar(ch3); /*输出*/putchar('\n');putchar(ch1); putchar('\n'); /*输出ch1的值,并换行*/putchar('E'); putchar('\n'); /*输出字符'E',并换行*/putchar(ch3); putchar('\n');}程序运行结果如下:NEWNEW1.putchar()函数的格式: putchar(ch);其中ch可以是一个字符变量或常量,也可以是一个转义字符。2.putchar()函数的作用:向终端输出一个字符。(1)putchar()函数只能用于单个字符的输出,且一次只能输出一个字符。另外,从功能角度来看,printf()函数可以完全代替putchar()函数。(2)在程序中使用putchar()函数,务必牢记:在程序(或文件)的开头加上编译预处理命令(也称包含命令),即:#include "stdio.h"表示要使用的函数,包含在标准输入输出(stdio)头文件(.h)中。4.3.2 单个字符的输入──getchar()函数[案例3.9] 说明getchar()函数的格式和作用。/*案例代码文件名:AL3_9.C*//*功能:说明getchar()函数的格式和作用。*/#include "stdio.h" /*文件包含*/main(){char ch;printf("Please input two character: ");ch=getchar(); /*输入1个字符并赋给ch */putchar(ch);putchar('\n');putchar(getchar()); /*输入一个字符并输出*/putchar('\n');}程序运行情况如下:Please input two characters: ab↙ab1.getchar()函数的格式:getchar();2.getchar()函数的作用:从系统隐含的输入设备(如键盘)输入一个字符。另外,从功能角度来看,scanf()函数可以完全代替getchar()函数。(1)getchar()函数只能用于单个字符的输入,一次输入一个字符。(2)程序中要使用getchar()函数,必须在程序(或文件)的开头加上编译预处理命令:#include "stdio.h“[Return]4.4 顺序结构程序设计在顺序结构程序中,各语句(或命令)是按照位置的先后次序,顺序执行的,且每个语句都会被执行到。[案例3.10] 输入任意三个整数,求它们的和及平均值。 /*案例代码文件名:AL3_10.C */ /*功能:设计一个顺序结构程序,求三个整数的和及平均值。*/ main() {int num1,num2,num3,sum; float aver; printf("Please input three numbers:"); scanf("%d,%d,%d",&num1,&num2,&num3);/*输入三个整数*/ sum=num1+num2+num3; /*求累计和*/ aver=sum/3.0; /*求平均值*/ printf("num1=%d,num2=%d,num3=%d\n",num1,num2,num3); printf("sum=%d,aver=%7.2f\n",sum,aver); }思考题:能否将“aver=sum/3.0;”中“3.0”改为“3”?[案例3.11] 求方程ax2+bx+c=0的实数根。a,b,c由键盘输入,a≠0且b2-4ac>0。/*案例代码文件名:AL3_11.C。*//*功能:设计一个顺序结构程序,求方程的根。*/#include "math.h" /*为使用求平方根函数sqrt(),包含math.h头文件 */main(){float a,b,c,disc,x1,x2;printf("Input a, b, c: ");scanf("%f,%f,%f",&a,&b,&c); /*输入方程的三个系数的值*/disc=b*b-4*a*c; /*求判别式的值赋给disc*/x1=(-b+sqrt(disc))/(2*a);x2=(-b-sqrt(disc))/(2*a);printf("\nx1=%6.2f\nx2=%6.2f\n",x1,x2);}[案例3.12] 从键盘输入一个小写字母,要求用大小写字母形式输出该字母及对应的ASCII码值。/*案例代码文件名:AL3_12.C。*/#include "stdio.h"main(){char c1,c2;printf("Input a lowercase letter: ");c1=getchar();putchar(c1);printf(",%d\n",c1);c2=c1-32; /*将大写字母转换成对应的小写字母*/printf("%c,%d\n",c2,c2);}程序运行情况如下:Input a lowercase letter: a↙a,97A,65在顺序结构程序中,一般包括以下几个部分:1.程序开头的编译预处理命令。在程序中要使用标准函数(又称库函数),除printf()和scanf()外,其它的都必须使用编译预处理命令,将相应的头文件包含进来。。2.顺序结构程序的函数体中,是完成具体功能的各个语句和运算,主要包括:(1)变量类型的说明。(2)提供数据语句。(3)运算部分。(4)输出部分。[Return]良 好 的 源 程 序 书 写 风 格──顺序程序段左对齐顺序程序段中的所有语句(包括说明语句),一律与本顺序程序段的首行左对齐。[Return](共52张PPT)第04-06章 C程序流程设计4.4 C语句概述C语句:以“;”作分隔符,编译后产生机器指令.C语句分类表达式语句:表达式加分号构成。空语句: ;程序控制语句(9种):if( )~else~switchfor( )~while( )~do~while( )continuebreakgotoreturn分支循环辅助控制如 total=total+limit;a=3;func( );printf(“Hello,world!\n”);复合语句:用 {…}括起来的一组语句一般形式: { [数据说明部分;]执行语句部分;}说明:“}”后不加分号语法上和单一语句相同复合语句可嵌套4.5 程序的三种基本结构结构化程序设计基本思想:任何程序都可以用三种基本结构表示,限制使用无条件转移语句(goto)结构化程序:由三种基本结构反复嵌套构成的程序叫~优点:结构清晰,易读,提高程序设计质量和效率三种基本结构顺序结构ABAB流程图N-S图PAB真假PBA真假选择结构kA1A2AiAnk=k2k=k1k=knk=ki......二分支选择结构多分支选择结构循环结构当型循环结构直到型循环结构PA假真当P为真AAP真假A直到P为真注:A,B,A1….An可以是一个简单语句,也可以是一个基本结构第5章 选择型程序设计if语句(条件选择语句)if语句的三种形式形式一:格式:if (expression)statement执行过程:exprstatement非0=0例:if (x>y)printf(“%d”,x);exprstatement1statement2非0=0形式二:格式:if (expression)statement1elsestatement2执行过程:例:if (x>y) max=x;else max=y;形式三:格式:if ( expr1 ) statement1else if (expr2 ) statement2else if (expr3 ) statement3…...[ else statementn ]expr1statemnt1非0=0expr2expr3statemntnstatemnt3statemnt2非0非0=0=0执行过程:例:if (salary>1000) index=0.4;else if (salary>800) index=0.3;else if (salary>600) index=0.2;else if (salary>400) index=0.1;else index=0;如:if(a==b&&x==y) printf(“a=b,x=y”);if(3) printf(“OK”);if(‘a’) printf(“%d”,’a’);说明:if后面的表达式类型任意语句可以是复合语句if(x) if(x!=0)if(!x) if(x==0)例 考虑下面程序的输出结果:#include main(){ int x,y;scanf(“%d,%d”,&x,&y);if(x>y)x=y; y=x;elsex++; y++;printf(“%d,%d\n”,x,y);}Compile Error!/*ch4_1.c*/#include main(){ int x,y;printf("Enter an integer:");scanf("%d",&x);y=x;if(y<0)y= -y;printf("\ninteger:%d--->absolute value:%d\n",x,y);}例 求一个数的绝对值运行:Enter an integer:-12 integer:-12--->absolute value :12/*ch4_2.c*/#include main(){ int a,b;printf("Enter integer a:");scanf("%d",&a);printf("Enter integer b:");scanf("%d",&b);if(a==b)printf("a==b\n");elseprintf("a!=b\n");}例 输入两个数并判断两数相等否运行:Enter integer a:12 Enter integer b:12 a==b运行:Enter integer a:12 Enter integer b:9 a!=b/*ch4_3.c*/#include main(){ char c;printf("Enter a character:");c=getchar();if(c<0x20) printf("The character is a control character\n");else if(c>='0'&&c<='9') printf("The character is a digit\n");else if(c>='A'&&c<='Z') printf("The character is a capital letter\n");else if(c>='a'&&c<='z') printf("The character is a lower letter\n");else printf("The character is other character\n");}例 判断输入字符种类运行:Enter a character: The character is a control character运行:Enter a character:8 The character is a digit运行: Enter a character: D The character is a capital letter运行: Enter a character: h The character is a lower letter运行: Enter a character:F1 The character is other characterif语句嵌套:一般形式:if (expr1)if (expr2) statement1else statement2elseif(expr3) statement3else statement4内嵌if内嵌ifif (expr1)if (expr2)statement1elsestatement2内嵌ifif (expr1)if (expr2)statement1elsestatement3内嵌ifif (expr1)statement1elseif(expr3)statement3elsestatement4内嵌if例 输入两数并判断其大小关系/*ch4_4.c*/#include main(){ int x,y;printf("Enter integer x,y:");scanf("%d,%d",&x,&y);if(x!=y)if(x>y) printf("X>Y\n");else printf("Xelseprintf("X==Y\n");}运行:Enter integer x,y:12,23 XEnter integer x,y:12,6 X>YEnter integer x,y:12,12 X==Yif ~ else 配对原则:缺省{ }时,else总是和它上面离它最近的未配对的if配对if(……)if(……)if(……)else…...else…...else…...例: if (a==b)if(b==c)printf(“a==b==c”);elseprintf(“a!=b”);修改: if (a==b){ if(b==c)printf(“a==b==c”);}elseprintf(“a!=b”);实现if ~ else 正确配对方法:加{ }例 考虑下面程序输出结果:main(){ int x=100,a=10,b=20;int v1=5,v2=0;if(aif(b!=15)if(!v1)x=1;elseif(v2) x=10;x=-1;printf(“%d”,x);}结果:-1switch语句(开关分支语句)一般形式:switch( 表达式){ case E1:语句组 1;break;case E2:语句组 2;break;…….case En:语句组 n;break;[default:语句组 ;break;]}执行过程:switch表达式语句组1语句组2语句组n语句组…...E 1E 2Endefaultcase说明:E1,E2,…En是常量表达式,且值必须互不相同语句标号作用,必须用break跳出case后可包含多个可执行语句,且不必加{ }switch可嵌套多个case可共用一组执行语句如: ……case ‘A’:case ‘B’:case ‘C’:printf(“score>60\n”);break;……..例 switch(score){ case 5: printf(“Very good!”);case 4: printf(“Good!”);case 3: printf(“Pass!”);case 2: printf(“Fail!”);default : printf(“data error!”);}运行结果:score为5时,输出:Very good! Good! Pass! Fail! data error!例 void main(){ int x=1,y=0,a=0,b=0;switch(x){ case 1:switch(y){ case 0: a++; break;case 1: b++; break;}case 2: a++;b++; break;case 3: a++;b++;}printf(“\na=%d,b=%d”,a,b);}运行结果:a=2,b=1/*ch4_5.c*/#include main(){ int c;printf("Enter m or n or h or other:");c=getchar();switch(c){ case 'm': printf("\nGood morning!\n");break;case 'n': printf("\nGood night!\n"); break;case 'h': printf("\nHello!\n"); break;default : printf("\n \n"); break;}}例 根据输入字母输出字符串第6章 循环型程序设计概述C语言可实现循环的语句:用goto 和 if 构成循环while 语句do ~ while 语句for 语句goto语句及用goto构成循环goto语句一般格式:goto 语句标号;….…..标号:语句;功能:无条件转移语句说明:不能用整数作标号只能出现在goto所在函数内,且唯一只能加在可执行语句前面限制使用goto语句例 用if 和goto语句构成循环,求/*ch5_1.c*/#include main(){ int i,sum=0;i=1;loop: if(i<=100){ sum+=i;i++;goto loop;}printf("%d",sum);}sum=0+1sum==1+2=3sum=3+3=6sum=6+4……sum=4950+100=5050循环初值循环终值循环变量增值循环条件循环体例 从键盘输入一组数据,以0结束输入,求数据和/*ch5_11.c*/#include main(){ int number,sum=0;read_loop: scanf("%d",&number);if(!number) goto print_sum;sum+=number;goto read_loop;print_sum: printf("The total sum is %d\n",sum);}while语句一般形式:while(表达式)循环体语句;执行流程:expr循环体假(0)真(非0)while特点:先判断表达式,后执行循环体说明:循环体有可能一次也不执行循环体可为任意类型语句下列情况,退出while循环条件表达式不成立(为零)循环体内遇break,return,goto无限循环: while(1)循环体;例 用while循环求/*ch5_2.c*/#include main(){ int i,sum=0;i=1;while(i<=100){ sum=sum+i;i++;}printf("%d",sum);}循环初值循环终值循环变量增值循环条件循环体例 显示1~10的平方/*ch5_21.c*/#include main(){ int i=1;while(i<=10){ printf("%d*%d=%d\n",i,i,i*i);i++;}}运行结果:1*1=12*2=43*3=94*4=165*5=256*6=367*7=498*8=649*9=8110*10=100do~while语句一般形式:do循环体语句;while(表达式);执行流程:do循环体expr假(0)真(非0)while特点:先执行循环体,后判断表达式说明:至少执行一次循环体do~while可转化成while结构expr循环体假(0)真(非0)循环体While循环例 用do~while循环求/*ch5_3.c*/#include main(){ int i,sum=0;i=1;do{ sum+=i;i++;}while(i<=100);printf("%d",sum);}例 while和do~while比较/*ch5_4.c*/#include main(){ int i,sum=0;scanf("%d",&i);do{ sum+=i;i++;}while(i<=10);printf("%d",sum);}main(){ int i,sum=0;scanf("%d",&i);while(i<=10){ sum+=i;i++;}printf("%d",sum);}for语句一般形式:for([expr1] ;[ expr2] ;[ expr3])循环体语句;执行流程:expr2循环体假(0)真(非0)forexpr1expr3for语句一般应用形式:for(循环变量赋初值;循环条件;循环变量增值){循环体语句;}说明:for语句中expr1, expr2 ,expr3 类型任意,都可省略,但分号;不可省无限循环: for(;;)for语句可以转换成while结构expr1;while(expr2){循环体语句;expr3;}例 用for循环求#include main(){ int i,sum=0;for(i=1;i<=100;i++)sum+=i;printf("%d",sum);}例:#includemain( ){ int i=0;for(i=0;i<10;i++)putchar(‘a’+i);}运行结果:abcdefghij例:#includemain( ){ int i=0;for(;i<10;i++)putchar(‘a’+i);}例:#includemain( ){ int i=0;for(;i<10;)putchar(‘a’+(i++));}例:#includemain( ){ int i=0;for(;i<10;putchar(‘a’+i),i++);}main(){ int i,j,k;for(i=0,j=100;i<=j;i++,j--){ k=i+j;printf("%d+%d=%d\n",i,j,k);}}#includemain(){ char c;for(;(c=getchar())!='\n';)printf("%c ",c);}#include main(){ int i,c;for(i=0;(c=getchar())!='\n';i+=3)printf("%c ",i+c);}例 (f0r)梯形法求数值积分0yxaa+ha+iha+(i+1)hbf(x)循环的嵌套三种循环可互相嵌套,层数不限外层循环可包含两个以上内循环,但不能相互交叉嵌套循环的执行流程(1) while(){ ……while(){ ……}…...}(2) do{ ……do{ ……}while( );…...}while( );(3) while(){ ……do{ ……}while( );…….}(4) for( ; ;){ ……do{ ……}while();……while(){ ……}…...}嵌套循环的跳转禁止:从外层跳入内层跳入同层的另一循环向上跳转例 循环嵌套,输出九九表1234567891234567892468101214161836912151821242791827364554637281……………..ij/*ch5_5.c*/#include main(){ int i,j;for(i=1;i<10;i++)printf("%4d",i);printf("\n---------------------------------------\n");for(i=1;i<10;i++)for(j=1;j<10;j++)printf((j==9) "%4d\n":"%4d",i*j);}i<10printf假(0)真(非0)i=1j++j=1j<10真(非0)假(0)i++for(i=1;i<10;i++)for(j=1;j<10;j++)printf((j==9) "%4d\n":"%4d",i*j);外循环内循环辅助控制语句break语句功能:在循环语句和switch语句中,终止并跳出循环体或开关体说明:break只能终止并跳出最近一层的结构break不能用于循环语句和switch语句之外的任何其它语句之中expr……break;……假(0)真(非0)whiledo……break;…...expr假(0)真(非0)whileexpr2……break;…...假(0)真(非0)forexpr1expr3switchexpr语句组1break;语句组2break;语句组nbreak;语句组break;…...const 1const 2const ndefaultcase例 break举例:输出圆面积,面积大于100时停止#define PI 3.14159main(){int r;float area;for(r=1;r<=10;r++){ area=PI*r*r;if(area>100)break;printf("r=%d,area=%.2f\n",r,area);}}例 break举例:小写字母转换成大写字母,直至输入非字母字符#include main(){int i,j;char c;while(1){ c=getchar();if(c>='a' && c<='z')putchar(c-'a'+'A');elsebreak;}}continue语句功能:结束本次循环,跳过循环体中尚未执行的语句,进行下一次是否执行循环体的判断仅用于循环语句中expr……continue;……假(0)真(非0)while真(非0)do……continue;…...expr假(0)whileexpr2……continue;…...假(0)真(非0)forexpr1expr3例 求输入的十个整数中正数的个数及其平均值/*ch5_12.c*/#include main(){ int i,num=0,a;float sum=0;for(i=0;i<10;i++){ scanf("%d",&a);if(a<=0) continue;num++;sum+=a;}printf("%d plus integer's sum :%6.0f\n",num,sum);printf("Mean value:%6.2f\n",sum/num);}程序举例t=1,pi=0,n=1.0,s=1当|t| 1e-6pi=pi+tn=n+2s=-st=s/npi=pi*4输出pi分子:1,-1,1,-1…分母:1,3,5,7,...例 求Fibonacci数列:1,1,2,3,5,8,……的前40个数f1=1,f2=1for i=1 to 20输出f1,f2f1=f1+f2f2=f2+f11534233159710946750255142293524578241578171855377258417711121393832040570288739088169213896104181286571964181346269922746563245986321144987676546368317811217830914930352102334155例 译密码例如 Hello,world!译成密码:Lipps,asvph! 展开更多...... 收起↑ 资源列表 ch01.ppt ch021.ppt ch03.ppt ch031.ppt ch04.ppt ch041.ppt ch04_06.ppt ch05.ppt ch06.ppt ch07.ppt ch071.ppt ch08.ppt ch081.ppt ch09.ppt ch091.ppt ch10.ppt ch101.ppt ch11.ppt ch111.ppt ch12.ppt ch121.ppt ch13.ppt ch131.ppt c_3_4.ppt c_5.ppt c_6.ppt c_7.ppt c程序设计第一、二章.ppt 目录.doc