一、算法是程序的灵魂
1、什么是算法
一个程序包括以下两个方面的内容:
- 对数据的描述
- 在程序中要指定数据的类型和数据的组织形式,即 数据结构
- 对操作的描述
- 即操作步骤,也就是 算法
著名计算机科学家沃思提出一个公式:
实际上,一个程序除了以上两个主要要素之外,还应当采用适当的程序设计方法进行程序设计,并且用某一种计算机语言表示
在这 4 个方面中, 算法是灵魂,数据结构是加工对象,语言是工具,编程需要采用合适的方法
广义地说,为解决一个问题而采取的方法和步骤,都称为“算法”
计算机算法可分为两大类别:
- 数值运算算法
- 数值运算的目的是求数值解
- 非数值运算算法
- 最常用于事务管理领域
2、怎样表示算法
- 用自然语言表示算法
- 用流程图表示算法
- 用 N-S 流程图表示算法
- 全部算法写在一个矩形框内,在该框内还可以包含其他的从属于它的框,或者说,有一些基本的框组成一个大的框,这种流程图又称为 N-S 结构化流程图
- 这种图适用于结构化程序设计,而且作图简单,占面积小,一目了然
- 用伪代码表示算法
- 伪代码 是介于自然语言和计算机语言之间的文字和符号来描述算法
if x is positive then
print x
else
print -x
N-S 流程图用以下流程图符号:
1)顺序结构用如图形式表示
2)选择结构用如图形式表示
3)循环结构用如图形式表示
二、程序的三种基本结构
- 顺序结构
- 各操作步骤是顺序执行的
- 顺序结构是最简单的一种基本结构
- 选择结构
- 选择结构又称为判断结构或分支结构
- 根据是否满足给定的条件而从两组操作中选择一种操作
- 无论条件是否成立,只能执行两个操作之一,不可能同时执行两种操作
- 无论执行哪种操作,执行完一种操作之后,就结束了
- 两个操作可以有一个是空操作,即不执行任何操作
- 循环结构
- 又称为重复结构,即在一定条件下反复执行某一部分的操作
用这三种基本结构构成的程序称为“ 结构化程序 ”
凡是能提供实现三种基本结构的语句的语言,称为 结构化语言 ,故 C 语言是结构化语言
三、C 语句综述
C 语言的语句用来向计算机系统发出操作指令
一个语句编译后产生若干条机器指令
一个实际的程序应当包含若干语句
一个程序是一个或多个函数组成的
在一个函数的函数体中一般包括 “ 声明部分 ” 和 “ 执行部分 ”
执行部分是由若干个语句组成的
C 语句都是用来完成一定操作任务的
声明部分的内容不称为语句
C 语句分为以下 5 类
- 控制语句
- 控制语句用于完成一定的控制功能
- C 语句只有 9 中控制语句
- if() ...... else ......
- 条件语句,用来实现选择结构
- switch
- 多分支选择语句
- for()......
- 循环语句,用来实现循环结构
- while()......
- 循环语句,用来实现循环结构
- do......while()
- 循环语句,用来实现循环结构
- continue
- 结束本次循环语句
- break
- 中止执行 switch 或 循环语句
- return
- 从函数返回的语句
- goto
- 转向语句,现已基本不用了
- if() ...... else ......
- 函数调用语句
- 函数调用语句由一个函数调用加一个分号构成
- 表达式语句
- 表达式语句由一个表达式加一个分号构成,最典型的是由赋值表达式构成一个赋值语句
- 函数调用语句也是属于表达式语句
- 空语句
-
;
-
- 复合语句
- 可以用 {} 把一些语句括起来成为复合语句
- 复合语句中最后一个语句中最后的分号不能忽律不写
C语言允许一行写几个语句,也允许一个语句拆开写在几行上!!!
四、赋值表达式和赋值语句
1、赋值表达式
1)赋值运算符
赋值符号 = 就是赋值运算符,作用是将一个数据赋值给一个变量
2)复合的赋值运算符
在赋值符 = 之前加上其他运算符,可以构成复合的运算符
3)赋值表达式
由赋值运算符将一个变量和一个表达式连接起来的式子称为 “ 赋值表达式 ”。
一般形式: 变量 赋值运算符 表达式
- 赋值运算符左侧的标识符称为 左值
- 赋值运算符右侧的表达式称为 右值
- 变量可以作为左值也可以作为右值,但表达式与常量不能作为左值
- 凡是左值都可以作为右值
- 赋值表达式可以作为左值,但必须添加括号
2、赋值过程中的类型转换
如果赋值运算符两侧的类型一致,则直接进行赋值
如果赋值运算符两侧的类型不一致,但都是数值型或字符型时,在赋值时要进行类型转换
类型转换是由系统自动进行的
转换规则
- 将实型数据(包括单、双精度)赋值给整型变量时,先对实数取整,然后赋予整型变量
- 将整型数据赋值给单精度或双精度型变量时,数值不变,但以实数形式存储到变量中
- 将一个双精度型数据赋值给单精度变量时,截取其前面 7 位有效数字,存放到单精度变量的存储单元(4字节)中,但应注意数值范围不能溢出
- 将一个单精度数据赋值给双精度型变量时,数值不变,有效位数扩展到 16 位,在内存中以 8 字节存储
- 字符型数据赋值给整型变量时,将字符的 ASCII 码赋值给整型变量
- 将一个占字节多的整型数据赋值给一个占字节少的整型变量或字符变量,只将其低字节原封不动地送到该变量
- 要避免这种赋值,数值可能会发生失真
- 如果将一个有符号整数赋值给无符号整型变量,或将一个无符号整数赋值给有符号整型变量,是按照存储单元字节中原样传送的
3、赋值语句
赋值语句由赋值表达式加上一个分号构成
赋值表达式的作用是将一个表达式的值赋值给一个变量,因此赋值表达式具有计算和赋值双重功能
程序中的计算功能主要是由赋值语句来完成的
在 C 程序中,赋值语句是用得最多得语句
赋值表达式末尾没有分号,赋值语句末尾必须有分号
在一个表达式中可以包含一个或多个赋值表达式,但决不能包含赋值语句
4、变量赋初值
程序中常需要对一些变量预先设置一个初值
设置初值既可以用赋值语句去实现,也可以在定义变量的同时使变量初始化,也可以对被定义的变量中的一部分变量赋初值
一般的变量初始化不是在编译阶段完成的(只有静态存储变量和外部变量的初始化是在编译阶段完成的),而是在程序运行时执行本函数时赋予初值的,相当于有一个赋值语句
五、数据输入输出的概念
输入输出是程序中最基本的一种操作,几乎每一个 C 程序都包含输入输出
- 所谓输入输出是以计算机主机为主体而言。
- 从计算机向输出设备(如显示器、打印机等)输出数据称为 输出
- 从输入设备(如键盘、鼠标、磁盘、光盘、扫描仪等)向计算机输入数据称为 输入
- C 语言本身不提供输入输出语句
- 输入输出操作是由 C 函数库中的函数来实现的
- 在 C 标准函数库中提供了一些输入输出的函数
- printf 和 scanf 不是 C 语言的关键字,只是库函数的名字
- 在使用系统库函数时,应当在程序中使用预编译指令 “ #include ”
- 目的是将有关的 “头文件” 的内容包括到用户源文件中
- 在头文件中包含了调用函数时所需的相关信息
- #include 指令都是放在程序的开头,因此这类文件被称为 “ 头文件 ”
- “ stdio.h ” 头文件包含了与标准 I/O 有关的变量定义和宏定义以及对函数的声明
C 提供的函数以库的形式存放在 C 的编译系统中,它们不是 C 语言文本中的组成部分
在不同的编译系统所提供的函数库中,函数的数量、名字和功能是不完全相同的。其中有些通用的函数,各种编译系统都提供,成为各种系统的标准函数
C 语言函数库中有一批 “标准输入输出函数”,它是以标准的输入输出设备为输入输出对象的
输入输出相关函数:
- putchar(输出字符)
- getchar(输入字符)
- printf(格式输出)
- scanf(格式输入)
- puts(输出字符串)
- gets(输入字符串)
- ……
两种 #include 指令形式的区别:
- 用尖括号形式(如 <stdio.h> )时,编译系统直接找到 C 编译系统所在的子目录(C 库函数头文件一般都是和 C 编译系统存放在同一个子目录中的)中去找所要包含的文件,这种方式称为 “ 标准方式 ”
- 用双撇号形式(如“ stdio.h ”),在编译时,系统先在用户当前目录中寻找要包含的文件,若找不到再按标准形式查找
一般情况下,用 #include 指令是为了使用系统函数库,因此要包含系统提供的相应头文件,所以用标准形式为宜,以提高效率
若想包含的头文件不是系统提供的相应头文件,而是自己编写的文件(一般都·存放在用户当前目录中),这是应当使用双撇号形式,否则会找不到文件。若文件不在当前目录中,可以在双撇号中写出文件路径,以便系统能从中找到所需文件
六、字符数据的输入输出
1、用 putchar 函数输出一个字符
#include <stdio.h>
int main(){
char a,b,c;
a = 'B';
b = 'O';
c = 'Y';
putchar(a);
putchar(b);
putchar(c);
putchar('\n');
return 0;
}
运行结果:
BOY
修改程序
#include <stdio.h>
int main(){
char a,b,c;
a = 66;
b = 79;
c = 89;
putchar(a);
putchar(b);
putchar(c);
putchar('\n');
return 0;
}
运行结果:
BOY
分析:
在程序中整型数据与字符数据是相通的,而 putchar 只能输出字符,不能输出整数
结论:
puchar 函数的参数可以是字符变量、整型变量(其值在字符的 ASCII 码范围内)、字符常量、整型常量、转义字符
2、用 getchar 函数输出一个字符
getchar 函数没有参数,其得到的结果就是从输入设备得到的字符
getchar 函数只能接收一个字符
#include <stdio.h>
int main(){
char a,b,c;
a = getchar();
b = getchar();
c = getchar();
putchar(a);
putchar(b);
putchar(c);
putchar('\n');
return 0;
}
运行结果:
BOY (输入三个字母,回车即可)
BOY
在用键盘输入信息时,并不是在键盘上敲一个字符,该字符就立即送到计算机中的
这些字符先暂存在键盘的缓冲器中,只有按了 Enter 键才把这些字符一起输入到计算机中,按先后顺序分别赋值给对应的变量
执行 getchar 函数不仅可以从输入设备获得一个可显示的字符,而且可以获得在屏幕上无法显示的字符,如控制字符
用 getchar 函数得到的字符可以赋值给一个字符变量或整型变量,也可以不赋值给任何变量,而是作为表达式的一部分,在表达式中利用它的值
七、简单的格式输入输出
1、用简单的 printf 函数输出数据
putchar 函数只能输出一个字符,而 printf 函数可以输出多个任意类型的数据
格式:
printf(格式控制,输出列表);
- 格式控制
- 是用双撇号括起来的一个字符串,称 “ 转换控制字符串 ”,简称 “ 格式字符串 ”
- 两个部分
- 格式声明
- 格式声明由 “%” 和格式字符组成
- 作用是将输出的数据转换为指定的格式输出
- 普通字符
- 需要原样输出的字符
- 格式声明
- 输出列表
- 是需要输出的一些数据,可以是常量、变量或表达式
基本的格式字符
- d 格式字符
- 按十进制整型数据的实际长度输出
- i 格式字符
- 按十进制整型数据的实际长度输出
- c 格式字符
- 用来输出一个字符
- s 格式字符
- 用来暑促一个字符串
- f 格式字符
- 用来输出实数,以小数形式,可以不指定输出数据的长度,由系统自动指定
- 实数中整数部分全部输出,小数部分输出 6 位
- e 格式字符
- 指定以指数形式输出实数
- 可以不指定输出数据所占的宽度和数字部分的小数位数
- 格式字符 e 也可以写成大写 E 形式,此时输出的数据中的指数不是以 e 表示而以 E 表示
用 printf 函数输出字符数据
#include <stdio.h>
int main(){
char c = 'a';
int i = 97;
printf("c = %c,c = %d\n",c,c);
printf("i = %c,i = %d\n",i,i);
return 0;
}
运行结果:
c = a,c = 97
i = a,i = 97
分析输出实数时的有效位数
#include <stdio.h>
int main(){
float a,b;
a = 111111.111;
b = 222222.222;
printf("%f\n",a + b);
return 0;
}
运行结果:
333333.328125
输出双精度数时的有效位数
#include <stdio.h>
int main(){
double a,b;
a = 11111111.11111111;
b = 22222222.22222222;
printf("%f\n",a + b);
return 0;
}
运行结果:
33333333.333333
2、用简单的 scanf 函数输入数据
1)scanf 函数的一般形式
scanf(格式控制,地址列表);
地址列表是由若干个地址组成的表列,可以是变量的地址或字符串的首地址
用 scanf 函数输入数据
#include <stdio.h>
int main(){
int a,b,c;
scanf("%d%d%d".&a,&b,&c);
printf("a = %d,b = %d,c = %d\n",a,b,c);
return 0;
}
运行结果:
3 4 5
a = 3,b = 4,c = 5
输入数据时,在两个数据之间以一个或多个空格分隔,也可以以 Enter 键或 Tab 键来分隔输入的数据(不可使用逗号分隔,若在格式控制部分中有加入 ” , “ 才可以有,但此时并不是分隔符,而是普通字符而已)
2)scanf 函数中的格式声明
- scanf 函数中的 “格式控制” 后面应当是变量地址,而不是变量名
- 如果在 “格式控制字符串” 中除了格式声明以外还有其他字符,则在输入数据时在对应位置应输入与这些字符相同的字符
- 在用 “%c” 格式声明输入字符时,空格字符和 “转义字符” 都作为有效字符输入
- 在连续输入 数值 时,在两个数值之间需要插入分隔符,以使系统区分两个值
- 在连续输入 字符 时,在两个字符之间不需要插入分隔符
- 在输入数值数据时,空格键、Enter 键、Tab 键或遇到不合要求的输入,认为该数据结束
- 对 unsigned 型变量所需的数据,可以用 “ %u ” 或 “ %d ” 格式输入
八、顺序结构程序设计举例
输入三角形的三个边长,求三角形面积
#include <stdio.h>
#include <math.h>
int main(){
double a,b,c,s,area;
scanf("%lf,%lf,%lf",&a,&b,&c);
s = (a + b + c) / 2.0;
area = sqrt(s * (s - a) * (s - b) * (s - c));
printf("a=%f\nb=%f\nc=%f\narea=%f\n",a,b,c,area);
return 0;
}
运行结果:
5,6,7
a=5.000000
b=6.000000
c=7.000000
area=14.696938
printf("a =%7.2f\nb =%7.2f\nc=%7.2f\narea=%7.2f\n",a,b,c,area);
9.89,12.56,8.76
a = 9.89
b = 12.56
c = 8.76
area= 43.11
这种用来丰富格式字符功能的附加字符称为”修饰符“,上面用的小写字母 l 也是修饰符,一般表示为 “ %m.nf ” ,其中 m 必须大于 n
从键盘输入一个大写字母,要求改用小写字母输出
#inluce <stdio.h>
int main(){
char c1,c2;
c1 = getchar();
printf("%c,%d\n",c1,c1);
c2 = c1 + 32;
printf("%c,%d\n",c2,c2);
return 0;
}
运行结果:
AA,65
a,65
求 方程的根
a、b、c 由键盘输入,设
如果
则一元二次方程有两个实根:
可以将上面的分式分为两项:
#include <stdio.h>
#include <math.h>
int main(){
double a,b,c,disc,x1,x2,p,q;
scanf("a=%lf,b=%lf,c=%lf",&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("x1 = %5.2f\nx2 = %5.2f\n",x1,x2);
return 0;
}
运行结果:
a=1,b=3,c=2
x1 = -1.00
x2 = -2.00
九、提高部分
1、关于无符号数据与有符号数据之间的赋值
1)将有符号整数赋值给长度相同的无符号整型变量
按字节原样赋值(连原有的符号位也作为数值一起传送)
有符号整数赋值给无符号整型变量,数据会失真
#include <stdio.h>
int main(){
unsigned short a;
short int b;
b = -1;
a = b;
printf("%u\n",a);
return 0;
}
运行结果
如果 b 为正值,且在 0 ~ 32767(0 ~ )之间,则赋值后数值不变
2)将无符号整数赋值给长度相同的有符号整型变量
应注意不要超出有符号整型变量的数值范围,否则会出错
#include <stdio.h>
int main(){
unsigned short a;
short int b;
a = 65535; // 2 字节,16 位全为 1
b = a; // -32768 ~ 32767
printf("%a\n",b);
return 0;
}
运行结果
-1
2、较复杂的输入输出格式控制
1)输出数据时的格式控制
- %md
- 用来指定输出数据的宽度,m 是指定的宽度,如果数据实际的位数小于 m,则左端补以空格,所大于 m,则按实际位数输出
- %ld
- 在输出长整型数据时在格式字符 d 前面加一个小写字母 l
- %o
- 以八进制形式输出,输出的数字不带符号
- %x
- 以十六进制形式输出
- %u
- 用来输出无符号型数据,以十进制整数形式输出
- %mc
- 用来指定输出字符数据的宽度
- %ms
- 指定输出的字符占 m 列
- 如果字符串本身长度大于 m,则突破 m 的限制;若小于 m,则左补空格
- %-ms
- 若串长小于 m,则在 m 列范围内,字符串向左靠,右补空格
- %m.ns
- 输出占 m 列,但只取字符串中左端 n 个字符
- 这 n 个字符输出在 m 列的右侧,左补空格
- %-m.ns
- 输出占 m 列,但只取字符串中左端 n 个字符
- 这 n 个字符输出在 m 列的右侧,右补空格
- 如果 n > m,m 自动取 n 值,即保证 n 个字符正常输出
- %m.nf
- 指定输出的实数共占 m 列,其中有 n 位小数
- 如果数值长度小于 m,则左端补空格
- %-m.nf
- 指定输出的实数共占 m 列,其中有 n 位小数
- 如果数值长度小于 m,则右端补空格
- %m.ne 和 %-m.ne
- m、n 和 "-" 字符的含义与前相同
- 此处 n 指拟输出的数据的小数部分的小数位数
- 凡未指定 n ,则默认 n = 6
- 有的 C 系统的输出格式与此略有不同
- %g
- 用来输出实数,根据数值的大小,自动选 f 格式或 e 格式,且不输出无意义的零
一个有符号整数(int 型)也可以用 %u 格式输出;一个无符号数据也可以用 %d 格式输出
无符号型数据也可以用 %o 或 %x 格式输出
无符号数据的输出
#include <stdio.h>
int main(){
unsigned short a = 65535;
short 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);
return 0;
}
运行结果
a=-1,177777,ffff,65535
b=-2,177776,fffe,65534
字符串的输出
#include <stdio.h>
int main(){
printf("%3s,%7.2s,%.4s,%-5.3s\n","CHINA","CHINA","CHINA","CHINA");
return 0;
}
运行结果:
CHINA, CH,CHIN,CHI
输出实数时指定输出两位小数
#include <stdio.h>
int main(){
float f = 123.456;
printf("%f %10f %10.2f %.2f %-10.2f\n",f,f,f,f,f);
return 0;
}
运行结果:
123.456001 123.456001 123.46 123.46 123.46
求 3 个圆的周长,输出结果时上下按小数点对齐,取两位小数
#include <stdio.h>
#define PI 3.1415926
int main(){
double r1 = 1.53,r2 = 21.83,r3 = 123.71,s1,s2,s3;
s1 = 2.0 * PI * r1;
s2 = 2.0 * PI * r2;
s3 = 2.0 * PI * r3;
printf("r1=%10.2f\nr2=%10.2f\nr3=%10.2f",r1,r2,r3);
return 0;
}
运行结果:
r1= 1.53
r2= 21.83
r3= 123.71
注意:
- 除了 X、E、G 外,其他格式字符必须用小写字母
- 可以在 printf 函数中的 ”格式控制“ 字符串内包含 “转义字符”
- d、o、x、u、c、0、s、f、e、g 等格式符,如用在 % 后面就成为格式声明
- 如果想输出字符 % ,则应该在 ”格式控制“ 字符串中用连续两个 % 表示
2)输入数据格式控制
- 对无符号型变量所需的数据,可以用 %u 、%d 或 %o 、%x 格式输入
- 可以指定输入数据所占的列数,系统自动按它截取所需数据
- 如果在 % 后面有一个 * 附加说明符,表示跳过它指定的列数
- 输入数据时不能规定精度
十、习题
1、用下面的 scanf 函数输入数据,使 a =3 , b = 7 , x = 8.5 , y = 71.82 , c1 = 'A' ,c2 = 'a'
#include <stdio.h>
int main(){
int a,b;
float x,y;
char c1,c2;
scanf("a=%d b=%d",&a,&b);
scanf(" %f %e",&x,&y);
scanf(" %c %c",&c1,&c2);
printf("%d %d %f %e %c %c",a,b,x,y,c1,c2);
return 0;
}
运行结果:
a=3 b=7 8.5 71.82 A a
3 7 8.500000 7.182000e+001 A a
2、输入一个华氏温度,输出摄氏温度
#include <stdio.h>
int main(){
double f,c;
scanf("%lf",&f);
c = (5.0 / 9.0) * (f - 32);
printf("华氏度:%.2lf,摄氏度:%.2lf",f,c);
return 0;
}
运行结果
98
华氏度:98.00,摄氏度:36.67
一 叶 知 秋,奥 妙 玄 心