C程序设计全套课件及上机辅导资料

文档属性

名称 C程序设计全套课件及上机辅导资料
格式 zip
文件大小 1.4MB
资源类型 教案
版本资源 通用版
科目 信息技术(信息科技)
更新时间 2007-12-02 22:14:00

文档简介

(共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_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;
num_pointer=#
*num_pointer=3;
[Return]
10.2 指针变量的定义与应用
10.2.1 指针变量的定义与相关运算
[案例9.1] 指针变量的定义与相关运算示例。
main() { int num_int=12, *p_int; float num_f=3.14, *p_f; char num_ch=’p’, *p_ch; p_int=&num_int; p_f=&num_f; p_ch=&num_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=12
num_f=3.14, *p_f=3.14
num_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个整数,按升序(从小到大排序)输出。


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 )
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=6
min=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],要求实参为指针变量。
void exchange(int *pointer1, int *pointer2) { int temp; temp=*pointer1, *pointer1=*pointer2, *pointer2=temp; }

main()
{ int num1,num2;

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 )
exchange(num1_p, num2_p);

printf(“min=%d, max=%d\n”, num1, num2);
}
程序运行情况:
Input the first number:9←┘
Input the second number:6←┘
num1=9, num2=6
min=6, max=9
调用函数exchange()之前、之时、结束时和结束后的情况,如图9-5所示。
形参指针变量pointer1(指向变量num1)和pointer2(指向变量num2),在函数调用开始时才分配存储空间,函数调用结束后立即被释放。
虽然被调用函数不能改变实参指针变量的值,但可以改变它们所指向的变量的值。
总结:为了利用被调用函数改变的变量值,应该使用指针(或指针变量)作函数实参。其机制为:在执行被调用函数时,使形参指针变量所指向的变量的值发生变化;函数调用结束后,通过不变的实参指针(或实参指针变量)将变化的值保留下来。
[案例9.4] 输入3个整数,按降序(从大到小的顺序)输出。要求使用变量的指针作函数调用的实参来实现。






void exchange(int *pointer1, int *pointer2)
{ int temp;
temp=*pointer1, *pointer1=*pointer2, *pointer2=temp;
}

main()
{ int num1,num2,num3;

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 )
exchange( &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] 使用指向数组的指针变量来引用数组元素。


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;
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) 使用行指针


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)使用列指针


main()
{ int array[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int *pointer, row, col;
pointer=array[0];
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] 动态数组的实现。


#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);
}

printf(“Input %d elements: ”, num);
for (i=0; i
printf(“%d elements are: ”, num);
for (i=0; iprintf(“\b ”);
free(array);
}
程序运行情况:
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] 使用字符指针变量表示和引用字符串。

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]。


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] 用函数调用方式,实现字符串的复制。






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个人,找出其中至少有一项成绩不合格者。要求使用指针函数实现。
int *seek( int (*pnt_row)[3] )
{ int i=0, *pnt_col;
pnt_col=*(pnt_row+1);
for(; i<3; i++)
if(*(*pnt_row+i)<60)
{ pnt_col=*pnt_row;
break;
}
return(pnt_col);
}

main()
{ int grade[3][3]={{55,65,75},{65,75,85},{75,80,90}};
int i,j,*pointer;
for(i=0; i<3; i++)
{ pointer=seek(grade+i);
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] 有若干计算机图书,请按字母顺序,从小到大输出书名。解题要求:使用排序函数完成排序,在主函数中进行输入输出。





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()
{ char *name[5]={“BASIC”,”FORTRAN”,”PASCAL”,”C”,”FoxBASE”};
int i=0;
sort(name,5);

for(; i<5; i++) printf(“%s\n”,name[i]);
}
程序运行结果:
BASIC
C
FORTRAN
FoxBASE
PASCAL
程序说明:
(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 +|- <被处理的文件名>,其中“+”为加密,“-”为解密。


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)(); (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-1
prod=prod*n
T
F
n<=10
prod=1; n=1
n++
循环初始化
循环继续条件
循环体
循环控制变量:自增
无条件跳转
#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语
句结束,接着执行后面的
语句。
语句
T
F
表达式
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语
句结束,接着执行其后的语句。
语句
T
F
表达式
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”,则循环体一次都不被执行。
语句
T
F
表达式
语句
T
F
表达式
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的具体值;
④ 返回第②步
循环体语句
T
F
表达式2
求解表达式1
求解表达式3
for 循环控制语句:例
例:编程计算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

T
F
表达式2

while后的语句
T
F
break
break 语句和continue 语句的区别
continue语句: 结束本次循环的执行
while(表达式1)
{

if(表达式2)
continue;

}
F
表达式1

T
F
表达式2

while后的语句
T
continue
break 语句和continue 语句的区别
F
表达式1

T
F
表达式2

while后的语句
T
continue
表达式1

T
F
表达式2

while后的语句
T
F
break
break 语句:例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循环控制语句实现:
循环继续条件
循环变量变化步长=2
continue 语句:例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语句实现:
如果不是偶数,就开始下一次循环
循环变量变化步长=1
9 循环结构程序举例
程序举例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=1
f2=1
f3=f2+f1
f4=f3+f2
f5=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 + f1
f3 =
f2
f1
(f3)
(f2)
+
f4 = f3 + f2
(f4)
f3 =
f2
f1
+
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);
else
printf(“%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);
else
printf(“%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);
else
printf(“%d 不是素数.\n” , num);
}
程序举例6-10 :实现方案一
根据循环结束后,循环变
量的当前值判断循环是否
是正常结束的
#include
#define TRUE 1
#define FALSE 0
void 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 0
void 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 0
void 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 0
void 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 0
void 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 4
struct 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 4
struct student_type
{ char name[10];
int num;
int age;
char addr[15];
}stud[SIZE];
下列程序从“stu_list”文件读入数据然后输出到屏幕。
[例3b]:读入二进制文件转存
#include "stdio.h"
#define SIZE 4
struct 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);
i=getw(fp);
fputs(“China”,fp);
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);
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 结构类型名 {数据类型 数据项1; 数据类型 数据项2; …… …… 数据类型 数据项n; };
[案例10.1] 定义一个反映学生基本情况的结构类型,用以存储学生的相关信息。
struct date
{int year;
int month;
int day;
};
struct std_info
{char no[7];
char name[9];
char sex[3];
struct date birthday;
};
struct score
{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,用于存储和显示一个学生的基本情况。
#include"struct.h" 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: 000102
Name: 张三
Sex: 男
Birthday:1980-9-20
1.结构变量的引用规则
对于结构变量,要通过成员运算符“.”,逐个访问其成员,且访问的格式为:
结构变量.成员
例如,案例中的student.no,引用结构变量student中的no成员;student.name引用结构变量student中的name成员,等等。
如果某成员本身又是一个结构类型,则只能通过多级的分量运算,对最低一级的成员进行引用。
此时的引用格式扩展为:
结构变量.成员.子成员.….最低1级子成员
例如,引用结构变量student中的birthday成员的格式分别为:
student.birthday.year
student.birthday.month
student.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,用于存储和显示三个学生的基本情况。
#include"struct.h" struct std_info student[3]={{“000102”,“张三”,“男”,{1980,9,20}}, {“000105”,“李四”,“男”,{1980,8,15}}, {“000112”,“王五”,“女”,{1980,3,10}} };

main()
{ int i;

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 Birthday
000102 张三 男 1980-9-20
000105 李四 男 1980-8-15
000112 王五 女 1980-3-10
与结构变量的定义相似,结构数组的定义也分直接定义和间接定义两种方法,只需说明为数组即可。
与普通数组一样,结构数组也可在定义时进行初始化。初始化的格式为:
结构数组[n]={{初值表1},{初值表2},...,{初值表n}}
例如,本案例中的结构数组student[3]。
[Return]
11.4 指向结构类型数据的指针
结构变量在内存中的起始地址称为结构变量的指针。
11.4.1 指向结构变量的指针 [案例10.4] 使用指向结构变量的指针来访问结构变量的各个成员。 #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).成员
注意:在格式(1)中,分量运算符左侧的运算对象,只能是结构变量,;而在格式(2)中,指向运算符左侧的运算对象,只能是指向结构变量(或结构数组)的指针变量,否则都出错。
思考题:如果要求从键盘上输入结构变量student的各成员数据,如何修改程序?
11.4.2 指向结构数组的指针
[案例10.5] 使用指向结构数组的指针来访问结构数组。

#include"struct.h"

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(),通过主函数调用来实现显示。

#include"struct.h"

struct std_info student[3]={{"000102","张三","男",{1980,5,20}},
{"000105","李四","男",{1980,8,15}},
{“000112”,“王五”,“女”,{1980,3,10}} };

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,实现将新申请的结点,插入到链表尾,使之成为新的尾结点。

#define NULL 0
#define LEN sizeof(struct grade)

struct grade
{ char no[7];
int score;
struct grade *next;
};



struct grade *create( void )
{ struct grade *head=NULL, *new, *tail;
int count=0;
for( ; ; )
{ new=(struct grade *)malloc(LEN);

printf("Input the number of student No.%d(6 bytes): ", count+1);
scanf("%6s", new->no);
if(strcmp(new->no,"000000")==0)
{ free(new);
break;
}
printf("Input the score of the student No.%d: ", count+1);
scanf("%d", &new->score);
count++;

new->next=NULL;

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个结点之后。




struct grade *insert(struct grade *head, struct grade *new, int i)
{ struct grade *pointer;

if(head==NULL) head=new, new->next=NULL;
else
if(i==0) new->next=head, head=new;
else
{ pointer=head;

for(; pointer!=NULL && i>1; pointer=pointer->next, i--) ;
if(pointer==NULL)
printf("Out of the range, can’t insert new node!\n");
else
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];
};
name
num
sex
age
score
addr
2字节
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 student
STUDENT
{ 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;
num
name
birthday
month
day
year
例 struct student
{ int num;
char name[20];
struct date
{ int month;
int day;
int year;
}birthday;
}stu;
num
name
birthday
month
day
year
11.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;
num
name
birthday
month
day
year
stu1.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];
num
name
sex
age
num
name
sex
age
stu[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);
}
name
count
Li
Zhang
Wang
0
0
0
11.6 结构体和指针
指向结构体变量的指针
定义形式:struct 结构体名 *结构体指针名;
例 struct student *p;
使用结构体指针变量引用成员形式
存放结构体变量在内存的起始地址
num
name
sex
age
stu
p
struct 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=10
struct 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);
}
num
name
sex
age
stu[0]
p
stu[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");
}
arg
a :27
b: 3
c :30
(main)
(func)
parm
a :27
b: 3
c :30
copy
arg
a :27
b: 3
c :30
(main)
(func)
parm
a :18
b: 5
c :90
arg
a :27
b: 3
c :30
(main)
arg
a :27
b: 3
c :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");
}
arg
a :18
b: 5
c :90
(main)
arg
a :27
b: 3
c :30
(main)
例 用结构体指针变量作函数参数
arg
a :27
b: 3
c :30
(main)
(func)
parm
****
arg
a :18
b: 5
c :90
(main)
(func)
parm
****
11.8 共用体
构造数据类型,也叫联合体
用途:使几个不同类型的变量共占一段内存(相互覆盖)
共用体类型定义
定义形式:
union 共用体名
{
类型标识符 成员名;
类型标识符 成员名;
…………….
};
例 union data
{ int i;
char ch;
float f;
};
f
ch
i
类型定义不分配内存
形式一:
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;
共用体变量的定义
f
ch
i
f
ch
i
a
b
共用体变量定义分配内存,
长度=最长成员所占字节数
共用体变量任何时刻
只有一个成员存在
共用体变量引用
引用方式:
例 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.f
p->i p->ch p->f
(*p).i (*p).ch (*p).f
d[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
低字节
高字节
01000001
01100001
ch[0]
ch[1]
运行结果:
i=60501
ch0=101,ch1=141
ch0=A,ch1=a
main()
{ union int_char
{ int i;
char ch[2];
}x;
x.i=24897;
printf("i=%o\n",x.i);
printf("ch0=%o,ch1=%o\n
ch0=%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;
a
ch
k
b
ch
k
变量的各成员同时存在
任一时刻只有一个成员存在
联系: 两者可相互嵌套
例 结构体中嵌套共用体
name
num
sex
job
class
position
Li
Wang
1011
2086
F
M
S
T
501
prof
循环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
低字节
高字节
00110100
00010010
low
high
0x1234
00010010 11111111
低字节
高字节
11111111
00010010
low
high
0x12ff
struct w_tag
{ char low;
char high;
};
union u_tag
{ struct w_tag byte_acc;
int word_acc;
}u_acc;
word_acc
byte_acc.low
byte_acc.high
u_acc
11.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趟后,整个排序过程结束。


#include "stdio.h"
#define NUM 10
main()
{ int data[NUM];
int i,j,temp;
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%。 #include “stdio.h” main() { int i=1,j; char con_key=‘\x20’; 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;
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();
}
}
[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维数组各元素赋值,并输出全部元素的值。


#define Row 2
#define Col 3
#include "stdio.h"
main()
{ int i, j, array[Row][Col];
for(i=0; ifor(j=0; j{printf("please input array[%2d][%2d]:",i,j);
scanf("%d",&array[i][j]);
}
printf("\n");

for(i=0;i7.2.1 2维数组的定义
{ for(j=0;jprintf("%d\t",array[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门课程,已知所有学生的各科成绩,编程:分别求每个学生的平均成绩和每门课程的平均成绩。


#define NUM_std 5
#define NUM_course 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];
score[NUM_std][j] += score[i][j];
}
score[i][NUM_course] /= NUM_course;
}
for(j=0;jscore[NUM_std][j] /= NUM_std;
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");
}

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]从键盘输入一个字符串,回车键结束,并将字符串在屏幕上输出。

main()
{int i;
static char str[80];
clrscr();
for(i=0;i<80;i++)
{ str[i]=getch();
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] 字符数组的整体输入与输出。


main()
{ int i;
char name[5][9]={"张三山", "李四季", "王五魁", "刘六顺", "赵七巧"};
for(i=0;i<5;i++)
printf("\n%s\t",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函数的应用。


#include "stdio.h"
main()
{char pass_str[80];
int i=0;

while(1)
{clrscr();
printf("请输入密码\n");
gets(pass_str);
if(strcmp(pass_str,“password”)!=0)
printf("口令错误,按任意键继续");
else
break;
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
-0
12.2 位运算符
1)除“~”外,均为二目运算符。
2)运算量只能是整型和字符型。
1. 按位与
0&0=0 0&1=0 1&0=0 1&1=1
2. 按位或
0|0=0 0|1=1 1|0=1 1|1=1
3. 按位异或(XOR)
0^0=0 0^1=1 1^0=1 1^1=0
4. 按位取反
用于对一个二进制数按位取反。它的优先级别高于算术运算符、关系运算符、逻辑运算符和其它位运算符。
5. 左移
用于将一个数的二进制位全部左移若干位。低位补0。
a=a<<2 (左移两位)
左移n位相当于乘以2n。
6. 右移
用于将一个数的二进制位全部右移若干位。无符号数,高位补0;有符号数,根据系统而定,Turbo C 采用“算术右移”,即移入最高位的是1。
a=a>>2 (右移两位)
右移n位相当于除以2n。
7. 扩展的赋值运算符:位运算符与赋值运算符的结合。
&= |= >>= <<= ^=
a&=b 相当于:a=a&b
a<<=2 相当于:a=a<<2
8. 不同长度的数据进行位运算
按右端对齐,正数和无符号数补0,负数补满1。
12.3 位运算举例
步骤分析:
1. 使a右移4位,即 a>>4
2. 设置一个低4位全1,其余为0的数,即 ~(~0<<4)(见下图)
0:0000…00 0000
~0:1111…11 1111
~0<<4:1111… 11 0000
~(~0<<4):0000 …00 1111
3. 将上面两数相与,即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 10
int 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]
数组名包含数组的首地址信息
0x1000
0x1002
0x1004
0x1012
=&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]
0x1000
0x1002
0x1004
0x1012
=&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]
0x1000
0x1002
0x1004
0x1012
=&a[0]
=&a[1]
=&a[2]
=&a[9]
如: int a[10];
数组名 a
&a[0]
0x1000
a
=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) 返回2
sizeof(float) 返回4
int 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;
fnum + 0;
fnum + 1;
fnum + 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;
}
运行结果:
9
8
7
6
5
4
3
2
1
0
一维数组例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:题目及分析
分析:
8
a[0]
a[1]
a[2]
a[4]
a[3]
6
5
4
1
算法:数组从首尾向中间,对应位置上的元素交
换值。直到交换到中间的元素为止。
1
a[0]
a[1]
a[2]
a[4]
a[3]
4
5
6
8
#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];
a
a[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];
a
a[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]
0x1000
0x1006
0x100C
=a[0] =&a[0][0]
=a[1] =&a[1][0]
=a[2] =&a[2][0]
比如:int a[3][3];
a
数组名 a
a[0]
&a[0][0]
0x1000
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]
元素 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;

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
字符串输出函数 puts
1. 原型:
int puts(const char *str);
2. 参数:常指针类型的数据,可以是字符串常量,
字符数组名或字符指针变量名。
3. 功能:从给定的地址开始,输出一个以 ‘\0’字
符结尾的字符串str到显示器上,最后再输出一个
回车换行字符。
4. 用法:puts(字符串常量或字符数组名或字符
指针变量名);
5. 返回值:一个非负整数。绝大多数情况下不使
用该返回值。
字符串输出函数 puts
char 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”);
字符串输入函数 gets
1. 原型:
char *gets(char *str);
2. 参数:指针类型的数据,可以是字符数组名
或指针变量名。
3. 功能:从键盘接收用户键入的以回车键结束
的字符序列,把它存放到由指定的地址(由 str
指定)开始的内存单元去。
4. 用法:gets(字符数组名或指针变量名);
5. 返回值:指向字符串str的指针。
字符串输入函数 gets
char 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 ++;
}
字符串拷贝函数 strcpy
1. 原型:
char *strcpy(char *dest, const char *src);
2. 参数:两个。
第一个:目标字符串,指针类型的数据,可以是字
符数组名或指针变量名。
第二个:源字符串,常指针类型的数据,可以是字
符串常量或字符数组名或指针变量名。
字符串拷贝函数 strcpy
1. 原型:
char *strcpy(char *dest, const char *src);
3. 功能:将源字符串src的全部字符拷贝到目标字符
串dest中。目标字符串中的原有内容被覆盖。
4. 用法:strcpy(字符数组名或字符指针变量名, 字
符串常量或字符数组名或指针变量名);
5. 返回值:指向目标字符串dest的指针。
字符串拷贝函数 strcpy
char 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

字符串拷贝的实际过程:
0
char 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,而不能直接使用赋值运算符。
因为数组名是指针常量,而常量不可能被赋值,
所以是错误的。
字符串连接函数 strcat
1. 原型:
char *strcat(char *dest, const char *src);
2. 参数:两个。
第一个:目标字符串,指针类型的数据,可以是字
符数组名或指针变量名。
第二个:源字符串,常指针类型的数据,可以是字
符串常量或字符数组名或指针变量名。
3. 功能:将源字符串src中的全部字符连接到目标字
符串dest的末尾。目标字符串原有内容仍存在。
字符串连接函数 strcat
原型:
char *strcat(char *dest, const char *src);
4. 用法:strcat(字符数组名或字符指针变量名, 字符串常量或字符数组名或指针变量名);
5. 返回值:指向目标字符串dest的指针。
字符串连接函数 strcat
char 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); 等价于
字符串比较函数 strcmp
1. 原型:
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
字符串比较函数 strcmp
char 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”);
字符串长度函数 strlen
1. 原型:
unsigned int strlen(const char *str);
2. 参数:一个常指针类型的数据,可以是字符串常量
或字符数组名或字符指针变量名。
3. 功能:求指定字符串str的长度(str 中不包括结束
标志字符在内的字符的个数)。
4. 用法:strlen(字符串常量或字符数组名或字符指
针变量名);
5. 返回值:str中不包括结束标志字符在内的字符的
个数。
字符串长度函数 strlen
char NewName[80] = “WPS2000”;
unsigned int slen = 0;
slen = strlen(NewName);

slen = strlen(“Office 2001”);

字符串长度函数 strlen
自己用代码编写 strlen 函数的功能:
unsigned int slen = 0;
while (NewName[slen] != ‘\0’)
slen ++;

printf(“The length is %u”, slen);
字符串小写函数 strlwr
1. 原型:
char *strlwr(char *str);
2. 参数:一个指针类型的数据,可以是字符数组
名或指针变量名。
3. 功能:将指定字符串str中的大写字母(A-Z)
转换成对应的小写字母(a-z),其它字符保持不
变。
4. 用法:strlwr(字符数组名或指针变量名);
5. 返回值:指向字符串str的指针。
字符串小写函数 strlwr
char NewName[80] = “WPSOffice”;
strlwr(NewName);
puts(NewName);
strcpy(NewName, “OFFICE2001”);
strlwr(NewName);
puts(NewName);
自己用代码编写 strlwr 函数的功能:
字符串大写函数 strupr
1. 原型:
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);
strcpy(NewName, “Word2001”);
strupr(NewName);
puts(NewName);
课后及上机作业
1. 课本p186 习题7.1,7.2,7.6,7.14,7.15
2. 自己编写代码实现函数 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)int
16
-32768~32767
(signed)short
16
-32768~32767
(signed)long
32
-2147483648~2147483647
16
unsigned int
0~65535
32
0~4294967295
unsigned long
unsigned short
16
0~65535

float
32
3.4e-38~3.4e38

double
64
1.7e-308~1.7e308

char
8
-128~127

unsigned char
8
0~255
说明:数据类型所占字节数随机器硬件不同而不同,上表以IBM PC机为例:
3.3 常量与变量
标识符
定义:用来标识变量、常量、函数等的字符序列
组成:
只能由字母、数字、下划线组成,且第一个字母必须是字母或下划线
大小写敏感
不能使用关键字
长度:最长32个字符
命名原则:
见名知意
不宜混淆 如l与I, o与0
例:判断下列标识符号合法性
sum Sum M.D.John day Date 3days
student_name #33 lotus_1_2_3
char a>b _above $123
M.D.John
3days
#33
char
$123
a>b
一般用大写字母
是宏定义预处理命令,不是C语句
直接常量:
整型常量
实型常量
字符常量
字符串常量
如 #define PRICE 30
常量
定义:程序运行时其值不能改变的量(即常数)
分类:
符号常量:用标识符代表常量
定义格式: #define 符号常量 常量
例 符号常量举例(ch2_1.c)
#define PRICE 30
main()
{
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 = ( )10
0x123 = ( )10
0Xff = ( )10
83
291
255
问题:
0123 = ( )10
0x123 = ( )10
0Xff = ( )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 C
Isay:”How are you ”
\C Program\
Turbo ‘C’
字符常量与字符串常量不同
字符串常量
定义:用双引号(“”)括起来的字符序列
存储:每个字符串尾自动加一个 ‘\0’ 作为字符串结束标志
h e l l o \0
例 字符串“hello”在内存中
a
a \0
例 ‘a’
“a”
例 空串 “”
\0
例: char ch;
ch=“A”;
例: char ch;
ch=‘A’;
编译程序根据变量定义为其
分配指定字节的内存单元
…...
地址
int a=1, b=-3,c;
a
b
c
2字节
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;
变量的使用:先定义,后使用
例1
int student;
stadent=19; //Undefined symbol ‘statent’ in function main
例2
float 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;
double b;
b=111111.111;
例 a=‘D’;
x=‘A’+5;
s=‘!’+‘G’
没有字符串变量,用字符数组存放

#define PRICE 12.5
main()
{ 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=d
3.4 不同类型数据间的转换
隐式转换
什么情况下发生
运算转换------不同类型数据混合运算时
赋值转换------把一个值赋给与其类型不同的变量时
输出转换------输出时转换成指定的输出格式
函数调用转换------实参与形参类型不一致时转换
运算转换规则:不同类型数据运算时先自动转换成同一类型
double
float
long
unsigned
int
char,short


说明:
必定的转换
运算对象类型不同时转换
例 char ch;
int i;
float f;
double d;
ch/i + f*d - (f+i)
int
int
double
double
double
double
double
double
int
int
double
double
double
double
double
double
10+‘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 = -1
1%10 = 1
5%1 = 0
5.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 exp2
a+=3
a=a+3
x*=y+8
x=x*(y+8)
x%=3
x=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=5
a=(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*4
a=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语句中
例:

#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)!=c
a==ba=b>c //a=(b>c)
关系表达式的值:是逻辑值“真”或“假”,用1和0表示
例 int a=3,b=2,c=1,d,f;
a>b
(a>b)==c
b+cd=a>b
f=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 结果为
1
0
关系运算注意:
例 注意区分“=”与“==”
int a=0,b=1;
if(a=b)
printf(“a equal to b”);
else
printf(“a not equal to b”);
例 应避免对实数作相等或不等的判断
如 1.0/3.0*3.0==1.0 结果为
可改写为:fabs(1.0/3.0*3.0-1.0)<1e-6
0
a
b
!a
!b
a&&b
a||b








逻辑运算符和表达式
逻辑运算符
种类: ! && ||
逻辑运算真值表
C语言中,运算量: 0表示“假”,
非0表示“真”,
运算结果: 0表示“假”,
1表示“真”,
















! (2)
&& (11)
|| (12)


例 a<=x && x<=b
a>b&&x>y
a==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;
!a
a&&b
a||b
!a||b
4&&0||2
5>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);
else
printf(“%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 11
int型:
最大:32767
10 00 00 00 00 00 00 00
最小:-32768
11 11 11 11 11 11 11 11
unsigned int型:
最大:65535
00 00 00 00 00 00 00 00
最小:0
数制


表示
数码
特点
10 ,10 ,10 ,…
十进制数
0~9
10
逢十进一
二进制数
0~1
2
2 ,2 ,2 ,…
逢二进一
八进制数
0~7
8
8 ,8 ,8 ,…
逢八进一
十六进制数
0~9,A~F,a~f
16
16 ,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]
0
1
4
5
a[1]
a[2]
a[3]
a[4]
a[5]
2
3
a
编译时分配连续内存
内存字节数=数组维数*
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 10
main()
{ 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]
……...
1
1
f[19]
0
1
4
5
2
3
19
2
3
5
#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]
0
1
4
5
a[1]
a[2]
a[3]
a[4]
a[5]
2
3
a
输入n 个数给a[1] 到 a[n]
for j=1 to n-1
for i=1 to n-j
a[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 ]
k
j
i=1
13
49
一趟: 13 [38 65 97 76 49 27 ]
i=2
27
38
二趟: 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 ]
k
k
k
k
j
j
j
j
j
j
j
j
j
j
Ch5_201.c
输入n 个数给a[1] 到 a[n]
for i=1 to n-1
for j=i+1 to n
a[j]

k=j
输出a[1] 到 a[n]
k=i
a[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]
0
1
4
5
2
3
a[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]
0
1
2
3
4
5
6
7
………...
20
21
22
23
c[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];
2016
17
2018
19
2020
21
2022
23
2008
9
2010
11
2012
13
2014
15
2000
1
2002
3
2004
5
20006
7
a[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]
行名
0
1
4
5
2
3
a[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]
6
7
10
11
8
9
a[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]
1
2
3
4
5
6
全部初始化
按元素排列顺序初始化
例 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]
1
2
0
4
0
0
部分初始化
例 int a[][3]={{1},{4,5}};
a[0][0]
a[0][1]
a[0][2]
a[1][0]
a[1][1]
a[1][2]
1
0
0
4
5
0
第一维长度省略初始化
例 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]
1
2
3
4
5
6
全部初始化
例 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]
1
2
4
0
0
0
部分初始化
例 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]
1
2
3
4
5
0
第一维长度省略初始化
程序举例
例 将二维数组行列元素互换,存到另一个数组中
a=
1 2 3
4 5 6
b=
1 4
2 5
3 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 2
for j=0 to 3
a[i][j]>max


max=a[i][j]
row=i
colum=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 6
15 7 9
8 23 3
2 5 17
12 4 6 22
15 7 9 31
8 23 3 34
2 5 17 24
37 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]
H
e
l
l
o
逐个字符赋值
ch[1]
ch[2]
ch[3]
ch[4]
有问题!
例 char ch[5]={‘H’,’e’,’l’,’l’,’o’};
ch[0]
H
e
l
l
o
逐个字符赋值
ch[1]
ch[2]
ch[3]
ch[4]
例 char ch[5]={‘B’,’o’,’y’};
ch[0]
B
o
y
\0
\0
逐个字符赋值
ch[1]
ch[2]
ch[3]
ch[4]
例 char ch[5]=“Boy”;
ch[0]
B
o
y
\0
\0
用字符串常量
ch[1]
ch[2]
ch[3]
ch[4]
例 char ch[6]={“Hello”};
char ch[6]=“Hello”;
char ch[]=“Hello”;
用字符串常量
ch[0]
H
e
l
l
o
ch[1]
ch[2]
ch[3]
ch[4]
\0
ch[5]
例 char diamond[][5]={{'.', '.','*'},{'.','*','.','*'},
{'*', '.', '.', '.' ,'*'},{'.','*', '.','*'},{'.', '.','*'}};
二维字符数组初始化
.
.
*
\0
\0
.
*
.
*
\0
*
.
.
.
*
.
*
.
*
\0
.
.
*
\0
\0
diamond[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]
A
p
p
l
e
\0
\0
O
r
a
n
g
e
\0
G
r
a
p
e
\0
\0
P
e
a
r
\0
\0
\0
P
e
a
c
h
\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");
}
I
a
m
a
b
o
y
0
1
2
3
4
5
6
7
8
9
字符串
字符串及其结束标志
无字符串变量,用字符数组处理字符串
字符串结束标志:‘\0’
例 “hello”共5个字符,在内存占6个字节 字符串长度5
h e l l o \0
104 101 108 108 111 0
内存存放字符ASCII码
字符串的输入输出
逐个字符I/O: %c
整个字符串I/O: %s
例 用%c
main()
{ 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]);
}
例 用%s
main()
{ 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 o
0
2
3
1
4
结果:Hello
用“%s”输出时,遇‘\0’结束
main()
{
char a[]={'h','e','l','\0','l','o','\0'};
printf("%s",a);
}

输出:hel
h 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 \0
h e l l \0
h e l l o
输入字符串长度<数组维数
例 字符串输入举例
H o w \0
a r e \0
y 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=How
b=are
c=you
输入:How are you
输出:a=How
scanf中%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++
T
r
b
o
C
+
+
0
1
2
3
4
5
6
7
8
9
u
\0
24
…….
T
r
b
o
0
1
2
3
4
5
6
7
8
9
u
\0
24
…….
…….
T
r
b
o
\0
0
1
2
3
4
5
6
7
8
9
u
24
…….
…...
字符串比较函数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=18
Ch6_23.c
应用举例
例 输入一行字符,统计其中有多少个单词
输入一字符串给 string
i=0 num=0 word=0
当((c=string[i])!=‘\0’)
c=空格




word=0
word=1
num=num+1
i=i+1
输出:num
word==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

1
1

1

0
1

0

1
2

1

1
2

1

0
2

0

1
3

1

0
3

0

1
4

1

1
4

1

1
4

1

1
4
例 输入:I am a boy.
当前字符
是否空格
word原值
新单词开始否
word新值
num值



I
a
m
a
b
o
y
.
例 有三个字符串,找出其中最大者
H o w \0
H e l l o \0
H i g h \0
str[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]);
else
strcpy(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 79
92
85
68
54
88
98
45
75
61
79
a[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 75
54 88 98 45
61 79 81 40
张三
李四
王二
数学
化学
英语
物理
二维数组
例 比较 int a[2][3]={{5,6},{7,8}};
与 int a[2][3]={5,6,7,8};
5 6 0
7 8 0
5 6 7
8 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 o
0
2
3
1
4
h e l l o \0
0
2
3
1
4
5(共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 1
main()
{ ……..
}
#undef YES
#define YES 0
max()
{……..
}
YES原作用域
YES新作用域
宏定义可嵌套,不能递归
例 #define MAX MAX+10 ( )
引号中的内容与宏名相同也不置换
例 #define PI 3.14159
printf(“2*PI=%f\n”,PI*2);
宏展开:printf(“2*PI=%f\n”,3.14159*2);
宏定义中使用必要的括号()
例 #define WIDTH 80
#define LENGTH WIDTH+40
var=LENGTH*2;
宏展开:var= 80+40 *2;
( )
( )
例 #define WIDTH 80
#define LENGTH WIDTH+40
var=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*x
x=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.c
file2.c
file1.c
file2.c
A
B
A
处理过程:预编译时,用被包含文件的内容取代该预处理命令,再对“包含”后的文件作一个源文件编译
<> 直接按标准目录搜索
“” 先在当前目录搜索,再搜索标准目录
可指定路径
被包含文件内容
源文件(*.c)
头文件(*.h)
宏定义
数据结构定义
函数说明等
文件包含可嵌套
#include “file2.c”
file1.c
A
file3.c
C
#include “file3.c”
file2.c
B
file1.c
A
file3.c
file2.c
例 文件包含举例

#define sqr(x) ((x)*(x))
#define cube(x) ((x)*(x)*(x))
#define quad(x) ((x)*(x)*(x)*(x))

#include
#include "d:\fengyi\bkc\powers.h"
#define MAX_POWER 10
void 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执行完一条指令后,取出下一条指令。并如此继续下去。
转跳指令另行设置指令地址,指定下一条指令的位置。
人命令计算机去执行某程序,计算机会按照程序规定,一丝不苟地执行,直至程序结束。
机器语言和程序
机器语言就是机器指令形成的语言;
二进制编码,机器可直接执行;
00000001000000001000
00000001000100001010
00000101000000000001
00000001000100001100
00000100000000000001
00000010000000001110
数据装入寄存器0
数据装入寄存器1
寄存器0与1的数据乘
数据装入寄存器1
寄存器0与1的数据加
保存寄存器0里的数据
难写难读,人使用不便,程序开发效率极低。
汇编语言和程序
汇编语言:助记符号形式,指令与机器指令对应,便于人识别和使用。
load 0 a
load 1 b
mult 0 1
load 1 c
add 0 1
save 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 1
0 1 0 1 0 1 1 0
0 0 0 1 1 0 0 0
……
1 1 0 1 0 1 1 0
1 0 0 1 0 0 1 0
1
“位”:bit
最小的电子线路单元,
只能保存一位二进制数
即一个 0 或一个 1 。
因此一“位”单元可以保
存的最大的十进制数据
是 1 。
内存储器的组织形式
1 0 1 1 0 0 1 1
0 1 0 1 0 1 1 0
0 0 0 1 1 0 0 0
……
1 1 0 1 0 1 1 0
1 0 0 1 0 0 1 0
1
“字节”:byte ,由若
干个“位”组成。微机中,
1字节=8位。
最基本的数据存储单元,
即数据在内存中的存储
是以“字节”为基本单位
的。任何数据总是占据
整数个“字节”单元 。
1 0 1 1 0 0 1 1
内存储器的地址和内容
1 0 1 1 0 0 1 1
0 1 0 1 0 1 1 0
0 0 0 1 1 0 0 0
……
1 1 0 1 0 1 1 0
1 0 0 1 0 0 1 0
计算机怎么知道到
哪一个内存单元去
找需要的数据?

内存储器的地址和内容
1 0 1 1 0 0 1 1
0 1 0 1 0 1 1 0
0 0 0 1 1 0 0 0
……
1 1 0 1 0 1 1 0
1 0 0 1 0 0 1 0
0000
0001
0002
0003
0004
……
0998
0999
1000
内存储器从第一个
字节开始到最后一
个字节为止,每一
个字节单元依次有
一个“编号” ,这
个编号就叫内存单
元的“地址”。
内存储器的地址和内容
1 0 1 1 0 0 1 1
0 1 0 1 0 1 1 0
0 0 0 1 1 0 0 0
……
1 1 0 1 0 1 1 0
1 0 0 1 0 0 1 0
← 0000
← 0001
← 0002
← 0003
← 0004
……
← 0998
← 0999
← 1000
该地址所指向的
内存单元中存放
的数据就叫内存
单元的“内容”。
内存储器的地址和内容
1 0 1 1 0 0 1 1
0 1 0 1 0 1 1 0
0 0 0 1 1 0 0 0
……
1 1 0 1 0 1 1 0
1 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 = 1010101
Ns 1 0 1 0 1 0 1
定点小数
符号位
数值部分
小数点
N = 0.1010101
Ns 1 0 1 0 1 0 1
定点小数:
小数点默认为在一个二进制数最高位(符号
位)的后面
定点数
可见,不管是定点整数还是定点小数,
由于它们的小数点位置固定,因此在运
算时可以不考虑小数点的位置,只要区
分是定点整数还是定点小数就可以了。
浮点数
在计算机中,既有整数部分又有小数部分的数
称为浮点数
浮点数
P = ± S × 2 ± N
P的尾数 (定点小数)
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 ALGOL
1963 CPL
1967 BCPL ( Basic Combind Programming Language)
1969 UNIX(汇编语言)
1970 B (Ken Thompson &Dennis Ritchie)
1972 C
1980 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()
{

int a , b , sum ;
a=123; b=456;
sum=a+b;
printf(“sum=%d\n”, sum);
return(0);
}
C程序例2
同样必须有
main主函数。
它是程序的执
行起点。
#include
int main()
{

int a , b , sum ;
a=123; b=456;
sum=a+b;
printf(“sum=%d\n”, sum);
return(0);
}
C程序例2

间的所有内容
称为“注释”。
注释不被编译。
#include
int main()
{

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()
{

int a , b , sum ;
a=123; b=456;
sum=a+b;
printf(“sum=%d\n”, sum);
return(0);
}
C程序例2
=是C语言的
赋值运算符,由
它构成赋值表达
式。此处把常量
的值赋给变量。
#include
int main()
{

int a , b , sum ;
a=123; b=456;
sum=a+b;
printf(“sum=%d\n”, sum);
return(0);
}
C程序例2
sum=a+b;
也是赋值语句:
把一个加法表达
式的值赋给变量。
#include
int main()
{

int a , b , sum ;
a=123; b=456;
sum=a+b;
printf(“sum=%d\n”, sum);
return(0);
}
C程序例2
printf函数中
的%d是格式字
符,用于格式输
出数据。
#include
int main()
{

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=200
C程序的基本结构和特点1
1. 一个C程序是由函数构成的。C语言
又被称作“函数式语言”
函数可能是系统库函数或用户自定义
函数。
  使用函数构成程序可以容易实现结构
化程序设计方法。
int main()
{
……
prod=func(…);
printf(……);
return(0);
}
int func( … , … )
{
……
}
C程序的基本结构和特点1
主函数 main
自定义函数 func
调用系统 库函数 printf
调用用户 自定义函数 func
C程序的基本结构和特点2
2. 一个C程序中必须有一个main函数,
而且只能有一个。无论该主函数位于程序
的何处,程序都由它开始执行。
int func( … , … )
{
……
}
int main()
{
……
prod=func(…);
printf(……);
return(0);
}
C程序的基本结构和特点2
主函数main在自定义函数func之后
自定义函数func 在主函数前面
C程序的基本结构和特点3
3. 一个函数由两部分组成:函数原型和
函数体。
函数原型确定函数名,形参数量和类
型,函数返回值及类型;
函数体包含在一对{ }中,由若干
语句组成以实现函数功能。
int func(int a )
{
……
}
int main()
{
……
prod=func(…);
printf(……);
return(0);
}
C程序的基本结构和特点3
自定义函数的原型
函数名:func 形参:一个,int 型 返回值:有,int 型
主函数的原型
函数名:main 形参:没有 返回值:有, int 型
C程序的基本结构和特点4
4. 每个C语句必须以分号结束。
C语句的种类有
变量定义语句
函数调用语句
表达式语句
控制语句
int main()
{

int a, b, prod;
a=10; b=20;
prod=func(a , b);
printf(“prod=%d”, prod);
return(0);
}
C程序的基本结构和特点4
变量 定义 语句
控制 语句
表达式语句
函数调用语句
C程序的基本结构和特点5
5. 可以用(块注释符)在程
序中任何地方作注释。注释的内容不会
被编译。必要的注释可以大大增强程序
的可读性。
int main()
{

int a, b, prod;

printf(“prod=%d”, prod);
return(0);
}
C程序的基本结构和特点5
注释在一行上
注释占用多行也可以
这注释也行
C程序的基本结构和特点6
6. C程序的书写格式很自由,但是为了
保证程序的可读性和易维护性,还是要
保持良好的书写规范和编程习惯。
int main()
{

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
编译
有错?
链接
执行
结果正确?
结束
Yes
Yes
No
No
目标程序 文件 *.obj
可执行程序 文件 *.exe
调试
No
Nucleus 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.14159
int 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);
}
符号常量的实现
这个字符串将要在预处理时去替换符号PI
PI会被指定的字符串3.14159代替
符号常量的好处
使用符号常量的好处是:
从标识符名可以清楚地看出该常量的
含义,做到“见名知意”
在需要修改常量的值时可以做到“一
改全改”,容易调整
符号常量的注意事项
必须注意的是:符号常量不是变量,
不能被赋值。
#define PI 3.14159
int main()
{
……
PI = 100;
……
}
这样的赋值是错误的
符号常量的注意事项
必须注意的是:如果指定的符号出现
在双引号中,就不被替换。
#define PI 3.14159
int main()
{
……
printf(“PI = %f”, PI);
……
}
这个PI不会被替换
这个PI会被替换
变量
程序运行期间,值可以被改变的量称为变量
C语言允许把数据存放在变量中,每一个
变量都由一个变量名来标识。
每个变量都必须具有一个类型。
每个变量根据它的类型,在内存中占据一
定数目的字节单元,该变量的值就存放在
这些内存单元中。
变量
在编译系统对程序进行编译时,根据每
一个变量的类型,给它分配一定数目的
字节单元,同时,系统内部维护着变量
名和它所占内存单元地地址的对应信息。
变量
123
prod
变量名 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 (全部小写难以辨认)
total
totals (名字太相似)
变量的定义和使用
C语言中规定,变量必须“先定义,后使用”
变量定义具有三个目的:
⑴ 定义变量名;
⑵ 定义变量类型;
⑶ 向程序员描述该变量。
系统将根据变量的定义为其分配相应大小的
内存单元。
变量的定义和使用
int radius;
关键字 int 告诉编译系统我要定义一个整型变量,用它来保存一个整型的数值。
这是变量的名字。以后的程序中就用这个名字来访问这个变量。
注释用来解释这个变量,它是给程序员的信息。对程序中的重要变量作注释是很好的编程习惯。
变量的定义格式
变量定义的一般格式是:
type name;
类型 名字;
int age;
float score;
float average;
long number;
char UserSelection;
变量先定义后使用的好处
变量“先定义,后使用”的好处是:
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
整型数据补码的求法:正数
正数的补码等于正数的原码(二进制表示)。
0
0
0
0
0
0
0
0
1
0
1
0
0
0
0
0
+10的原码:+10的二进制数,最高位为0
0
0
0
0
0
0
0
0
1
0
1
0
0
0
0
0
+10的反码:等于+10的原码
0
0
0
0
0
0
0
0
1
0
1
0
0
0
0
0
+10的补码:等于+10的原码
整型数据补码的求法:负数
负数的补码等于负数的反码加1。
0
0
0
0
0
0
0
0
1
0
1
0
0
0
0
1
-10的原码:+10的原码,将最高位变为1
1
1
1
1
1
1
1
1
0
1
0
1
1
1
1
1
-10的反码:原码最高位不动,其余位取反
1
1
1
1
1
1
1
0
1
1
0
1
1
1
1
1
-10的补码:反码整个加上1
有符号和无符号的区别
◆ 有符号和无符号的区别是什么?
关键在于“最高位”的性质。
对于有符号数据:把最高位作为符号位
表示数据的符号(0为正,1为负),其余位
保存具体数值,因此可以用于正确保存限
值范围内的正数或负数。
对于无符号数据:最高位不作为符号位,
全部的位都用来表示数据的数值,因此可
以用于正确保存限值范围内的正数。
整型数据的表示范围
◆由于每一种整型数据占有的字节单元数
目都是有限的,所以每一种整型数据可表
示的数的范围也是有限的。
◆由于不同类型的整型数据占有的字节单
元数目是不同的,所以不同类型的整型数
据可表示的数的范围也是不同的。
整型数据的表示范围
ANSI C标准定义的整型类型
类 型 Bit 数 有效表示范围
[signed] int 16 -32768 ~ +32767
unsigned int 16 0 ~ +65535
[signed] short [int] 16 -32768 ~ +32767
unsigned short [int] 16 0 ~ +65535
[signed] long [int] 32 -2147483648 ~ +2147483647
unsigned 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.所有实型常数都被默认为double
3.绝对值小于1的实型数,其小数点前的零可以省略:如0.09可写作5.095
4.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 Interchange
ASCII 代码实际上是一个0~255之间的整数。每一个整数代表着一个不同的字符,即每一个字符都有一个不同的整数代码。对字符型数据来讲,它的那一个字节中保存的就是这个整数。
字符型数据在内存中的存储方式
常用字符的ASCII代码表
ASCII值 字符 ASCII值 字符 ASCII值 字符 ASCII值 字符
0 NUL 48 0 65 A 97 a
10 LF 换行 49 1 66 B 98 b
13 CR 回车 … … … … … …
32 空格 57 9 90 Z 122 z
字符型数据在内存中的存储方式

0 1 0 0 0 0 0 1
65
这个字节存储的内容是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=a
n1=98,n2=97
c1=98,c2=97
n1=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, 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);
}
实际上这些运算都是整数之间的运算。
整型数据和字符型数据通用:例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=97
c2=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] 定义一个函数,用于求两个数中的大数。


int max(int n1, int n2)
{ 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] 实参对形参的数据传递。


void main()
{ void s(int n);
int n=100;
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


#define K 4
#define N 5
long f1(int n,int k)
{ long power=n;
int i;
for(i=1;ireturn power;
}
long f2(int n,int 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!。


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] 写一函数,统计字符串中字母的个数。


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门课程的成绩,求平均成绩。

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)
{ int b,c;
……
}

int f2(int x)
{ int y,z;
……
}

main()
{ int m,n;
……
}
关于局部变量的作用域还要说明以下几点:
1.主函数main()中定义的内部变量,也只能在主函数中使用,其它函数不能使用。同时,主函数中也不能使用其它函数中定义的内部变量。因为主函数也是一个函数,与其它函数是平行关系。这一点是与其它语言不同的,应予以注意。
2.形参变量也是内部变量,属于被调用函数;实参变量,则是调用函数的内部变量。
3.允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。
4.在复合语句中也可定义变量,其作用域只在复合语句范围内。
[Return]
8.4.2 外部变量
在函数外部定义的变量称为外部变量。以此类推,在函数外部定义的数组就称为外部数组。
外部变量不属于任何一个函数,其作用域是:从外部变量的定义位置开始,到本文件结束为止。
外部变量可被作用域内的所有函数直接引用,所以外部变量又称全局变量。
[案例7.9] 输入长方体的长(l)、宽(w)、高(h),求长方体体积及正、侧、顶三个面的面积。


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] 外部变量的定义与说明。

int vs(int xl,int xw)
{ extern int xh;
int v;
v=xl*xw*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;
[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.c
main()
{ 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]自动变量与静态局部变量的存储特性。

void auto_static(void)
{ int var_auto=0;
static int var_static=0;
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);
[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码形式存储到一个磁盘文件中。
#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码文件。



#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码文件副本的功能。



#include "stdio.h"
main(int argc, char *argv[])
{ FILE *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码形式存储到一个磁盘文件中;然后再输出到屏幕上。


#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);
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] 输入圆的半径,求圆的周长、面积和球的体积。要求使用无参宏定义圆周率。


#define 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;]
#endif
2.功能:当“标识符”已经被#define命令定义过,则编译程序段1,否则编译程序段2。
(1)在不同的系统中,一个int 型数据占用的内存字节数可能是不同的。
(2)利用条件编译,还可使同一源程序即适合于调试(进行程序跟踪、打印较多的状态或错误信息),又适合高效执行要求。
3.关于#ifndef ~ #endif命令
格式与#ifdef ~ #endif命令一样,功能正好与之相反。
[Return]
9.3.2 #if ~ #endif
1.一般格式
#if 常量表达式
程序段1;
[#else
程序段2;]
#endif
2.功能:当表达式为非0(“逻辑真”)时,编译程序段1,否则编译程序段2。
[案例8.2] 输入一个口令,根据需要设置条件编译,使之能将口令原码输出,或仅输出若干星号“*”。

#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的累计和。 main() { int i,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)。 main() { int i, n; long 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 ! = 120
1.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的累计和。 main() { int i=1,sum=0; while( i<=100 ) { sum += i; i++; } printf(“sum=%d\n”,sum); }
程序运行情况如下: sum=5050
5.循环嵌套
(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的累计和。 main() { int i=1, sum=0; do { sum += i; i++; } while(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)。参考源程序如下: main() { long int f1=1,f2=1; int i=1;
for( ; i<=20; i++ ) { printf(“%15ld%15ld”, f1, f2); if(i%2==0) printf(“\n”); f1 += f2; f2 += f1; } }
[例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)
{ for(j=2; j<=i-1; j++) if(i%j= =0) break; if(counter%10= =0) printf(“\n”); if( j >= i ) { 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;
内存中每个字节有一个编号-----地址
…...
…...
2000
2001
2002
2005
内存
0
2003
i
k
编译或函数调用时为其分配内存单元
变量是对程序中数据
存储空间的抽象
…...
…...
2000
2004
2006
2005
整型变量i
10
变量i_pointer
2001
2002
2003
指针与指针变量
指针:一个变量的地址
指针变量:专门存放变量地址的变量叫指针变量
2000
指针
指针变量
变量的内容
变量的地址
指针变量
变量
变量地址(指针)
变量值
指向
地址存入
指针变量
&与*运算符
含义
含义: 取变量的地址
单目运算符
优先级: 2
结合性:自右向左
含义: 取指针所指向变量的内容
单目运算符
优先级: 2
结合性:自右向左
两者关系:互为逆运算
理解
…...
…...
2000
2004
2006
2005
整型变量i
10
变量i_pointer
2001
2002
2003
2000
指针变量
i_pointer-----指针变量,它的内容是地址量
*i_pointer----指针的目标变量,它的内容是数据
&i_pointer---指针变量占用内存的地址
2000
10
i_pointer
*i_pointer
&i_pointer
i
i_pointer &i &(*i_pointer)
i *i_pointer *(&i)
i_pointer = &i = &(*i_pointer)
i = *i_pointer = *(&i)
直接访问与间接访问
直接访问:按变量地址存取变量值
间接访问:通过存放变量地址的变量去访问变量
例 i=3; -----直接访问
指针变量
…...
…...
2000
2004
2006
2005
整型变量i
10
变量i_pointer
2001
2002
2003
2000
3
例 *i_pointer=20; -----间接访问
20
指针变量
…...
…...
2000
2004
2006
2005
整型变量i
10
变量i_pointer
2001
2002
2003
2000
整型变量k
例 k=i; --直接访问
k=*i_pointer; --间接访问
10
例 k=i;
k=*i_pointer;
10.2 指针变量
指针变量与其所指向的变量之间的关系
指针变量的定义
一般形式: [存储类型] 数据类型 *指针名;
3
变量i
2000
i_pointer
*i_pointer
i
*i_pointer
&i
i_pointer
i=3;
*i_pointer=3
3
变量i
2000
i_pointer
*i_pointer
i
*i_pointer
&i
i_pointer
i=3;
*i_pointer=3
合法标识符
指针变量本身的存储类型
指针的目标变量的数据类型
表示定义指针变量
不是‘*’运算符
例 int *p1,*p2;
float *q ;
static char *name;
注意:
1、int *p1, *p2; 与 int *p1, p2;
2、指针变量名是p1,p2 ,不是*p1,*p2
3、指针变量只能指向定义时所规定类型的变量
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);
}
指针变量必须先赋值,再使用
…...
…...
2000
2004
2006
2005
整型变量i
10
指针变量p
2001
2002
2003
随机
零指针与空类型指针
零指针:(空指针)
定义:指针变量值为零
表示: int * p=0;
p指向地址为0的单元,
系统保证该单元不作它用
表示指针变量值没有意义
#define NULL 0
int *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)
…...
…...
f86
f8a
f8c
f8b
整型变量a
10
指针变量pa
f87
f88
f89
f86
例 输入两个数,并使其从大到小输出
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=9
max=9,min=5
…...
…...
指针变量p1
指针变量p
2000
2008
2002
2004
2006
指针变量p2
整型变量b
整型变量a
5
2006
9
2008
2006
2008
2006
指针变量作为函数参数——地址传递
特点:共享内存,“双向”传递
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);
}
例 将数从大到小输出
…...
…...
2000
2008
200A
2002
2004
2006
5
变量a
变量b
(main)
9
变量temp
变量y
变量x
(swap)
5
5
9
5
9
COPY
指针变量作为函数参数——地址传递
特点:共享内存,“双向”传递
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);
}
例 将数从大到小输出
值传递
…...
…...
2000
2008
200A
2002
2004
2006
5
变量a
变量b
(main)
9
运行结果: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("\n%d,%d\n",a,b);
}
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010
...
5
9
整型变量a
整型变量b
(main)
指针pointer_1
指针pointer_2
2000
2002
(swap)
指针p1
指针p2
整型p
5
9
2000
2002
COPY
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);
}
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010
...
5
9
整型变量a
整型变量b
(main)
指针pointer_1
指针pointer_2
2000
2002
5
9
例 将数从大到小输出
运行结果: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;
例 将数从大到小输出
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010
...
5
9
整型变量a
整型变量b
(main)
指针pointer_1
指针pointer_2
2000
2002
9
9
2000
2002
COPY
(swap)
指针p1
指针p2
指针p
****
假设2000
指针变量在使用前
必须赋值!

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
例 将数从大到小输出
值传递
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010
...
5
9
整型a
整型b
(main)
pointer_1
pointer_2
2000
2002
9
COPY
(swap)
整型x
整型y
整型t
5
5
5
9
运行结果: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);
}
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010
...
5
9
整型a
整型b
(main)
pointer_1
pointer_2
2000
2002
2000
2002
COPY
(swap)
指针p1
指针p2
指针p
****
2000
地址传递
2000
2002
10.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)/d
p1+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数组
p
p+1,a+1
p+i,a+i
p+9,a+9
1
指针变量的关系运算
若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]
...
a
a+9
a+1
a+2
地址
元素
下标法
a[0]
a[1]
a[2]
a[9]
a[0]
a[1]
a[2]
a[3]
a[9]
...
p
p+9
p+1
p+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]);
}
1
2
3
4
5
pa
例 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 6
p
p
5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
例 注意指针变量的运算
6
main()
{ 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;
p
p
5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
p
p
p
p
p
p
指针变量可以指到数组后的内存单元
数组名作函数参数
数组名作函数参数,是地址传递
数组名作函数参数,实参与形参的对应关系
实参
形参
数组名
指针变量
数组名
指针变量
数组名
数组名
指针变量
指针变量
例 将数组a中的n个整数按相反顺序存放
i
j
3 7 9 11 0 6 7 5 4 2
0 1 2 3 4 5 6 7 8 9
i
j
i
j
i
j
j
i
11
7
6
0
5
9
4
7
2
3
实参与形参均用数组
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");
}
实参用数组,形参用指针变量
3
7
9
11
0
6
7
5
4
2
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
x
p=x+m
a数组
6
0
7
11
5
9
4
7
2
3
i
j
i
j
i
j
j
i
j
i
例 将数组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)
array
int array[10];
对于二维数组:
(1)a是数组名,
包含三个元素
a[0],a[1],a[2]
(2)每个元素a[i]
又是一个一维
数组,包含4个
元素
a
a+1
a+2
*(*(a+0)+1)
*(a[0]+1)
int a[3][4];
a[0]
a[1]
a[2]
2000
2008
2016
2000
2002
2008
2010
2016
2018
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]
基类型
行指针与列指针
a[0]+1
a[1]+1
a[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]
2000
2008
2016
2000
2002
2008
2010
2016
2018
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]
a
a+1
a+2
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]
二维数组元素表示形式:
(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列元素值
2000
2000
2008
2008
2012
13
二维数组的指针变量
指向二维数组元素的指针变量
例 指向二维数组元素的指针变量
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]
a
a+1
a+2
p
p+1
p+2
p[0]+1或 *p+1
p[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]
p
p
p
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,3
1
2
3
4
3
4
5
6
5
6
7
8
p
q
2
p
q
p
q
二维数组的指针作函数参数
用指向变量的指针变量
用指向一维数组的指针变量
用二维数组名
实参
形参
数组名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]
65
52
79
60
80
87
90
81
90
99
100
98
p
p
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);
}
65
52
79
60
80
87
90
81
90
99
100
98
p
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);
}
I
l
o
v
e
C
h
i
string[0]
string[1]
string[2]
string[3]
string[4]
string[5]
string[6]
string[7]
string[8]
string[9]
string
string[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++;
}
}
I
l
o
v
e
C
h
i
string
n
!
a
\0
字符指针初始化:把字符串首地址赋给string
char *string;
string=“I love China!”;
string
*string!=0
字符串指针作函数参数
例 用函数调用实现字符串复制
(1)用字符数组作参数
(2)用字符指针变量作参数
a
I
a
m
a
t
e
a
c
e
h
\0
r
.
from
a
b
y
u
a
r
a
s
u
t
n
d
e
to
b
o
e
t
.
\0
I
a
a
e
c
e
h
\0
r
.
t
.
\0
m
t
a
void 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 %c
char 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);
}
pointer
pointer+1
34
78
90
66
56
89
67
88
60
70
80
90
score数组
p
p
p
p
例 写一个函数,求两个int型变量中居于较大值的变量的地址
int *f1(int *x,int *y)
{
if(*x>*y)
return x;
else
return y;
}
main()
{ int a=2,b=3;
int *p;
p=f1(&a, &b);
printf("%d\n",*p);
}
…...
2000
2008
200A
2002
2004
2006
2
3
指针变量y
指针变量x
(f1)
2002
2000
COPY
变量a
变量b
(main)
指针变量p
**
例 写一个函数,求两个int型变量中居于较大值的变量的地址
…...
…...
2000
2008
200A
2002
2004
2006
2
变量a
变量b
(main)
3
指针变量p
**
2002
int *f3(int *x,int *y)
{
if(*x>*y)
return x;
else
return 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;
else
return &y;
}
main()
{ int a=2,b=3;
int *p;
p=f3(a, b);
printf("%d\n",*p);
}
…...
2000
2008
200A
2002
2004
2006
2
3
变量y
变量x
(f3)
3
2
COPY
变量a
变量b
(main)
指针变量p
**
例 写一个函数,求两个int型变量中居于较大值的变量的地址
不能返回形参或局部变量
的地址作函数返回值
…...
…...
2000
2008
200A
2002
2004
2006
2
变量a
变量b
(main)
3
指针变量p
**
200A
int *f3(int x,int y)
{
if(x>y)
return &x;
else
return &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]
1
2
3
2
4
6
初始化:
main()
{ int b[2][3],*pb[ ]={b[0],b[1]};
……..
}
int *pb[2]
pb[0]
pb[1]
int b[2][3]
1
2
3
2
4
6
指针数组赋值与初始化
L i s p \0
F o r t r a n \0
B a s i c \0
p[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 \0
F o r t r a n \0
B a s i c \0
p[0]
p[1]
p[2]
p[3]
0
char name[5][9]={“gain”,“much”,“stronger”, “point”,“bye”};
char *name[5]={“gain”,“much”,“stronger”, “point”,“bye”};
g a i n \0
s t r o n g e r \0
p o i n t \0
m u c h \0
name[0]
name[1]
name[2]
name[3]
name[4]
b y e \0
g a i n \0
s t r o n g e r \0
p o i n t \0
m u c h \0
b 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)
1
2
3
2
4
6
例 对字符串排序(简单选择排序)
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]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
k
j
k
j
j
j
i=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]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
k
k
j
j
j
i=1
k
例 对字符串排序(简单选择排序)
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]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
k
k
j
j
i=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]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
k
k
j
i=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]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
多级指针
定义: 指向指针的指针
一级指针:指针变量中存放目标变量的地址
p1
&p2
&i
3
P2(指针变量)
i(整型变量)
例 int **p1;
int *p2;
int i=3;
p2=&i;
p1=&p2;
**p1=5;
二级指针:指针变量中存放一级指针变量的地址
例 int *p;
int i=3;
p=&i;
*p=5;
&i
3
P(指针变量)
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;
i
p1
p2
3
&i
&p1
**p2, *p1
*p2
多级指针
例 三级指针 int ***p;
四级指针 char ****p;
2000
2008
200A
2002
2004
2006
1
2
变量a
变量b
(main)
指针变量p
2000
指针变量q
2002
例 一级指针与二级指针
#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);
}
2002
2000
COPY
指针变量s
指针变量r
(swap)
指针变量t
2000
2002
2000
2000
2008
200A
2002
2004
2006
1
2
变量a
变量b
(main)
指针变量p
2000
指针变量q
2002
例 一级指针与二级指针
#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);
}
a
b
p
q
a
b
p
q
r
s
a
b
p
q
s
r
a
b
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);
}
2000
2008
200A
2002
2004
2006
1
2
变量a
变量b
(main)
指针变量p
2000
指针变量q
2002
2006
2004
COPY
二级指针s
二级指针r
(swap)
指针变量t
2000
2002
2000
例 一级指针与二级指针
#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);
}
2000
2008
200A
2002
2004
2006
1
2
变量a
变量b
(main)
指针变量p
2000
指针变量q
2002
2000
2002
输出: 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);
}
a
b
p
q
b
a
p
q
a
b
r
s
p
q
a
b
r
s
p
q
输出: 2,1
例 用二级指针处理字符串
#define NULL 0
void 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]
world
bye
\0
hello
good
name
p
运行结果:
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………参数n
main(int argc, char *argv[])
{ ………
}
命令行参数传递
带参数的main函数形式:
C:\TC> copy[.exe] source.c temp.c
有3个字符串参数的命令行
命令行中参数个数
元素指向命令行参数
中各字符串首地址
形参名任意
命令行实参
main(形参)
系统自动调用
main函数时传递
第一个参数: main所在的可执行文件名
例 输出命令行参数

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.exe
2. 在DOS状态下运行(test.exe所在路径下)
例如: C:\TC> test[.exe] hello world!
运行结果:hello
world!
运行结果:test
hello
world!
argv[0]
argv[1]
argv[2]
char *argv[]
world
test
hello
argv
argc=3
定义
含义
int i;
int *p;
int a[n];
int *p[n];
int (*p)[n];
int f();
int *p();
int (*p)();
int **p;
定义整型变量i
p为指向整型数据的指针变量
定义含n个元素的整型数组a
n个指向整型数据的指针变量组成的指针数组p
p为指向含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’
◆系统在求解包含混合运算的表达式时,
先把不同类型的数据转换成同一类型,
然后进行运算。转换规则如图:
各类数值型数据间的混合运算
double
long
unsigned
int
float
char, 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”);
else
printf(“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 / 3
10 - 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 – 2
15
开始求解 (按各种运算符的结合性)
强制类型转换运算符和表达式
◆强制类型转换运算符(单目)
运算符: (类型名)
用 法: (类型名) (变量或表达式)
优先级: 高于算术运算符
结合性: 右结合性
如:(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=6
a=5, m=6
a=6, m=5
a=6, m=6
自增、自减运算符使用注意事项
注意:自增、自减运算符的运算量只能
是变量,不可能是常量或表达式。

为什么?
因为自增、自减运算符实际上具有赋值功能,是C语言中除了赋值运算符之外唯一可以改变变量值的运算符。
++ i
i = i + 1
- - i
i = 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
为了求解整个赋值表达式的值 先求解=号右边的表达式的值
再把求解出的=号右边的表达式 的值赋给=号左边的变量
- 1
x
最后,原来的赋值表达式的值就等于被赋值后的变量的值:
x = -b/(2*a)
的值就等于
- 1
x
赋值表达式:例
#include
void main( )
{
int a, b, c;

a = b = c = 10;
printf(“a=%d, b=%d, c=%d\n”, a, b, c);

c = ( a = 3 ) * b;
printf(“a=%d, b=%d, c=%d\n”, a, b, c);
}
是错误的。因为当一次定义多个变量时,不能用连续=号的方式给多个变量赋同一初值.
赋值表达式:例
#include
int main( )
{
int a, b, c;

a = b = c = 10;
printf(“a=%d, b=%d, c=%d\n”, a, b, c);

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;

a = b = c = 10;
printf(“a=%d, b=%d, c=%d\n”, a, b, c);

c = ( a = 3 ) * b;
printf(“a=%d, b=%d, c=%d\n”, a, b, c);
}
是错误的。因为=号左边只能是变量。
赋值表达式:例
#include
int main( )
{
int a, b, c;

a = b = c = 10;
printf(“a=%d, b=%d, c=%d\n”, a, b, c);

c = ( a = 3 ) * b;
printf(“a=%d, b=%d, c=%d\n”, a, b, c);
}
是正确的。
#include
int main( )
{
int a, b, c;

a = b = c = 10;
printf(“a=%d, b=%d, c=%d\n”, a, b, c);

c = ( a = 3 ) * b;
printf(“a=%d, b=%d, c=%d\n”, a, b, c);
}
赋值表达式:例
运行结果:
a=10, b=10, c=10
a=3, b=10, c=30
3.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=3
x=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.h
4.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
普通字符
格式字符
普通字符
格式字符
普通字符
15
is
30
.
_
待输出的数据
普通字符:原样输出 格式字符:格式化要输出的数据
printf函数中的格式字符
格式字符 格式作用
整型 d, i 格式化为有符号的十进制整数
u 格式化为无符号的十进制整数
o 格式化为无符号的八进制整数
x, X 格式化为无符号的十六进制整数
字符型 c 格式化为一个字符
s 格式化为一个字符串
printf函数中的格式字符
格式字符 格式作用
实型 f 格式化为小数形式的实数,隐含输出6
位小数
e, E 格式化为指数形式的实数,数字部分有
6位小数
g, G 选用%f或%e格式中输出宽度较短的一
种格式,不输出无意义的0
printf函数中的附加格式字符
在格式说明时,还可以在%和上述格式
字符之间插入几种附加的格式字符。(又
叫修饰符)。
% [附加格式字符] 格式字符
% [-][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, 65535
b = -2, 177776, fffe, 65534
a 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
b 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
printf函数的使用:例
#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, 2304
f1 = 12345.669922, 1.234567e+04
printf函数的使用:例
#include
void main( )
{
printf("%c%c%c", 'B', 'O', 'Y');
printf("%c", '\n');
printf("%s", "China");
printf("%s", "\n");
printf("Please input three data:");
}
运行结果: BOY
China
Please input three data:
格式输入函数scanf
1. 原型:
int scanf(const char *format [,
address, ...]);
2. 参数:两部分
第一部分:为字符型常指针,大多数情况
下都是字符串常量
第二部分:若干个内存单元地址的列表
注:[ ] 中的参数表示是可选的。
格式输入函数scanf
3. 功能:一次函数调用可以从默认的输入
设备上(键盘)扫描用户的输入并经格式化
为若干个不同类型的数据后依次保存到指
定的地址列表中。
4. 用法:scanf(格式控制字符串,地址
列表);
5. 返回值:扫描、格式化后保存的数据的
个数。绝大多数情况下不使用该返回值。
格式输入函数scanf的参数
scanf(格式控制字符串,地址列表);
⒈ 格式控制字符串:常字符指针,既可以是
字符串常量,也可以是字符数组或字符指针。
⒉ 地址列表:一些已经分配给某些变量的内
存单元的地址的列表,scanf 函数将把扫描
用户输入并格式化后的数据按顺序依次送到
这些地址所指向的单元中去。
scanf 函数中的格式控制字符串
格式控制字符:由%和规定的格式字符组成,
一个格式控制字符串中可以有一个或多个格式
控制字符。
格式控制字符串中可能包含两类字符:
格式字符的作用是:
把扫描用户键盘输入后得到的数据格式化成该
格式字符所规定的类型的数据。
如: %d %c %f
scanf 函数中的格式控制字符串
普通字符:除格式字符以外的所有其它字符。
它们不但不起任何格式作用,也不被显示,反
而要求使用者在输入信息时照原样输入,否则
函数将不能扫描到正确的数据。因此,通常在
scanf函数的第一部分参数中,不要包含除必
要的分隔符号之外的任何字符。
格式控制字符串中可能包含两类字符:
注:人们一般约定俗成在输入数据时,用逗号
作为分隔符号
取变量地址运算符&
◆ 取地址运算符(单目)
运算符: &
用 法: & 变量
优先级: 处于整个运算符优先级表中的第
二级,高于所有的双目运算符
结合性: 自右向左
作 用: 得到变量所占内存单元的地址
取变量地址运算符&
注意:& 运算符的运算量只能是变量,即
是说只能取变量的地址,不能取常量或表
达式的地址。
如:int a; float fnum;
&a;
&fnum;
&10 &(x+y)
像下面这样的用法就是错误的:
int num1, num2;
scanf(“num1=%d ,num2=%d” , &num1, &num2);
格式输入函数scanf的执行过程
num1=
普通 字符
格式字符
普通 字符
格式字符
15
,num2=
30

地址列表
15
30
num1
num2
普通字符:原样输入 格式字符:格式化扫描到的数据
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’);
答案: 98
b
#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 数据输出
字符输出函数

#include
main()
{ int c;
char a;
c=65; a='B';
putchar(c); putchar('\n'); putchar(a);
}
运行结果:A
B

格式:printf(“格式控制串”,输出表)
功能:按指定格式向显示器输出数据
返值:正常,返回输出字节数;出错,返回EOF(-1)
格式输出函数
输出表:要输出的数据(可以没有,多个时以“,”分隔)
格式控制串:包含两种信息
格式说明: %[修饰符]格式字符 ,用于指定输出格式
普通字符或转义序列:原样输出
格式字符
d,i
x,X
o
u
c
s
e,E
f
g
%%
格式字符:
十六进制无符号整数
不带符号十进制整数
十进制整数
指数形式浮点小数
单一字符
字符串
八进制无符号整数
小数形式浮点小数
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(“%%”);
567
ff
101
567
A
ABC
5.677890e+02
567.789000
567.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 4
a=3, b=4
11 11 11 11 11 11 11 11
65535
附加格式说明符(修饰符)
.n
对实数,指定小数点后位数(四舍五入)
对字符串,指定实际输出位数
修饰符
功 能
m
输出数据域宽,数据长度输出数据在域内左对齐(缺省右对齐)
-
指定在有符号数的正数前显示正号(+)
+
输出数值时指定左面不使用的空位置自动填0
0
在八进制和十六进制数前显示前导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+02
a
例 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!
Hello
Hello
Hel
例 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,1234
123.46,123.5
Hello,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);
例 l
00 00 00 00 00 00 00 01
00 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 数据输入
字符输入函数


#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
修饰符
功 能
h
m
*
用于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 程序举例

#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.50
area= 5.33
文件包含预处理命令
变量定义
输入数据
输出数据
例 从键盘输入大写字母,用小写字母输出

#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,65
a,97

#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.00
x2=-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进行按位与运算。


main()
{ int num, mask;
printf("Input a integer number: ");
scanf("%d",&num);
num >>= 8;
mask = ~ ( ~0 << 4);
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,按二进制位输出该数。


#include "stdio.h"
main()
{ int num, mask, i;
printf("Input a integer number: ");
scanf("%d",&num);
mask = 1<<15;
printf("%d=" , num);
for(i=1; i<=16; i++)
{ putchar(num&mask ’1’ : ‘0’);
num <<= 1;
if( i%4==0 ) putchar(‘,’);
}
printf("\bB\n");
} [程序演示]
程序运行情况:
Input a integer number:1000 ←┘
1000=0000,0011,1110,1000B
12.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;
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语言程序。


main( ){
printf(“This is a C program.\n”);
}
程序运行结果:
This is a C program.
例 Hello,World!

#include
main()
{
printf(“Hello,World!”);
}
注释
编译预处理
函数
语句
输出:
Hello,World!


#include

main()
{ int a,b,sum;
a=10;
b=24;
sum=add(a,b);
printf(”sum= %d\n",sum);
}

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程序由语句组成
用“;”作为语句终止符
注释
为注释,不能嵌套
不产生编译代码
例: */
非法
编译预处理命令
[案例1.2] 由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=9
1.函数是C语言程序的基本单位。
main( )函数的作用,相当于其它高级语言中的主程序;其它函数的作用,相当于子程序。
2.C语言程序总是从main( )函数开始执行。
一个C语言程序,总是从main( )函数开始执行,而不论其在程序中的位置。当主函数执行完毕时,亦即程序执行完毕。
习惯上,将主函数main( )放在最前头。
1.2.2 函数的一般结构
任何函数(包括主函数main( ))都是由函数说明和函数体两部分组成。其一般结构如下:
[函数类型] 函数名(函数参数表) 函数说明部分
{
说明语句部分;
执行语句部分; 函数体部分
}
1. 使用的语法符号约定
[...]──方括号表示可选(即可以指定,也可以缺省)
……──省略号表示前面的项可以重复
| ──多(含2)中选1
2.函数说明
由函数类型(可缺省)、函数名和函数参数表三部分组成,其中函数参数表的格式为:
数据类型 形参[, 数据类型 形参2……]
例如,[案例1.2]中的函数max( ),其函数说明各部分如图1-1所示。
函数类型 函数名 函数参数表
↓ ↓ ↓
int max ( int x , int y )
图1-1 函数说明部分结构图
注意:在旧标准中,函数可以缺省参数表。
3.函数体
在函数说明部分的下面、大括号(必须配对使用)内的部分。
函数体一般由说明语句和可执行语句两部分构成:
(1)说明语句部分
说明语句部分由变量定义、自定义类型定义、自定义函数说明、外部变量说明等组成。
(2)可执行语句
一般由若干条可执行语句构成。图1-2是[案例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));
}
图1-2 函数体结构示意图
4.说明
(1)函数体中的变量定义语句,必须在所有可执行语句之前。
下面程序中变量定义语句“int max;”的位置是非法的:
main( )
{
int x,y;
x=3;
y=6;
int max;
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, return
2. 函数调用语句
函数调用语句由一次函数调用加一个分号(语句结束标志)构成。
例如,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和非0
C语言用整数“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,求三个数中的最大值。 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,18
max=22
本案例中的第1个if语句,可优化为如下不带else子句的形式:
max=num1;
if(num2>max) max=num2;
这种优化形式的基本思想是:首先取一个数预置为max(最大值),然后再用max依次与其余的数逐个比较,如果发现有比max大的,就用它给max重新赋值,比较完所有的数后,max中的数就是最大值。这种方法,对从3个或3个以上的数中找最大值的处理,非常有效。请读者仔细体会。
[案例4.2]输入任意三个数num1、num2、num3,按从小到大的顺序排序输出。

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,22
1.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即可。这种处理两种状态值的方法,对优化算法和提高程序可读性非常有效,请读者仔细体会。参考程序如下:


main()
{int year,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);
else
printf("%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] 从键盘上输入一个字符,如果它是大写字母,则把它转换成小写字母输出;否则,直接输出。

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。 main() {int score, grade; printf(“Input a score(0~100): ”); scanf(“%d”, &score); grade = score/10; 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=B
1.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)。 #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) 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、1
1000<profit≤2000 对应1、2
2000<profit≤5000 对应2、3、4、5
5000<profit≤10000 对应5、6、7、8、9、10
10000<profit 对应10、11、12、……
为解决相邻两个区间的重叠问题,最简单的方法就是:利润profit先减1(最小增量),然后再整除1000即可:
profit≤1000 对应0
1000<profit≤2000 对应1
2000<profit≤5000 对应2、3、4
5000<profit≤10000 对应5、6、7、8、9
10000<profit 对应10、11、12、……

main()
{long profit;
int grade;
float salary=500;
printf("Input profit: ");
scanf("%ld", &profit);
grade= (profit – 1) / 1000;
switch(grade)
{ case 0: break;
case 1: salary += profit*0.1; break;
case 2:
case 3:
case 4: salary += profit*0.15; break;
case 5:
case 6:
case 7:
case 8:
case 9: salary += profit*0.2; break;
default: salary += profit*0.25;
}
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] 用转义字符输出可打印字符和不可打印字符。
main() { printf(“\x4F\x4B\x21\n”); printf(“\x15 \xAB\n”); }
程序运行结果如下: OK! §
3.6.2 字符变量
字符变量的类型关键字为char,一般占用1字节内存单元。
1.变量值的存储 字符变量用来存储字符常量。将一个字符常量存储到一个字符变量中,实际上是将该字符的ASCII码值(无符号整数)存储到内存单元中。
例如,
char ch1, ch2;
ch1=’a’; ch2=’b’;
2.特性
字符数据在内存中存储的是字符的ASCII码── 一个无符号整数,其形式与整数的存储形式一样(如图2-4所示),所以C语言允许字符型数据与整型数据之间通用。
(1)一个字符型数据,既可以字符形式输出,也可以整数形式输出。 [案例2.2] 字符变量的字符形式输出和整数形式输出。 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] 字符数据的算术运算。 main() { char ch1,ch2; ch1=‘a’; ch2=‘B’; printf(“ch1=%c,ch2=%c\n”,ch1-32,ch2+32); 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=b
ch1+200=297
ch1+200=)
ch1+256=353
ch1+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))
(int)(x + y)
(float)5 / 2(等价于(float)(5)/2)
(float)(5 / 2)
注意:强制转换类型得到的是一个所需类型的中间量,原表达式类型并不发生变化。例如,(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
y *= x + 6
C语言规定的10种复合赋值运算符如下:
+=,-=,*=,/=,%=;
&=,^=,|=,<<=,>>=;
3.赋值表达式
由赋值运算符或复合赋值运算符,将一个变量和一个表达式连接起来的表达式,称为赋值表达式。
(1)一般格式
变量 (复合)赋值运算符 表达式
(2)赋值表达式的值
任何一个表达式都有一个值,赋值表达式也不例外。被赋值变量的值,就是赋值表达式的值。
例如,“a = 5”这个赋值表达式,变量a的值“5”就是它的值。
[Return]
3.9 C语言特有的运算和运算符
3.9.1 自增(++)、自减(--)运算 1.作用 自增运算使单个变量的值增1,自减运算使单个变量的值减1。 2.用法与运算规则 自增、自减运算符都有两种用法: (1)前置运算──运算符放在变量之前:++变量、--变量 先使变量的值增(或减)1,然后再以变化后的值参与其它运算,即先增减、后运算。 (2)后置运算──运算符放在变量之后:变量++、变量-- 变量先参与其它运算,然后再使变量的值增(或减)1,即先运算、后增减。
[案例2.4] 自增、自减运算符的用法与运算规则示例。


main()
{ int x=6, y;
printf("x=%d\n",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=6
y=++x: x=7,y=7
y=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, ……, 表达式n
2.求解过程
自左至右,依次计算各表达式的值,“表达式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);
}
例 函数带回不确定值
输出:10
void 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++不行)

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);
}
被调函数出现在主调函数
之前,不必函数说明

main()
{ float add(float,float);
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);
}
x
a
product
××
××
1.2
1.2
1.728
参数传递方式
值传递方式
方式:函数调用时,为形参分配单元,并将实参的值复制到形参中;调用结束,形参单元被释放,实参单元仍保留并维持原值
特点:
形参与实参占用不同的内存单元
单向传递
7
11
x:
y:
调用前:
调用结束:
7
11
x:
y:
例 交换两个数

#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;
}
调用:
7
11
a:
b:
7
11
x:
y:
swap:
7
11
x:
y:
11
7
a:
b:
temp
地址传递
方式:函数调用时,将数据的存储地址作为参数传递给形参
特点:
形参与实参占用同样的存储单元
“双向”传递
实参和形参必须是地址常量或变量

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);
}
例 交换两个数
a
5
9
b
调前:
a
5
9
b
调swap:
p1
&a
&b
p2
a
9
5
b
交换:
p1
&a
&b
p2
a
9
5
b
返回:
#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
调用函数min
min函数
例 用弦截法求方程根 ( P.155 )
x
y
f(x)
0
x1
x2
x
f(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=x
y1=y
x2=x
直到 |y|<
root=x 输出 root
root函数
运行情况:
Input x1,x2:
2,6
A root of equation is 5.0000
main( )
调用函数root
输出根 x
结束
root函数
xpoint函数
调用函数xpoint
调用函数f
f函数
递归调用 ( P.158 )
定义:函数直接或间接的调用自身叫函数的递归调用
f( )
调f
调f2
调f1
f1( )
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的阶乘

#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');
}
A
B
C
8.7 数组作为函数参数
数组元素作函数实参——值传递
例 两个数组大小比较
4
3
2
1
0
5
a
56
23
12
10
76
88
4
3
2
1
0
5
b
21
23
43
98
66
54
n=0
m=0
k=0
i
n=0
m=0
k=1
i
n=0
m=1
k=1
i
n=1
m=1
k=1
i
n=1
m=1
k=2
i
n=2
m=1
k=2
i
n=3
m=1
k=2
a和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;
}

}
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[ ]
.
.
2
1
0
9
score
56
23
12
….
….
88
stu
例 数组元素与 数组名
作函数参数比较
1
2
a
调用前
a[0]
a[1]
1
2
a
调用
a[0]
a[1]
1
2
x
y
2
1
x
y
交换
1
2
a
返回
#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]);
}
值传递
1
2
a
调用前
1
2
a
x
调用
2
1
a
x
交换
2
1
a
返回
#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");
}
0
1
2
3
4
5
6
7
8
9
a
49
68
57
32
9
99
27
13
76
88
array
k
j
j
j
k
j
k
j
j
j
j
j
9
49
i=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");
}
k
j
j
k
j
k
j
j
j
j
j
0
1
2
3
4
5
6
7
8
9
a
49
68
57
32
9
99
27
13
76
88
array
9
49
k
k
13
68
i=1
0
1
2
3
4
5
6
7
8
9
a
9
13
27
32
49
57
68
76
88
99
array
i=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 7
2 4 6 8
15 17 34 12
i
j
max=1
1 3 5 7
2 4 6 8
15 17 34 12
i
j
max=3
1 3 5 7
2 4 6 8
15 17 34 12
i
j
max=5
j
1 3 5 7
2 4 6 8
15 17 34 12
i
max=7
j
1 3 5 7
2 4 6 8
15 17 34 12
i
max=7
j
1 3 5 7
2 4 6 8
15 17 34 12
i
max=34
int 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]);
}
3
1
4
6
7
9
a
sum_row
x
result
18
12
8.8 变量的存储属性
概述
变量是对程序中数据的存储空间的抽象
内存
…….
main()
{ int a;
a=10;
printf(“%d”,a);
}
编译或函数调用时为其分配内存单元
10
2000
2001
程序中使用变量名对内存操作
变量的属性
数据类型:变量所持有的数据的性质(操作属性)
存储属性
存储器类型:寄存器、静态存储区、动态存储区
生存期:变量在某一时刻存在-------静态变量与动态变量
作用域:变量在某区域内有效-------局部变量与全局变量
变量的存储类型
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 5
main()
{ 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 5
main()
{ 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=4
sub:a=6,b=7
main:a=3,b=4
全局变量---外部变量
定义:在函数外定义,可为本文件所有函数共用
有效范围:从定义变量的位置开始到本源文件结束,及有extern说明的其它源文件
应尽量少使用全局变量,因为:
全局变量在程序全部执行过程中占用存储单元
降低了函数的通用性、可靠性,可移植性
降低程序清晰性,容易出错
定义 说明
次数: 只能1次 可说明多次
位置: 所有函数之外 函数内或函数外
分配内存: 分配内存,可初始化 不分配内存,不可初始化
外部变量说明: extern 数据类型 变量表;
外部变量定义与外部变量说明不同
若外部变量与局部变量同名,则外部变量被屏蔽
外部变量可用存储类型:缺省 或 static
float 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];

ave=average(score,10);
printf("max=%6.2f\nmin=%6.2f\n
average=%6.2f\n",max,min,ave);
}
作用域
max
min
int 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=13
extern int a,b;
int max()
{ int z;
z=a>b a:b;
return(z);
}
main()
{ printf("max=%d",max());
}
int a=13,b=-8;

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=8
int 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
局部static
auto
外部static
外部
存储类别
例 文件file1.c
int a;
main( )
{ …….
…….
f2;
…….
f1;
…….
}
f1( )
{ auto int b;
………
f2;
……..
}
f2( )
{ static int c;
………
}
C作用域
b作用域
a作用域
main
f2
f1
main
f1
f2
main
a生存期:
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=5
2nd x=3
1st x=1
x=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);
}
例 局部静态变量值具有可继承性
运行结果:1
1
1
main()
{ void increment(void);
increment();
increment();
increment();
}
void increment(void)
{ static int x=0;
x++;
printf(“%d\n”,x);
}
运行结果:1
2
3
例 变量的寿命与可见性
#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
全局i
1
main: a
0
b:-10
register
main:c
0
静态
存储区
动态
存储区
other: a
2
other: b
0
other: c
10
8
4
33
15
4
43
other: c
10
6
75
15
6
main()
{ 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=0
2: x=135 y=246
3: 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.c
file2.c
file3.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 语句
第五章 选择结构程序设计
选择结构(分支结构)
◆“选择结构”也可以叫
做“分支结构”。在一个
合理的“选择结构”中,
总是先进行某个条件的
判断,然后根据判断的
结果选择执行预先设定
的操作。
A
B

Yes
No
a
b
◆ 要想实现“选择结构”,需要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=0
5.2 逻辑运算符和逻辑表达式
逻辑运算符
逻辑表达式
逻辑表达式的求解过程
逻辑运算符
◆逻辑运算实际上是复合的关系运算,即
要判断一个大命题的成立与否,不仅要判
断其中的各个小命题是否成立,还取决于
每个小命题影响大命题成立的方式。
逻辑运算符:3种
&& 逻辑与(双目)
|| 逻辑或(双目)
! 逻辑非(单目)
逻辑运算符
逻辑运算符的优先级:
逻辑运算符的结合性:
&& 、|| 自左向右
关系运算符
&& 逻辑与


算术运算符
|| 逻辑或
! 逻辑非
= 赋值运算符
! 自右向左
逻辑运算符的运算规则
◆逻辑运算是关系比较运算结果之间进行
的运算,所以参与逻辑运算的运算量都是
逻辑量 (即值只为“真”或“假”的量) ,逻
辑运算的结果也是逻辑值(“真”或“假”,
也可以用“True”或“False”来表示) 。
逻辑运算符的运算规则
&&逻辑与:
全部运算量都为“T”,逻辑与运算才为“T”;
只要有一个运算量为“F”,逻辑与运算即为“F”
-5
+5
x > -5
x < +5
&&
逻辑运算符的运算规则
|| 逻辑或:
全部运算量都为“F”,逻辑或运算才为“F”;
只要有一个运算量为“T”,逻辑或运算即为“T”
x < -5
x > +5
||
-5
+5
逻辑运算符的运算规则
! 逻辑非:
运算量为“T” ,逻辑非运算则为“F” ;
运算量为“F” ,逻辑非运算则为“T”
x < 0
! (x=> 0)

0
逻辑运算符的运算规则
逻辑运算规则表
a b ! a ! b a && b a || b
T T F F T T
T F F T F T
F T T F F T
F 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 1
0 非0 1 0 0 1
0 0 1 1 0 0
注:a、b代表任何类型的数据(可以是常量、
变量或表达式)
逻辑表达式的求解过程
◆逻辑表达式的求解顺序按照其结合性进行。
由于参与逻辑运算的量必须是逻辑量,所以
系统求的是每一个参与逻辑运算的运算量(可
能是常量、变量或表达式)的逻辑值。
对于“逻辑与&&”表达式和“逻辑或||”表达
式,系统按从左向右顺序求解时,如果当求
出某个运算量后整个逻辑表达式的值就能够
确定时,则余下的运算量不被求解。
◆特别注意:
逻辑表达式的求解过程
x < -5 || x > +5
假设: x = -6
1 || x < +5
没有被求解
1
x < -5 || x > +5
假设: x = 6
1
0 || x > +5
0 || 1

逻辑表达式的求解过程
x > -5 && x < +5
假设: x = 6
0
1 && x < +5
1 && 0

x > -5 && x < +5
假设: x = -6
0 && 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) → 0
y = 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
这个关系表达式的逻辑值是 0
a == 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;
语句
表达式
T
F
if 语句的三种形式
2. if (表达式) 语句1; else 语句2;
如:if (x>0) sign=1; else sign=-1;
语句1
表达式
T
F
语句2
if 语句的三种形式
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
表达式1
T
F
表达式2
……
语句2
……
F
语句n
表达式n
语句n+1
F
F
T
T
T
if语句第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;
else
sign = -1;
等价
if (! (num > 0))
sign = -1;
else
sign = 1;
等价
if (num <= 0)
sign = -1;
else
sign = 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;
else
if(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;
else
if(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
表达式1
T
F
条件表达式的求解过程
x > y max = x : max = y;
if(x > y)
max = x;
else
max = y;
max=x;
max=y;
x>y
T
F
从对条件表达式的求解过程来看,完全等同于一个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;
else
if(y > z) max = y; else max = z;
等 价
同样,由于C语言运算符丰富,原本需要用
语句才能执行的操作可以使用表达式完成。
5.4 switch 语句
switch语句
switch语句是多条件判断多分支选择
语句,类似于if 语句的第三种形式,即if
嵌套语句。
switch语句中使用了3个关键字:
switch case default
switch语句的一般形式
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的值?
T
T
T
T
F
F
F

流程好像 有问题?
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的值?
T
T
T
T
F
F
F
有break
Y
N
有break
N
有break
N
有break
N
Y
Y
Y
switch语句的有关注意事项1
switch语句中,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);
else
printf(“%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);
else
printf(“%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);
else
printf(“%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);
else
printf(“%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);
else
printf(“%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);
else
printf(“%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.10
P104习题 5.4 ~ 5.10
2. 编程判断一个正数是否是同构数。
同构数是这样一个数,它出现在它的平方数
的右边。比如,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,求圆周长和圆面积。 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.500000
length= 9.42,area= 7.07
printf()函数的一般格式如下:
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的使用。

main()
{ int num1=123;
long num2=123456;

printf("num1=%d,num1=%5d,num1=%-5d,num1=%2d\n",
num1,num1,num1,num1);

printf("num2=%ld,num2=%8ld,num2=%5ld\n",num2,num2,num2);
printf("num1=%ld\n",num1);
}
程序运行结果如下:
num1=123,num1=□□123,num1=123□□,num1=123
num2=123456,num2=□□123456,num2=123456
num1=16908411
对于整数,还可用八进制、无符号形式(%o(小写字母o))和十六进制、无符号形式(%x)输出。对于unsigned型数据,也可用%u格式符,以十进制、无符号形式输出。
所谓无符号形式是指,不论正数还是负数,系统一律当作无符号整数来输出。例如,printf("%d,%o,%x\n",-1,-1,-1);
2.类型转换字符f──以小数形式、按系统默认的宽度,输出单精度和双精度实数。
[案例3.3] 类型转换字符f的使用。

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.46
d1+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的使用。

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,65
i=65,A
需要强调的是:在C语言中,整数可以用字符形式输出,字符数据也可以用整数形式输出。将整数用字符形式输出时,系统首先求该数与256的余数,然后将余数作为ASCII码,转换成相应的字符输出。
4.类型转换字符s──输出一个字符串。
[案例3.5] 类型转换字符s的使用。

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,求其体积。 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,求其体积。


main()
{float radius,high,vol,pi=3.1415926;
printf("Please input radius & high: ");
scanf("%f%f",&radius,&high);
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() 函数的格式和使用方法。


#include "stdio.h"
main()
{char ch1='N', ch2='E', ch3='W';
putchar(ch1); putchar(ch2); putchar(ch3);
putchar('\n');
putchar(ch1); putchar('\n');
putchar('E'); putchar('\n');
putchar(ch3); putchar('\n');
}
程序运行结果如下:
NEW
N
E
W
1.putchar()函数的格式: putchar(ch);
其中ch可以是一个字符变量或常量,也可以是一个转义字符。
2.putchar()函数的作用:向终端输出一个字符。
(1)putchar()函数只能用于单个字符的输出,且一次只能输出一个字符。另外,从功能角度来看,printf()函数可以完全代替putchar()函数。
(2)在程序中使用putchar()函数,务必牢记:在程序(或文件)的开头加上编译预处理命令(也称包含命令),即:
#include "stdio.h"
表示要使用的函数,包含在标准输入输出(stdio)头文件(.h)中。
4.3.2 单个字符的输入──getchar()函数
[案例3.9] 说明getchar()函数的格式和作用。


#include "stdio.h"
main()
{char ch;
printf("Please input two character: ");
ch=getchar();
putchar(ch);putchar('\n');
putchar(getchar());
putchar('\n');
}
程序运行情况如下:
Please input two characters: ab↙
a
b
1.getchar()函数的格式:getchar();
2.getchar()函数的作用:从系统隐含的输入设备(如键盘)输入一个字符。另外,从功能角度来看,scanf()函数可以完全代替getchar()函数。
(1)getchar()函数只能用于单个字符的输入,一次输入一个字符。
(2)程序中要使用getchar()函数,必须在程序(或文件)的开头加上编译预处理命令:
#include "stdio.h“
[Return]
4.4 顺序结构程序设计
在顺序结构程序中,各语句(或命令)是按照位置的先后次序,顺序执行的,且每个语句都会被执行到。
[案例3.10] 输入任意三个整数,求它们的和及平均值。 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。


#include "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;
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码值。

#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,97
A,65
在顺序结构程序中,一般包括以下几个部分:
1.程序开头的编译预处理命令。
在程序中要使用标准函数(又称库函数),除printf()和scanf()外,其它的都必须使用编译预处理命令,将相应的头文件包含进来。。
2.顺序结构程序的函数体中,是完成具体功能的各个语句和运算,主要包括:
(1)变量类型的说明。
(2)提供数据语句。
(3)运算部分。
(4)输出部分。
[Return]
良 好 的 源 程 序 书 写 风 格
──顺序程序段左对齐
顺序程序段中的所有语句(包括说明语句),一律与本顺序程序段的首行左对齐。
[Return](共52张PPT)
第04-06章 C程序流程设计
4.4 C语句概述
C语句:以“;”作分隔符,编译后产生机器指令.
C语句分类
表达式语句:表达式加分号构成。
空语句: ;
程序控制语句(9种):
if( )~else~
switch
for( )~
while( )~
do~while( )
continue
break
goto
return
分支
循环
辅助控制
如 total=total+limit;
a=3;
func( );
printf(“Hello,world!\n”);
复合语句:用 {…}括起来的一组语句
一般形式: { [数据说明部分;]
执行语句部分;
}
说明:
“}”后不加分号
语法上和单一语句相同
复合语句可嵌套
4.5 程序的三种基本结构
结构化程序设计
基本思想:任何程序都可以用三种基本结构表示,限制使用无条件转移语句(goto)
结构化程序:由三种基本结构反复嵌套构成的程序叫~
优点:结构清晰,易读,提高程序设计质量和效率
三种基本结构
顺序结构
A
B
A
B
流程图
N-S图
P
A
B


P
B
A


选择结构
k
A1
A2
Ai
An
k=k2
k=k1
k=kn
k=ki
...
...
二分支选择结构
多分支选择结构
循环结构
当型循环结构
直到型循环结构
P
A


当P为真
A
A
P


A
直到P为真
注:A,B,A1….An可以是一个简单语句,也可以是一个基本结构
第5章 选择型程序设计
if语句(条件选择语句)
if语句的三种形式
形式一:
格式:if (expression)
statement
执行过程:
expr
statement
非0
=0
例:if (x>y)
printf(“%d”,x);
expr
statement1
statement2
非0
=0
形式二:
格式:if (expression)
statement1
else
statement2
执行过程:
例:if (x>y) max=x;
else max=y;
形式三:
格式:
if ( expr1 ) statement1
else if (expr2 ) statement2
else if (expr3 ) statement3
…...
[ else statementn ]
expr1
statemnt1
非0
=0
expr2
expr3
statemntn
statemnt3
statemnt2
非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;
else
x++; y++;
printf(“%d,%d\n”,x,y);
}
Compile Error!

#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

#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");
else
printf("a!=b\n");
}
例 输入两个数并判断两数相等否
运行:Enter integer a:12
Enter integer b:12
a==b
运行:Enter integer a:12
Enter integer b:9
a!=b

#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 character
if语句嵌套:
一般形式:
if (expr1)
if (expr2) statement1
else statement2
else
if(expr3) statement3
else statement4
内嵌if
内嵌if
if (expr1)
if (expr2)
statement1
else
statement2
内嵌if
if (expr1)
if (expr2)
statement1
else
statement3
内嵌if
if (expr1)
statement1
else
if(expr3)
statement3
else
statement4
内嵌if
例 输入两数并判断其大小关系

#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("Xelse
printf("X==Y\n");
}
运行:Enter integer x,y:12,23
XEnter integer x,y:12,6
X>Y
Enter integer x,y:12,12
X==Y
if ~ else 配对原则:缺省{ }时,else总是和它上面离它最近的未配对的if配对
if(……)
if(……)
if(……)
else…...
else…...
else…...
例: if (a==b)
if(b==c)
printf(“a==b==c”);
else
printf(“a!=b”);
修改: if (a==b)
{ if(b==c)
printf(“a==b==c”);
}
else
printf(“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;
else
if(v2) x=10;
x=-1;
printf(“%d”,x);
}
结果:-1
switch语句(开关分支语句)
一般形式:
switch( 表达式)
{ case E1:
语句组 1;
break;
case E2:
语句组 2;
break;
…….
case En:
语句组 n;
break;
[default:
语句组 ;
break;]
}
执行过程:
switch
表达式
语句组1
语句组2
语句组n
语句组
…...
E 1
E 2
En
default
case
说明:
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

#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语句构成循环,求

#include
main()
{ int i,sum=0;
i=1;
loop: if(i<=100)
{ sum+=i;
i++;
goto loop;
}
printf("%d",sum);
}
sum=0+1
sum==1+2=3
sum=3+3=6
sum=6+4
……
sum=4950+100=5050
循环初值
循环终值
循环变量增值
循环条件
循环体
例 从键盘输入一组数据,以0结束输入,求数据和

#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循环求

#include
main()
{ int i,sum=0;
i=1;
while(i<=100)
{ sum=sum+i;
i++;
}
printf("%d",sum);
}
循环初值
循环终值
循环变量增值
循环条件
循环体
例 显示1~10的平方

#include
main()
{ int i=1;
while(i<=10)
{ printf("%d*%d=%d\n",i,i,i*i);
i++;
}
}
运行结果:
1*1=1
2*2=4
3*3=9
4*4=16
5*5=25
6*6=36
7*7=49
8*8=64
9*9=81
10*10=100
do~while语句
一般形式:
do
循环体语句;
while(表达式);
执行流程:
do
循环体
expr
假(0)
真(非0)
while
特点:先执行循环体,后判断表达式
说明:
至少执行一次循环体
do~while可转化成while结构
expr
循环体
假(0)
真(非0)
循环体
While循环
例 用do~while循环求

#include
main()
{ int i,sum=0;
i=1;
do
{ sum+=i;
i++;
}while(i<=100);
printf("%d",sum);
}
例 while和do~while比较

#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)
for
expr1
expr3
for语句一般应用形式:
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);
}
例:#include
main( )
{ int i=0;
for(i=0;i<10;i++)
putchar(‘a’+i);
}
运行结果:abcdefghij
例:#include
main( )
{ int i=0;
for(;i<10;i++)
putchar(‘a’+i);
}
例:#include
main( )
{ int i=0;
for(;i<10;)
putchar(‘a’+(i++));
}
例:#include
main( )
{ 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);
}
}
#include
main()
{ 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)梯形法求数值积分
0
y
x
a
a+h
a+ih
a+(i+1)h
b
f(x)
循环的嵌套
三种循环可互相嵌套,层数不限
外层循环可包含两个以上内循环,但不能相互交叉
嵌套循环的执行流程
(1) while()
{ ……
while()
{ ……
}
…...
}
(2) do
{ ……
do
{ ……
}while( );
…...
}while( );
(3) while()
{ ……
do
{ ……
}while( );
…….
}
(4) for( ; ;)
{ ……
do
{ ……
}while();
……
while()
{ ……
}
…...
}
嵌套循环的跳转
禁止:
从外层跳入内层
跳入同层的另一循环
向上跳转
例 循环嵌套,输出九九表
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
2
4
6
8
10
12
14
16
18
3
6
9
12
15
18
21
24
27
9
18
27
36
45
54
63
72
81
……………..
i
j

#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<10
printf
假(0)
真(非0)
i=1
j++
j=1
j<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)
while
do
……
break;
…...
expr
假(0)
真(非0)
while
expr2
……
break;
…...
假(0)
真(非0)
for
expr1
expr3
switch
expr
语句组1
break;
语句组2
break;
语句组n
break;
语句组
break;
…...
const 1
const 2
const n
default
case
例 break举例:输出圆面积,面积大于100时停止
#define PI 3.14159
main()
{
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');
else
break;
}
}
continue语句
功能:结束本次循环,跳过循环体中尚未执行的语句,进行下一次是否执行循环体的判断
仅用于循环语句中
expr
……
continue;
……
假(0)
真(非0)
while
真(非0)
do
……
continue;
…...
expr
假(0)
while
expr2
……
continue;
…...
假(0)
真(非0)
for
expr1
expr3
例 求输入的十个整数中正数的个数及其平均值

#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-6
pi=pi+t
n=n+2
s=-s
t=s/n
pi=pi*4
输出pi
分子:1,-1,1,-1…
分母:1,3,5,7,...
例 求Fibonacci数列:1,1,2,3,5,8,……的前40个数
f1=1,f2=1
for i=1 to 20
输出f1,f2
f1=f1+f2
f2=f2+f1
1
5
34
233
1597
10946
75025
514229
3524578
24157817
1
8
55
377
2584
17711
121393
832040
5702887
39088169
2
13
89
610
4181
28657
196418
1346269
9227465
63245986
3
21
144
987
6765
46368
317811
2178309
14930352
102334155
例 译密码
例如 Hello,world!
译成密码:Lipps,asvph!
同课章节目录