1
第 4章 最简单的 C程序设计
——顺序结构程序设计
2
4.1 C语言语句概述声明与说明语句表达式语句复合语句控制语句空语句
1,语句分类语句是构造程序的基本成分。 C 语言中的各种语句从构成上看可以分为 5大类:
3
空语句形式,;
功能:
作为一个独立的语句,没有动作效果。其实分号是语句间的分隔符,或语句的结束标志。
例如:; ; ;
表示有三条语句,它们都是 空语句。
4
复合语句用大括号,{…… }”将一组语句括起来组成的一个语句,又称分程序或程序块。 在程序中作为一条语句看待,或者说在语法上与一条语句的作用相同。
一般形式:
{
声明序列语句序列
}
注意,因为复合语句是一条语句,最后的大括 号 }
表示复合语句的结束,故不要在 } 后再写分号 。
5
if ( c<=10 )
{
int k=c ;
printf ( ―%d‖,k) ;
}
else
{
z = x+y ;
t = z/100 ;
printf ( ―%f‖,t ) ;
}
例如:
6
1)因复合语句是一种特殊语句,它的语句结束标志是最后的大括号,因此在复合语句的后面不要再以分号结束。 但是多加了一个分号对程序没有影响,只是作为空语句。
2)在复合语句中可以定义变量,不过在其中声明的变量的作用域只是该复合语句。
3)凡是可以出现单条语句的地方都可以使用复合语句。
注意:
7
表达式语句表达式语句是 C语言中最基本的语句之一。 所谓 表达式语句就是在任何一个表达式后面加上一个分号构成的语句。 一般形式是:
e ;
这里的 e 表示任何合法的表达式。
例如,假定 int x=1,y=2 ; 以下均为正确的表达式语句:
2 ; x ; x+1 ; sqrt(x) ;
x++ ; y=x ; y= sqrt(x) ; x=2,y*=x ;
x == 0 ; x>0 && y>0 ;
8
但是这些例子中的,
2;,x;,x+1;,sqrt(x);,x == 0 ;,x>0 && y>0 ;
表达式语句,虽然是正确的表达式语句,但对程序并不做什么事情,所以对程序而言它们是无用的语句。
另外,象表达式 a=b 与表达式语句 a=b; 虽然只相差一个分号,但它们是完全不同的,其作用与使用位臵不一样。
例如,a=b? 1,0
其中的 a=b 是一个表达式,但如果写成,a=b ;? 1,0
那就错了,因为在这个位臵处只能是表达式而不能是语句。 C中类似这样的情况很多,要特别注意。
9
声明与说明语句声明与说明语句不是表达式语句,如果去掉后面的分号,得到的并不是表达式。它们的作用只是建立变量,非动作语句。例如,int a ;
控制语句
C 有 9 种控制语句,它们是:
if ( )? else? (条件语句 )
for ( )? (循环语句 )
while ( )? (循环语句 )
do? while ( ) (循环语句 )
goto (转向语句 )
break (中止执行 switch或循环语句 )
switch (多分支选择语句 )
continue (结束本次循环语句 )
return ( 从函数返回语句 )
10
2、顺序结构程序的特点
a) 由若干语句组成,一个入口,一个出口。
b) 每个语句均能够被执行并且只执行一次。
c) 语句的执行顺序与排列顺序一致。
d) 语句顺序执行,不会发生程序控制转移。
3、可用于顺序结构程序设计的 C语言语句表达式语句函数调用语句复合语句空语句
11
4.2 赋值语句与函数调用语句赋值语句赋值语句是表达式语句的特例。
1) 与赋值表达式不同,赋值表达式有值,有类型;
而赋值语句无值,也无类型。
2) 赋值表达式可作运算操作数;而赋值语句不能。
例如,if ( ( a=b) > 0 ) t = a ;
/* 这是允许的 */
而如果写成:
if ( ( a=b; ) > 0 ) t = a ; /*错误的,它不是表达式 */
12
函数调用与函数调用语句函数调用函数调用将引起函数的执行。一般调用形式是:
函数名(实参表)
这种调用形式适用于任何函数的调用(有返回值的函数调用 /无返回值函数调用)。例如,
printf ( ―%d‖,x ) ;
有返回值的函数调用表达式,可以出现在其他表达式中作为运算分量参与运算。例如:
y = sin (1) + 3 ;
n = printf ( ―%d‖,x ) /2 ;
13
void f ( int x ) /* void 表示函数 f 无返回值 */
{
printf ( ―%d‖,x ) ;
}
main ( )
{
int y ;
y = f (4) + 1 ; /* 错误 */
f (4) ; /* 正确 */
}
14
函数调用语句函数调用语句是在函数调用表达式后面加上一个分号后构成的语句。一般形式为:
函数名(实参表) ;
适用于任何函数的调用(有返回值的函数调用 /
无返回的值函数调用) 例如:
printf ( ―%d‖,x ) ;
但对于:
sin (x) ;
虽然是正确的函数调用语句,但它作为语句在程序中是无意义的。
15
4.3 输入输出的概念及在 C语言中的实现一、输入 /输出基本概念
1、输入 /输出输入,程序在运行期间接收来自程序外部的数据的过程。 (外部设备 内存 ) 。
输入来源,键盘 ( 标准输入设备文件 )磁盘文件 ( 软盘、硬盘、光盘 )
输出,程序在运行期间将数据发送到外部的过程。
(内存 外部设备 )。
磁盘文件 ( 软盘、硬盘 )
屏幕 ( 标准输出设备文件 )输出目的地:
16
2,标准 I/O函数库
C语言没有专门的输入输出语句,I/O 操作均通过调用 I/O函数库中的 I/O 函数来完成 。其 好处是用函数实现输入输出使 C语言编译系统简单,避免与硬件有关的问题,便于移植。
如果 C程序中要调用 I/O函数库中的函数进行 I/O
操作,则应在程序的开始安排如下的一条 C预处理程序命令:
#include <stdio.h> 或 #include ―stdio.h‖
stdio.h 是一个文本文件,称为标准 I/O头文件。
其中包含有标准 I/O 函数的原型说明 ( 函数的名字、
参数个数、类型等),标准 I/O函数使用的符号常量等信息。尖括号或双引号指出如何找到 stdio.h 文件。
17
流是 一种以行为单位组织的字符序列 (文本流 /字符流 ); 或者是 一种字节序列,没有行的概念,C程序读写流中的每 一个字节,不存在任何字符变换( 二进制流 /字节流 ),实际的 I/O被映射到这样的数据流。
3、流( stream)
C语言中,把 I/O设备都看成文件,称之为设备文件,键盘看作为输入设备文件,显示器看作为输出设备文件。但是程序中的 I/O 操作并不直接与具体的设备文件关联,而是通过所谓 的“流”进行读写,
“流”再与具体的设备文件关联。
18
流实际上是内存中的一个缓冲区,
一个程序在运行期间可以建立多个输入流和多个输出流。通常一个程序总是要进行 I/O,于是系统在每个程序被启动运行时,自动为其建立至少 3个流:
19
标准输入流( stdin)
与 键盘 连接,stdin是指向该流的指针。标准输入函数被调用时从 stdin指向的流中取得字符。
标准输出流( stdout)
与 显示器 连接,stdout是指向该流的指针。标准输出函数被调用时向 stdout 指向的流发送字符,
并显示在屏幕上。
标准错误输出流( stderr)
也与 显示器 连接,stderr 是指向该流的指针。
程序在运行期间向 stderr指向的流发送所有报错信息,并显示在屏幕上。
可见一个设备上可以对应多个流。
20
二、标准输入输出函数
printf ( )
putchar ( )
puts ( )
标准 I/O函数通过标准输入流( stdin) 和标准输出流( stdout) 进行输入输出操作。
常用的标准输入函数:
scanf ( )
getchar ( )
gets ( )
常用的标准输出函数:
21
4.4 字符数据的输入输出
1) putchar函数 ( 字符输出函数 )
将一个字符的编码送到标准输出流 stdout,并显示在屏幕上,正确时返回送出的字符编码,出错时返回 –1。其实,putchar不是函数,而是 stdio.h中的预处理宏定义:
#define putchar (c) putc( c,stdout )
putchar函数的每次调用,总是将一个字符送到
stdout 的当前输出位臵,不进行任何加工处理,同时将 stdout的当前位臵后移一个字符 (下一次输出字符的位臵)。调用形式:
putchar ( 整型表达式 )
22
给出的参数总是按无符号整数解释。可以是字符型数、任意的整型数。如果不在 0~ 255之间,则自动按 256取模。
如 putchar (321) 与 putchar (65) 等同,都输出字符 A 。
如果给出的是负数,则仅取其低 8 位,按无符号数解释。如 putchar (-5) 则等同 putchar (251),输出字符,√” 。
若有声明,char c =?A‘ ; 则下面都是正确的调用例子:
23
putchar ( c+1 ) ;
putchar (?A‘ ) ;
putchar (?\12‘ ) ; /* 输出换行符 */
putchar (?\xa‘ ) ; /* 输出换行符 */
putchar (?\n‘ ) ;
putchar ( 259 ) ; /* 对应 3的字符是?*/
24
2) getchar函数 (字符输入函数 )
从标准输入流 stdin中读入一个字符,返回该字符的编码,若出错或遇到文件结束符 ( DOS中是 Ctrl-z )
则返回 –1 。
getchar函数也是 stdio.h中的预处理宏定义:
#define getchar ( ) getc( stdin )
getchar 函数的每次调用,总是从 stdin 的当前位臵取一个字符 ( 新行字符也为一个字符 ),同时将
stdin 的当前位臵后移一个字符(下一次取字符的位臵)。
调用形式:
getchar ( ) /* 无参数,括号不能省 */
25
main ( )
{
char c ;
c = getchar ( ) ;
putchar (c) ;
getchar ( ) ; /*去掉上次输入的回车字符 */
putchar ( getchar( ) ) ;
}
例:
26
4.5 格式输入输出
(1) printf( 格式输出函数 )
1、调用形式:
printf ( 格式字符串,输出项表 )
或 printf ( 字符串 )
2、函数功能将输出项表中指定的内存中的数据形式,按指定的格式,转换 为字符序列(字符串)送到 stdout,
并在屏幕上 显示 。 函数的 返回值为 实际输出的字符个数 。
27
3,格式字符串格式字符串由两类符号组成:
直接字符格式转换说明符直接字符 就是任何可表示的字符,按原样或字符的功能直接输出。
格式转换说明符 用来指出如何取输出数据 (取多长 ),并按何种格式转换输出。例如:
printf ( ―ave=%f,number=%d ‖,ave,count ) ;
printf ( ―x=%2d,square=%04d,sqrt=%.2f‖,4,4*4,sqrt(4) ) ;
28
4,格式转换说明符的一般格式
% - 0 m.n l (或 h) # 数据类型转换字符
-,左端对齐,缺省时右端对齐。 [可缺省 ]
0,空位填 0,缺省时填空格。 [可缺省 ]
m,指出 显示数据的最小域宽。若显示的字符数 <m的值,则根据对齐方式在前 /后补足空格或 0。 若实际宽度 >m,则 m无效。按实际数据长度输出 [可缺省 ]
n,精度(小数位数),n前必须有小数点 [可缺省 ]
l,修饰转换字符 (long) 。 [可缺省 ]
h,修饰整型转换字符 (short)。 [可缺省 ]
#,显示八进制整数时加前缀 0,显示十六进制数时加前缀 0x [可缺省 ]
29
数据类型转换字符转换字符 显示形式
d,i 有符号十进制整数
o 八进制数
x 十六进制数
u 无符号十进制整数
c 单字符
s 字符串
f float或 double [-m.dddddd]中 d的个数由精度决定 (缺省为 6)
e [-]m.ddddde± xx,或 [-]m.dddddE± xx,d的个数由精度决定 (缺省为 5)。用于 double
g 用于 double,指数小于 -4或大于 +4时使用 %e,否则使用 %f
p 地址
30
5、输出项表
a) 输出项表给出要求输出的数据,给出的数据形式可以是,
变量,常量,函数调用,表达式
b) 输出项之间用逗号分隔。
c) 每一个输出项应与格式控制串中的格式转换字符一一对应(个数、顺序、类型)。
d) 输出项表中的表达式求值顺序是从最右边的表达式开始往左顺序求值。 例如,若有 int c = 3 ; 则下面语句:
printf ( ―%d %d \n‖,++c,++c ) ;
的输出结果是,5 和 4 而不是 4和 5 。
31
main ( )
{
int a;
printf ( ―a=%d%d‖,( (a=3*5,a*4),a+5),a ) ;
} /* 首先输出 a,而 a 此时是无定义的 */
main ( )
{
int a ;
printf ( ―a=%d %d‖,a,( (a=3*5,a*4),a+5) ) ;
} /* 右边表达式先算,计算 a = 3*5 后 a 已经有值 */
如下面的程序是错误的,
而下面的程序是正确的,
32
2) printf ( ―%u‖,-455 ) ;
输出,65081
更正:应把,%u‖ 改为,%d‖
3) printf ( ―%f‖,1234567.89 ) ;
输出,1234567,890000
4) printf ( ―%.2f‖,1234567.89 ) ;
输出,1234567,89
5) printf ( ―%e‖,1234567.89 ) ;
输出,1.23457e+06
- 1*1*32768+200000000%32768
= - 32768+5120
= - 276486,printf函数使用例
1) printf ( ―%d‖,2000000000 ) ;
输出,-27648
更正,应把 ―%d‖ 改为,%ld‖
33
main ( )
{
long double x = 123.1 ;
printf ( "%lf",x ) ;
}
该程序的输出将是,(若是 double则不必 )
-2.361660589884215120000000000000000000000e+261
main ( )
{
long double x = 123.1 ;
printf ( "%Lf",x ) ;
}
正确的做法是,
其输出结果是,123.100000
6) 若有程序
34
main ( )
{
int a ;
scanf ( "%d",&a ) ;
printf ( "%f \n",a ) ;
}
123 /* 输入的数据 */
系统将给出如下的错误信息,
printf,floating point formats not linked
Abnormal program termination
7) 整型数据不能以浮数格式输出 。例如:
同理,浮点数也不能以整型格式输出 。
35
8) long int型数据应以 %ld格式输出。如果以 %d格式输出,若数值大于 32767,则自动进行溢出处理,输出溢出处理后的值。若不溢出,则能正确输出。如:
main ( )
{
long int a ;
scanf ( ―%ld‖,&a ) ;
printf ( ―a=%d \n",a ) ;
}运行例:
32767 /* 输入的数据 */
a=32767 /* 输出的结果 */
32769 /* 输入的数据 */
a=-32767 /* 输出的结果 */
long int 数据必须以 %ld 格式读入
36
9) 若有,char c =?a‘ ; 则
printf ( ―%c,%c‖,?a‘,c ) ;
输出:
a,a
10) 若有,char str1[ ] = ―China‖,*str2 = ―Japan‖ ;
则,
printf ( ―%s\n%s\n%s\n‖,str1,str2,―France‖ ) ;
输出:
China
Japan
France
37
7、显示 域宽 和 精度 例假定有,int i = 873 ; float f = 123.45678 ;
char s[ ] = ―happy birthday‖ ;
则,
1) printf ( ―%.9d‖,i ) ;
输出,000000873 /* 对% d指定精度,效果是输出宽度 */
2) printf ( ―%.3f,%.3e‖,f,f ) ;
输出,123.457,1.23e+02 /* %e的精度是有效数字 */
3) printf ( ―%010.3f,%010.3e‖,f,f ) ;
输出,000123.457,001.23e+02
4) printf ( ―%.5s‖,s ) ;
输出,happy /*对 %s指定精度,是输出指定的字符数 */
38
要输出 %号,使用 %%。
例如,printf ( ―%,2 f %%‖,a )
格式转换字符都用小写字母。
8、使用 printf ( ) 函数的注意点通常,格式转换说明符的个数应与输出项个数、类型,顺序一致。 若输出项的个数多于格式转换说明符的个数,则不输出多余的项; 反之也有输出项,但输出内容不定且无意义。
39
(2) scanf函数 ( 格式输入函数 )
1、调用形式
scanf ( 格式字符串,输入项表 ) ;
2、函数功能从标准输入流 (stdin) 中读取若干字符,按照格式字符串中的格式说明 将读入的字符串形式的数据转换为指定类型 的数据,并保存到对应的输入项中。
scanf函数的返值是本次调用时成功输入的项目数。
若标准输入流中无任何字符,则暂停程序运行,
等待键盘输入;否则,直接取字符。
40
3、格式字符串
scanf函数的格式字符串由三类符号组成,数据项分隔字符,普通字符 及 格式转换说明符 。
a) 数据项分隔符 (也称白空字符)
指的是 空格、回车,tab 这三种字符。它们作为输入流中数据项之间的分隔符。读入时遇到空白字符将丢掉,直到遇到一个非空白字符为止。它们是输入数据时数据间的缺省分隔符。
41
b) 普通字符 ( 常规字符)
指的是除数据项分隔符、格式转换说明符和 %
号之外的所有其他字符。
它们的作用与数据项分隔符的作用基本相同,
区别在于普通字符与输入流中输入的字符是一一对应的 ; 而数据项分隔符与输入流中作为数据项分隔符的空白字符不必一一对应。
如果输入的普通字符与格式字符串的普通字符不一致将作为读入的数据而导致输入数据错 。
42
c) 格式转换说明符用来指出将输入的字符串形式的输入项或字段,
转换成为指定类型 的数据。一般格式,
% * m l( 或 h) 类型转换字符其中:
*,赋值抑制符 ( 压缩指示符 ),* 可缺省。
l (或 h),long 或 short长度修饰。用于修饰类型转换字符,可缺省。
m,域宽,当没有遇到数据分隔字符 时,从输入 流中读取 m个字符。 m可缺省 。
43
类型转换字符 作用与意义
d 转换成有符号十进制整数。
i 转换成有符号十进制整数,但数据可以十、八及十六进制形式输入。
o 转换成八进制数。
x 转换成十六进制数。
u 转换成无符号十进制整数以上类型转换说明符前均可加修饰符 l 和 h,
指示 long 和 short。
44
s 将读入的字段转换成字符串。
c 将读入的字符转换成字符型数据。
注意,若前面加修饰符 l,则转换成 double 型;若前面加修饰符 L
( 大写 ),则转换成 long double
型浮点数。
e 将读入的字段转换成浮点数。
注意,若前面加修饰符 l( 小写字母 l ),则转换成 double型 浮点数。
f 将读入的字段转换成 float浮点数 。
类型转换字符 作用与意义
45
因此,输入项表是一个变量的地址表。各输入项之间用逗号隔开 。 若把读入数据放入变量 a,
则必须给出 &a (即 a 的地址 )。输入项的个数应该与格式转换说明符一一对应(个数,顺序、类型)。
4、输入项表用来指出读入的数据经转换后存储的地方。
46
输入对象必须以地址形式给出。
5,scanf ( ) 函数使用注意点:
scanf ( ―%d%f%x‖,&a,&b) ;
输入对象的个数应该与格式转换说明符一一对应。
若不一致,多余的输入项或多余的格式转换说明符将被忽略 。例如:
除 %c 外,所有的输入字段之间都用白空字符 ( 空格、回车,tab) 作分隔符,而用 %c 输入字符时,输入的 所有 字符都作为输入数据 (回车换行也作为输入数据! )。例如:
47
main( )
{
int a,b ;
char c ;
scanf ( ― %d%c%d ‖,&a,&c,&b ) ;
printf ( ― \n%d,%c,%d\n ‖,a,c,b ) ;
}
输入的这些数据都收集在在 stdin中,则输出将是:
123,
,123
若运行该程序时,以如下形式输入数据:
123↙ ( ↙ 表示回车换行)
123↙
48
用 %s 输入字符串时,输入的字符串中间不能有空格字符(因除 %c 外,都用白空字符作分隔符,空格字符是白空字符)。
解决的办法是用另一个专用于输入字符串的函数 gets( ) 输入字符串 (该函数允许输入字符串中含有空格 )
用 %s 输入字符串时,输入的字符串不要用双引号括起来,否则作为输入内容。
例如,……char str[10];
gets( str);
……
输入的字符串,ABCD AAA BBBB
49
输入数据时不能规定精度。
scanf ( ―%5.2f ‖,&a ) ;
5.2是无效的!
但 scanf ( ―%5f ‖,&a ) ; 是正确的输入语句。
指取输入数据的前 5 个字符。如输入,1234567↙,
则变量 a 中获得是:
12345.000000
例如:
其中,
50
例如:
scanf ( ―%d%*d%f ‖,&a,&c) ;
若输入的数据是 123 456 12.911 则,
123 → a,正常读入 456,但被丢掉并不赋给任何对象 。 而 12.911→ c 。
压缩指示符 * 用来把正常读入的字段 丢掉,并不赋给某个输入对象,所以称之为赋值抑制符。当然 不能指定对应的输入项。
51
例如:
scanf ( ―d=%dc=%f ‖,&a,&c ) ;
输入时则必须按如下形式输入:
d = 123 c= 456.789 ↙
注意:
c= 之前不能有其他字符,即使是白空字符也不行!
若格式字符串中含有常规字符 (包括白空字符 ),则输入数据中必须有 完全对应 的常规字符,否则将把其后的数据作为要求的常规字符读入。
52
常规字符的实际作用是代替白空符作为输入数据之间的分隔符,只不过指定使用 特殊的分隔符 。作为这种使用,经常出现的错误用法是:
scanf ( ―%d%f \n‖,&a,&b ) ;
许多初学者习惯于在最后多加一个新行字符 \n,
这在语法上没有错,但因它现在是作为白空字符出现在格式控制串中,所以在输入数据的最后必须多输入一个回车换行符对应于它,否则系统将等待你继续输入数据。
为避免不必要的麻烦,输入时在输入格式控制字符串的最后不要加 \n 。 但在 printf 中为美观输出形式,应适当加 \n 。
53
6、关于输入字段(输入项)的说明前面多次提到 字段 这个术语,现在对它作一个说明。 一个字段是指:
a) 由一系列非白空字符 (输入数据间的分隔符 )组成。
b) 或者是从 stdin 当前读入位臵的第一个非白空字符起到下一个白空字符之间的读入字符;
c) 或者是从 stdin当前读入位臵的第一个非白空字符起,到格式转换说明中指定的宽度为止的、已读入的字符;
d) 或者是从 stdin当前读入位臵的第一个非白空字符起,一直到遇到一个用当前格式转换 字符不能转换的字符为止的已读入内容。
54
例 2:
long x ;
scanf ( ―%ld‖,&x ) ;
7、使用 scanf ( ) 函数的例子例 1:
int a,b,c,d,e,f,g ;
scanf ( ― %d%i%i%i%o%u%x ‖,&a,&b,&c,
&d,&e,&f,&g ) ;
printf ( ― %d,%d,%d,%d,%d,%d,%d ‖,
a,b,c,d,e,f,g ) ;
输入,70 70 070 0x70 70 70 70
输出,70,70,56,112,56,70,112
55
float a,b ;
scanf ( ― %e%f ‖,&a,&b ) ;
printf ( ―%f,%f ‖,a,b ) ;
char a,b[9] ; /* b[9] 是数组,最多存 8个字符 */
scanf ( ―%c%s‖,&a,b ) ;
printf ( ―%c\n%s‖,a,b ) ;
appy输出,h
输入,happy
输出,1.234567,1234.567017
输入,1.234567 1.234567e3
例 3:
例 4:
56
int a,b ;
scanf ( ―%2d%d‖,&a,&b ) ;
int a,b;
scanf ( ―a=%d,b=%d ‖,&a,&b ) ;
例 5 域宽使用例例 6 数据项分隔符使用例输入,123456
执行效果,12→ a 3456→ b
正确输入,a=123,b=456
57
例7 赋值抑制符使用例
int year,month,day ;
scanf ( ―%d-%d-%d‖,&year,&month,&day ) ;
正确输入,2000-3-1
错误输入,2000/3/1
scanf (―%d%*c%d %*c%d‖,&year,&month,
&day ) ;
输入,2000-3-1 和 2000/3/1 均可注意,使用带有赋值抑制符的格式转换说明时,不能有对应的输入项 !