PowerPoint 演示文稿
Download
Report
Transcript PowerPoint 演示文稿
第三讲
C 程序设计初步
1
主要内容
C语言概述
数据结构
程序设计流程(顺序、选择、循环)
函数
指针
文件
参
考
资
料
谭浩强,C 程序设计,第三版
B.W. Kernighan and D.M. Ritchie,The C Programming
Language (C程序设计语言) , 第二版
H. Schildt,C语言大全, 第四版
2
程序设计语言的发展
机器语言
汇编语言
CPU指令系统,由0、1构成
的指令码组成;是计算机唯
一能识别并直接执行的语言
用助记符号描述的指令系统
如 ADD, SUB;需翻译成机
器语言,符号化的机器语言
效率高
编程难
高级语言
面向过程
面向对象
面向应用
3
程序设计语言的发展
52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96
Ada
Modula-2
Pascal
ALGOL60
ALGOL68
CPLBCPL B
C
C++
Smalltalk 80
Java
Simula 67
BASIC
FORTRAN
ANSI-BASIC
FORTRAN77
QBASIC VB
FORTRAN90
PL/1
COBOL
LISP
PROLOG
4
程序设计语言的发展
FORTRAN:Formula Translation
1956年,高级语言产生的标志,简洁高效,是科学计
算的主流语言
BASIC:Beginner's All-purpose Symbolic Instruction Code
1964年,面向大众,特别是大学生
Pascal
70年代,结构化程序设计,强调语言的可读性
C
70年代,通用的、过程式的编程语言,高效、灵活,
主流的软件开发语言
5
C 语言的发展
C语言的发展历史
1969-1973,由 Dennis M. Ritchie 设计并实现
1973,UNIX的内核正式用C语言改写
1978,B. Kernighan 和 D. Ritchie《C程序设计语言》
1983-1989,ANSI C 标准形成 (C89)
1999, 发表新的标准 C99
常见的 C语言编译器
GNU C Comipler (Linux): gcc
Microsoft Visual C Comipler
Borland Turbo C
Intel C Comipler (Linux/Windows)
PGI C Comipler (Linux/Windows)
6
一个简单的编程实例
/* example: calculate the sum of a and b */
#include <stdio.h>
/* the main program */
main()
{
int a, b, sum;
}
预处理
注解语句
函数
a=10; b=24;
sum=add(a,b);
printf("sum=%d\n", sum);
/* function: sum of two integers */
int add(int x,int y)
{
int z;
z=x+y;
return(z);
}
7
C 程序结构特点
源程序结构
一个 C 源程序由一个或多个源文件组成
每个源文件可由一个或多个函数组成
一个源程序有且只能有一个main 函数,即主函数
程序执行从 main 开始,在 main 中结束
源程序中可以有预处理命令,通常应放在源文件或
源程序的最前面
8
C 程序分析
源程序书写
每个说明和每个语句都必须以分号 “ ; ” 结尾,
但预处理命令,函数头和花括号 “ } ” 之后不能加分号
标识符,关键字之间必须至少加一个空格以示间隔,
若已有明显的间隔符,也可不再加空格来间隔
一行可以写多个语句,一个语句可以分几行书写
习惯用小写字母,区分大小写
书写漂亮的 C 程序的基本要求:
注释:/* */ 为注释符, 不能嵌套
{ }对齐
不使用行号,无程序行概念
一行写写一个语句,一个语句写一行
常用锯齿形书写格式
使用 TAB 缩进
有足够的注释
9
有合适的空行
C 语言字符集
字母(大小写共 52 个)
数字
空白符(空格符、制表符、换行符)
标点和特殊字符
10
C 语言词汇
六类:标识符,关键字,运算符,分隔符,常量,注释符
标识符:用来标识变量名、函数名等的字符序列
由字母、数字、下划线组成,第一个字符必须是字母或下划线
区分大小写,不能用关键字
标准 C 不限制标识符的长度,实际长度与编译器有关
命名原则:见名知意、不宜混淆
关键字 (32个):具有特定意义的字符串,通常也称为保留字
类型说明符、语句定义符(控制命令)、预处理命令字
运算符
分隔符:逗号和空格
常量:数字、字符、字符串、转义字符、符号常量等
注释符:以“ /* ”开头并以“ */ ”结尾
11
编辑与运行 (Linux)
编写源程序,以 .c 为扩展名(可使用任何文本编辑器)
编译并连接源文件 (可以一步完成)
gcc –o 输出文件 源文件
-o :指定输出文件名,缺省为 a.out
-c :只编译不链接,即只生产目标文件(.o 文件)
-Ipath :指定或增加包含文件(如 *.h)的搜索路径
-Lpath :指定(增加)库文件的搜索路径
-lname:与库文件 libname.a 链接
-O, -O1, -O2, -O3 :优化开关
-g :在目标码中加入更多信息,用于程序调试
运行编译生成的可执行文件
12
算法
程序 = 算法 + 数据结构 + 程序设计方法 + 语言工具和环境
算法:为解决一个问题而采取的方法和步骤
结构化程序设计方法
程序结构规范、清晰、模块化
学习程序设计的目的不只是学习一种特定的语言,而是
学习进行程序设计的一般方法
掌握了算法就是掌握了程序设计的灵魂,再学习有关的
计算机语言,就能顺利编写出程序
脱离了具体的语言去学习程序设计是困难的,但学习语
言只是为了程序设计,它本身并不是目的
13
主要内容
C语言概述
数据结构
程序设计流程(顺序、选择、循环)
函数
指针
文件
14
数据结构
基本类型
整型
整型 (int)
短整型 (short)
长整型 (long)
实型
单精度 (float)
双精度 (double)
无符号 (unsigned)
有符号
字符型 (char)
C
数
据
类
型
枚举型 (enum)
数组
构造类型
结构体 (struct)
公用体 (union)
指针类型
空类型(void)
定义类型(typedef)
数据类型决定:
1. 数据占内存字节数
2. 数据取值范围
3. 可参与的运算与操作
15
基本数据类型
类型
关键字
整型
int
2
表示范围
-215 ~ 215 -1
short
2
-215 ~ 215 -1
long
4
-231 ~ 231 -1
unsigned int
2
0 ~ 216 -1
unsigned short
2
0 ~ 216 -1
0 ~ 232 -1
float
4
4 (6-7)
double
8 (15-16)
10-308 ~ 10308
unsigned long
实型
long double
字符型
所占字节数
char
16 (18-19)
10-38 ~ 1038
10-4932 ~ 104932
1
例:example_datatype.c (long double与系统及编译器有关)
16
常量
在程序运行中值不能改变的量
整型常量:整数,后面加 l 或 L 表示长整型
实型常量:双精度实数,后面加 f 或 F 表示单精度
字符型常量:用单引号括起来的单个字符
字符串常量:用双引号括起来的字符序列
符号常量:用标识符代表常量
#define PRICE 30
一般使用大写字母
是宏定义预处理命令,不是 C 语句
17
变量
值可以改变的量
变量名:要求与标识符相同
变量类型:整型、实型、字符型
变量必须先声明,后使用
变量的声明
类型关键字 变量名列表
变量声明时可以进行初始化
int i, j, k=0;
double a,b=3.1415,c;
char c;
18
类型转换
自动转换
不同类型的数据进行运算,需先转换成同一类型
转换按数据长度增加的方向进行,以保证精度不降低
所有的浮点运算都是以双精度进行的
char 型和 short 型参与运算时,必须先转换成 int 型
赋值号两边的数据类型不同时,右边的类型将转换为左边的
char, short int unsigned long double float
强制转换
(类型说明符) 表达式
将表达式的值转换成指定的类型
19
运算符
算术运算符:+、-、*、/、%、++ (自增)、-- (自减)
关系运算符:用于比较运算,>、<、= =、>=、<=、!=
逻辑运算符:用于逻辑运算,&&、||、!
位操作运算符:按二进制位进行运算,
&、|、~、^ (异或)、<< (左移)、>> (右移)
赋值运算符:
=、+=、-=、*=、/=、%= 、&=、|=、^=、>>=、<<=
条件运算符:是一个三目运算符,用于条件求值 ( ?
:)
逗号运算符:,(把若干表达式组合成一个表达式)
指针运算符:* (取内容)、& (取地址)
求字节数运算符:sizeof (计算数据类型所占的字节数)
特殊运算符:括号 (),下标 [],成员 (→,.) 等几种
20
运算优先级
高
低
详见课程主页的C语言编程讲义
() [] –> .
! ~ ++ –– – (type)
* / %
+ –
<< >>
< <= > >=
== !=
&
^
|
&&
||
? :
= += –= *= /= 等
,
*
&
sizeof
21
数组变量
一维数组的声明
类型关键字 数组名[常量表达式]
int a[10];
double b[12+21], c[16];
char ch[3*4];
一维数组的引用
数组名[常量表达式]
例:example03.c
C 语言中数组的下标是从 0 开始的
22
数组变量
二维数组的声明
类型关键字 数组名[常量表达式1][常量表达式2]
double A[8][8], B[8][8];
二维数组的引用
数组名[常量表达式1][常量表达式2]
例:example02.c
C 语言中二维数组是按行排列的
23
字符变量
字符变量的声明
char c1, c2[10], c[8][6];
每个字符变量只能存放一个字符
字符是按 ASCII 码的形式存放在内存中的
字符串和字符串结束标志
字符串是以双引号括起来的字符序列
通常用字符数组存放字符串
字符串结束标志“\0”是系统自动生成的
常用的字符串函数有(在头文件 string.h 中):
puts、gets、strcat、strcpy、strcmp、strlen
例:example04.c
24
主要内容
C语言概述
数据结构
程序设计流程(顺序、选择、循环)
函数
指针
文件
25
C 程序结构
C 程序
源程序文件 1
预处理命令
源程序文件 2
变量声明
……
……
函数 1
函数首部
源程序文件 n
函数 n
函数体
局部变量声明
执行语句
26
语句
C 语句可分为以下五类
表达式语句
表达式;
函数调用语句
函数名(实际参数表);
控制语句
条件、循环等
复合语句
将多个语句用 {} 括起来组成的一个语句
空语句
只有分号
27
赋值语句
赋值语句
变量 = 表达式;
左端只能是变量名
当表达式的数据类型与左边的变量不一致时,系统自
动将表达式的值转换为左端变量的数据类型
可以多重赋值(初始化时不能多重赋值)
x=y=z=0;
28
输出printf
输入/输出三要素:对象(数据)、格式、设备
C 语言的输入输出函数包含在头文件 “stdio.h”
中
输出函数:printf
printf(“格式控制字符串”,输出表列);
格式控制字符串:格式字符串+转义字符+普通字符串
格式字符串:以“%”开头,后面跟各种 格式字符
普通字符串:原样输出
printf(“k=%d, a=%f\n”,k, a);
29
格式字符串
%[标志][输出最小宽度][.精度][长度]类型
printf("pi= %-12.6lf, \n", pi)
格式字符
以 % 开头
标志
输出最小宽度
- :左对齐
+ :输出符号
空格、#
精度
长度
小数点后
输出位数
30
格式字符串
%[标志][输出最小宽度][.精度][长度]类型
%d、%o、%x、%X、%u ( 整数 )
%e (实数:采用科学计数法形式 )
%f (实数:采用浮点数形式)
%g ( 由系统自动选取上述两种格式之一)
%c ( 输出单个字符)
%s ( 输出字符串)
转义字符
\n (换行) \t (制表符)
\b (退格) \r (回车) \f (走纸换页)
\’ (单引号) \” (双引号) \\ (反斜杆) %% (百分号) \a (响铃 )
例:example_printf.c
31
输入scanf
输入函数:scanf
scanf(“格式控制字符串”,地址表列);
地址运算符为 “ & ”
格式控制字符串只含“格式字符串”,一般格式为
%[*][输入数据宽度][长度]类型
“*” 表示跳过该输入值
输入多个数据时,如果格式字符串之间没有间隔符,则输入
数据以空格或回车隔开;如果有间隔符,则用间隔符隔开
scanf(“%d%f”,&k,&a);
scanf(“%d,%f”,&k,&a);
32
关系运算与逻辑运算
关系运算符
< <= >
== !=
>=
关系运算优先级低于算术运算,第一排高于第二排
运算结果为:真 “1” 或 假 “0”
逻辑运算符
||
&&
!
优先级:! 高于算术运算,||低于&&低于算术运算
运算结果为:真 “1” 或 假 “0”
逻辑运算时,所有非零值均表示真
33
if 语句
if 语句的三种形式
if(表达式) 语句;
if(表达式) 语句1;
else
语句2;
if(表达式1)
语句1;
else if(表达式2) 语句2;
……
else if(表达式n) 语句n;
else 语句n+1;
表达式可以是任意表达式
(关系、逻辑、赋值、算术等)
语句可以是复合语句
else if 中间必须要留空格
if 语句可以嵌套
else 可以省略
34
switch 语句
switch 语句的三种形式
switch(表达式){
case 常量1: 语句1;
case 常量2: 语句2;
…
case 常量n: 语句n;
default : 语句n+1;
}
执行过程:先计算表达式的值,
并逐个与 case 后的常量相比较,
当其与某个常量相等时, 即执行
其后的语句,然后不再进行判断,
继续执行后面所有case后的语句。
如表达式的值与所有case 后的常
量表达式均不相同时,则执行
default 后的语句
表达式是对整数或字符作运算
语句可以是复合语句
switch 语句可以嵌套
通常每个语句后面要加 break
35
for 循环
for 语句
for(表达式1;表达式2;表达式3) 循环体
执行过程:
1) 先求解表达式1
2) 求解表达式2,若其值为真(非0),则执行循环体中的语句;
若其值为假(0),则结束循环
3) 计算解表达式3,并回到上面的第 2) 步
循环体可以是单个语句,也可以是复合语句
表达式1、2、3均可省略,但分号不能省
3个表达式都省略时,构成无限循环
for(循环变量赋初值;循环条件;循环变量增量) 循环体
36
while 循环
while 语句
while(表达式) 循环体
循环体可以是单个语句,也可以是复合语句
do-while 语句
do
循环体
while(表达式)
无论条件是否成立,至少执行一次循环
37
跳转语句
break 语句
跳出循环体
continue 语句
跳过本次循环中剩余的语句,强行执行下一次循环
return 语句
从函数返回
函数的返回值通过 return 返回
return 表达式
return(表达式)
38
主要内容
C语言概述
数据结构
程序设计流程(顺序、选择、循环)
函数
指针
文件
39
函数分类
函数分类
从函数定义的角度:库函数和用户定义函数
从有无返回值角度:有返回值函数和无返回值函数
从数据传送角度:无参函数和有参函数
库函数
数学函数、输入输出函数、字符串操作函数等
参见课程主页
40
数学函数
int
double
long
double
double
double
double
double
double
double
double
double
double
double
double
double
double
double
double
double
int
double
double
abs(int i)
fabs(double x)
labs(long n)
exp(double x)
log(double x)
log10(double x)
pow(double x,double y)
pow10(int p)
sqrt(double x)
头文件 “math.h”
acos(double x)
asin(double x)
编译时加 –lm 选项
atan(double x)
cos(double x)
更多函数参见课程主页
sin(double x)
tan(double x)
cosh(double x)
例:example_math.c
sinh(double x)
tanh(double x)
ceil(double x)
floor(double x)
rand()
modf(double x,double *iptr)
fmod(double x,double y)
41
函数的定义
函数的一般定义形式
函数头
类型标识符 函数名(形式参数列表)
{ 声明部分
语句
函数体
}
类型标识符指明了本函数的类型,即函数返回值的类型
形参要指定数据类型,有多个形参时,用逗号隔开
如果函数不带参数,则形式参数可以省略,但括号不能省
例: int max(int a, int b)
{
}
if (a>b) return a;
else return b;
42
函数调用
main()
{
int max(int a, int b);
int x,y,z;
printf("input two numbers:\n");
scanf("%d%d",&x,&y);
z=max(x,y);
printf("maxmum=%d",z);
}
int max(int a, int b)
{
if(a>b) return a;
else return b;
}
例:example_fun1.c
43
形参与实参
函数的形参和实参
形参只在被调用时才分配内存单元,调用结束即被释放
形参只有在函数内部有效
实参可以是常量、变量、表达式、函数等,但它们必须
具有确定的值,以便把这些值传送给形参
实参和形参在数量上,类型上,顺序上应严格一致
参数是变量时,传递的是数值,即数据传递是单向的
44
数组参数
数组作为函数参数
数组的元素作为实参
数组名作为参数
形参与实参的类型必须相同
函数调用时,传递的是数组的首地址(地址传递)
形参与实参的长度可以不一致
多维数组也可以作为函数的参数
在形参表中,可以不指定形参数组的长度,或用一个变量
来表示形参数组元素的个数
void nzp(int a[]);
void nzp(int a[], int n);
void nzp(int A[][5], int m);
45
函数返回值
函数的返回值(函数值)
返回值只能通过 return 语句返回主调函数
函数中允许有多个 return 语句,
但每次调用只能有一个return 语句被执行
返回值的类型和函数定义时的类型应一致,
否则以函数定义时的类型为准,自动进行类型转换
如果返回值为整型,在函数定义时可以省去类型说明
没有返回值的函数,可以定义为“空类型”(void)
46
函数调用
函数调用的一般形式
函数名(实参列表)
函数调用的方式
函数表达式
z=max(x,y);
函数语句
scanf("%d",&a);
函数实参
printf("%d",max(x,y));
47
函数调用
函数调用前需声明
类型标识符 被调函数名(类型 形参,类型 形参,… )
类型标识符 被调函数名(类型,类型,… )
如果被调函数是整型或字符型时,可以不作说明
当被调函数定义在主调函数之前时,可以不作说明
库函数不需作说明,但必须包含该函数所在的头文件
可以在所有函数定义之前,在函数外预先说明各个函数
的类型,则在以后的调用中,不需要作说明
48
函数调用
函数可以嵌套调用
函数可以递归调用
49
局部变量
根据作用域的不同,变量可分为全局变量与局部变量
局部变量(内部变量)
在函数内作定义说明,作用域仅限于该函数内
主函数中定义的变量也只能在主函数中使用
主函数中也不能使用其它函数中定义的变量
形参是被调函数的局部变量,实参是主调函数的局部变量
在不同函数中可定义相同的变量名,
但它们代表的是不同的对象,分配不同的内存单元
复合语句中也可定义变量,其作用域只在复合语句范围内
50
全局变量
全局变量(外部变量)
在函数外部定义的变量,其作用域是整个源程序
在函数中使用全局变量,一般应作全局变量说明 (extern)
在函数之前定义的全局变量,可不作说明
如果外部变量与局部变量同名,则使用局部变量
51
变量存储类别
变量的存储方式有:动态存储与静态存储
自动变量(auto)
动态存储,函数调用结束后即被释放
所有的局部变量缺省为自动变量
静态局部变量(关键字:static)
静态存储,函数调用后不释放,保留原来的值
静态局部变量在编译时赋初值,即只赋初值一次
若没有赋初值,静态局部变量的值为0或空字符;
而自动变量的值不定
寄存器变量(关键字:register)
将局部变量存放在CPU的寄存器中,提供执行效率
只有自动变量可作为寄存器变量,存放个数有限
52
主要内容
C语言概述
数据结构
程序设计流程(顺序、选择、循环)
函数
指针
文件
53
指针
指针极大地丰富了C语言的功能
运用指针编程是C语言最主要的风格之一
运用指针可以很方便地使用数组和字符串
指针能使C语言象汇编语言一样处理内存地址,提高效率
指针是C语言中最重要的一环,也是最为困难的一部分
能否正确理解和使用指针是掌握C语言的一个标志
54
指针定义与引用
指针变量(指针)
用来存放某个变量的地址的变量称为指针变量,
通常称为指向该变量的指针
指针变量的定义
类型标识符 *指针变量名;
指针的类型是该指针所指向的变量的数据类型
指针变量的引用
& :取地址运算符
* :指针运算符(或称“间接访问” 运算符)
a=3; p=&a; *p=5;
55
指针运算
指针运算
赋值运算
int a,*pa,b[5],*pb;
pa=&a; pb=b;
例:example_pointer1.c
int a,*pa=&a,b[5],*pb=b;
加减运算:主要针对指向数组的指针变量
int b[5],*pb0=b,*pb3;
例:example_pointer2.c
pb3=pb0+3;
指针作为函数参数:传递变量的地址
空指针:值为 0 的指针变量
56
数组指针
指向一维数组的指针变量
C语言中,数组名代表该数组所在的内存单元的首地址
数组元素的引用方式:下标法、指针法
*pb=b; b[3]; *(pb+3); *(b+3);
数组名作为函数参数:传递数组的首地址
指向二维数组的指针变量
二维数组的数组名代表整个数组的首地址
二维数组可分解为多个一维数组来处理
int a[3][4];
a; a[0]; *a; &a[0][0];
a+1; a[1]; &a[1];
例:example_pointer3.c
57
数组指针
指向二维数组的指针变量
数组的列数
类型标识符 (*指针变量名)[长度];
int (*p)[4], A[3][4];
p=a;
(*p+i)+j
&A[i][j]
例:example_pointer4.c
58
字符串指针
指向字符串的指针变量
访问字符串的两种方式:字符数组、字符串指针
字符串指针的定义与普通变量指针定义一样,但赋值不一样
char c,*p=&c;
char *s="Hello world";
char *ps;
ps="Hello world";
字符串指针变量与字符数组的区别
赋值不一样
char *ps, str[20];
ps="Hello world";
str="Hello world";
59
函数指针等
指向函数入口的指针变量
类型标识符 (*指针变量名)();
略(详见讲义或参考书籍)
指针型函数(略)
指针数组和指向指针的指针(略)
main函数的参数
main (int argc, char *argv[]);
60
主要内容
C语言概述
数据结构
程序设计流程(顺序、选择、循环)
函数
指针
文件
61
文件操作
文件指针
FILE *指针变量名;
文件打开
文件指针名=fopen(文件名, 使用文件方式);
文件名为字符串
打开方式有:(r为读,w为写,+为读写,t为文本,b为二进制
rt、wt、at、rb、wb、ab、rt+、wt+、at+、rb+、wb+、ab+
文件的关闭
fclose(文件指针名)
62
文件读写
文件的读写(stdio.h)
字符读写函数:fgetc 和 fputc
字符串读写函数:fgets 和 fputs
数据块读写函数:freed 和 fwrite
格式化读写函数:fscanf 和 fprinf
略(详见讲义或参考书籍)
63
其它
预处理命令
包含头文件:#include
#include <stdio.h>
#include “math.h”
宏定义:#define
#define N 100
#define f(x) (x*x+3*x)
条件编译指令:
#if、#else、#endif
#ifdef、#ifndef
64
计时函数
clock(time.h)
double x;
x = clock()/CLOCKS_PER_SEC;
/* 需要计时的程序片段 */
x = clock()/CLOCKS_PER_SEC - x;
time(time.h)
time_t t0, t1;
double t;
t0=time(NULL);
/* 需要计时的程序片段 */
t1=time(NULL);
t=difftime(t1,t0);
例:example_time.c
65
上机作业
(1) 比较三种矩阵相乘的计算效率
C=A m×n Bn×L
算法一:
i-j-k 循环
for(i=0; i<m; i++) {
for(j=0; j<n; j++) {
for(k=0; k<L; k++)
C[i][j]=C[i][j]+A[i][k]*B[k][j];
}
}
算法二:
j-k-i 循环
for(j=0; j<n; j++) {
for(k=0; k<L; k++) {
for(i=0; i<m; i++)
C[i][j]=C[i][j]+A[i][k]*B[k][j];
}
}
算法三:
i-k-j 循环
for(i=0; i<m; i++) {
for(k=0; k<L; k++) {
for(j=0; j<n; j++)
C[i][j]=C[i][j]+A[i][k]*B[k][j];
}
}
取 m=n=L=1024, 每种算法计算20次,统计总的运算时间
66
上机作业
(2) 用 Jacobi 和 GS 迭代算法解线性方程组 Ax = b, 其中
T
I
A
I
I
1
4 1
1
R10241024 , T
R 3232 , b
I
1
1
T
1 4
迭代初始向量取零向量
最大迭代步数设为 1000 次
相对残量误差精度设为 10-6
67