Download presentation
Presentation is loading. Please wait.
1
C语言程序设计 北京工业大学计算机学院 软件学科部 宋凯 学时40:课堂24+上机16
软件学科部 宋凯 学时40:课堂24+上机16 教案下载:
2
教学安排 总学时: 40 上课:24学时 星期二 7-8节(1,10周) 1-316 星期四 5-6节(1-10周) 1-316
星期二 7-8节(1,10周) 星期四 5-6节(1-10周) 上机:16学时 星期二 7-8节(2-9周) 信南206 (微机1室)
3
计算机与算法初步总结 教学效果 存在问题 初步的程序设计基础知识 初步的程序编程和调试能力 工程能力:设计规范、算法描述等 分析问题能力
动手能力 考试要求低,不反映实际水平
4
本学期的教学内容调整 教学内容 作业要求 增加软件工程初步、程序文档编制 编程体要求提供数据说明和程序流程图 上机题
预先完成算法设计(数据说明和程序流程图) 上交实验报告(功能简介、数据说明和流程图)
5
一、什么是地址? 计算机内存是以字节为单位的一片连续的存储空间,每个字节都有一个编号,这个编号就称为内存地址。 内存地址的特点:
地址是连续的; 通常用二进制表示。为直观起见,在高级语言中,可以用十进制表示。
6
二、变量与地址的关系 每个变量在内存中都占有一定字节数的存储单元。 int a, b; float x;
... a b x 变量的地址是指该变量所占存储单元的首字节地址。 可以使用运算符&获取变量的地址: &a、 &b、&x。 变量的内容是指在内存的存储单元中存放的数据。
7
第六章 指针 6.1 指针的概念 什么是指针 指针变量的声明 保存内存单元地址的变量 为内存中的数据提供了直接访问手段
第六章 指针 指针的概念 什么是指针 保存内存单元地址的变量 为内存中的数据提供了直接访问手段 指针变量的声明 int *p; 声明p 用于保存整型数据的地址 double *q; 声明q 用于保存双精度数据的地址 变量p, q本身各占用4个字节(地址需用的空间) 指针变量本身的数据长度是由系统决定的。
8
指针变量示意图 1012 a ptr 2002 a ptr 2002 a ptr
9
操作方式 a a=123 123 直接操作 ptr *ptr=123 间接操作 ptr = &a 间接访问提供了另一种访问内存数据的手段
10
指针变量的特性有2点: (1)变量的值为地址值; (2)变量的类型为该指针所指向的实体的类型。 指针的名字与一般变量名相同。
11
通过指针的引用与赋值 注意: 定义后的指针在没有赋初值或赋值之前是不能使用的. x 3.02 double x, y; double *q;
q = &x; /* 取 x 的地址 */ *q = /* 等效于 x = 3.02 */ y = *q; /* 等效于 y = x */ 变量的地址 = 所占用单元的首地址 3.02 注意: 定义后的指针在没有赋初值或赋值之前是不能使用的.
12
&和*是 指针操作中两个特殊的单目运算符。
指针相关运算符 &和*是 指针操作中两个特殊的单目运算符。 取地址运算符: & 指针运算符(间接访问运算符): * (简称:取内容) 它们优先级与结合方式同其他单目运算符。
13
不同指针类型操作示意图: int data1 = 12 , *ptr1; float data2 = 56.8, *ptr2; data1
1000 1002 2001 2003 1000 • 1002 • ptr1 ptr2 ptr1=&data1 ptr2=&data2 data1 data2
14
数组与指针运算 数组名 = 数组占用的内存单元的首地址 int arr[ 32 ], x;
int *p = arr; /* 将数组首地址保存在 p */ x = *(p + 2); /* 取数组中的第 3 个元素 */ p++; /* 使指针指向第 2 个元素 */ p = p + 5; /* 使指针指向第 7 个元素 */ *p = 308; /* 改变第 7 个元素 */ x = p – arr; /* 得到 6 */ /* 计算两个地址之间有几个整数的空间 */
15
注意: 在指针加、减运算中,数字“1”不是指一个字节,而是以一个数据(基类型)为基本单位。 地址计算与地址中存放的数据长度有关。
16
对于不同的数据类型的指针p,p n表示的实际位置的地址是: (p) n * 数据长度(含几个字节)
计算后的指针地址值的变化取决于它指向的数据类型。 见例pointsz2.c pointsz3.c
17
例6-1: 设计一个函数,用于交换两个变量中的整数
#include <stdio.h> void swap(int *p, int *q) { int t; t = *p; *p = *q; *q = t; } main( ) { int x, y; scanf( “%d%d”, &x, &y ); swap( &x, &y ); printf( “%d %d\n”, x, y );
18
下面的函数能否实现两个变量中的整数交换吗?
#include <stdio.h> void swap(int x, int y) { int t; t = x; y = x; y = x; } main( ) { int x, y; scanf( “%d%d”, &x, &y ); swap( x, y ); printf( “%d %d\n”, x, y );
19
程序分析 作为参数的是指针类型变量 参数传递(按单向值调用) 函数体内 分析方法的特点 参数变量有自己的内存单元(存地址)
将变量 x 的地址值传到参数 p 中 用 *p 访问 x 函数体内 以间接访问方式操作 分析方法的特点 利用内存单元的图示 表示所有变量的变化(包括指针)
20
例6-2:设计一个函数,用于将字符串中的小写字母改为大写字母
#include <stdio.h> void toUpper( char p[ ] ) { for( ; *p != ‘\0’; p++ ) if( ‘a’ <= *p && *p <= ‘z’ ) *p = *p – ‘a’ + ‘A’; } main( ) { char *buf = “characters.”; toUpper( buf ); printf( “%s\n”, buf );
21
程序分析 数组与指针 空间分配 字符串 char p[ ] 等价于 char *p p[ i ] 等价于 *( p + i )
指针代替了下标变量 空间分配 数组:数组大小 * 元素占用空间 数组名 = 首元素地址 (本身无空间) 指针:4个字节保存地址 字符串 以‘\0’结束的字符数组 用首字符地址参加运算
22
例 6.3 求两个向量之和的函数 double *addVector( double a[ ], double b[ ], int n )
例 6.3 求两个向量之和的函数 double *addVector( double a[ ], double b[ ], int n ) { int i; double *p; p = (double *)malloc( n * sizeof(double) ); for( i=0; p != NULL && i<n; i++ ) p[ i ] = *a++ + *b++; return p; } /* 用指针代替下标变量 */
23
主函数 free(p); main() { double x[3], y[3]; double *p;
x[0] = x[1] = x[2] = 4; y[0] = y[1] = y[2] = 5; p = addVector( x, y, 3 ); printf(“%lf”, *p); printf(“%lf”, *(p+1); printf(“%lf”, p[2]); } free(p);
24
动态空间分配 申请存储单元的函数 释放该空间 存储单元大小的计算 void *malloc( long size );
给定所需字节数,取得系统分配的存储单元,返回首地址 如果分配失败,返回空指针 NULL 释放该空间 void free( void *p ); 给定malloc获得的存储单元首地址 存储单元大小的计算 sizeof( 类型 ) sizeof( 变量 )
25
6.2 字符串处理 常用字符串处理函数 int strlen( char *s ); 求字符串中字符个数
6.2 字符串处理 常用字符串处理函数 int strlen( char *s ); 求字符串中字符个数 int strcmp( char *s1, char *s2 ); 按照字母顺序比较, 0 表示相等 char *strcpy( char *dec, char *src ) 字符串从 src 复制到 dec,返回 dec char *strcat( char *dec, char *src ) 将字符串 src 连接到 dec 后面,返回 dec
26
例6-4:字符串连接 使用前提:dec字符数组中必须有足够的空间
char *strcat( char *dec, char *src ) { char *p ; p = dec; while( *p != ‘\0’ ) p++; while( *src != ‘\0’ ) *p++ = *src++; *p = ‘\0’; return dec; } 使用前提:dec字符数组中必须有足够的空间
27
使用合法的内存单元 main( ) { int n; char *p = “abc”, *q = “xyz”, *s;
strcat( p, q ); /* 非法 */ n = strlen(p) + strlen(q); s = (char *)malloc( n+1 ); /* 分配空间 */ strcpy( s, p ); strcat( s, q ); printf( “%s\n”, s ); free( s ); /* 释放空间 */ }
28
例6-5:子字符串的查找 要求:实现函数 char *strstr( char *s1, char *s2 )
用于在 s1 中找出子串 s2,返回首元素地址;若 s2 不存在,则返回 NULL s1比s2短 返回NULL s2是s1的 前缀 返回 s1 在s1首字符之 后的字符串中 查找,返回 结果 Y N
29
函数实现(递归法) char *strstr( char *s1, char *s2 ) {
if( strlen(s1) < strlen(s2 ) return NULL; if( 0 == strncmp( s1, s2, strlen(s2) ) ) return s1; /* 前n个字符相等 */ return strstr( s1+1, s2 ); /* 递归查找 */ }
30
strncmp 的设计 int strncmp( char *s1, char *s2, int n ) 比较2个字符串,返回字符的差
返回 0 n=0? 首字符相等? 首字符=\0? 返回首字 符之差 比较其余字符 返回比较结果 Y N strncmp 的设计 int strncmp( char *s1, char *s2, int n ) 比较2个字符串,返回字符的差 非结构化 控制流图
31
strncpy 的实现(递归法) int strncmp( char *s1, char *s2, int n ) {
if( 0 == n ) return 0; /* 前 n 个相同 */ if( *s1 != *s2 ) return *s1 - *s2; /* 首元素之差 */ if( *s1 == ‘\0’ ) /* 2个字符串都结束 */ return 0; return strncmp( s1+1, s2+1, n-1 ); } /* 比较其余字符 */
32
6.3 指针应用 例6-6:排序函数(按照字符顺序) 要求:对n个字符串进行排序 基本算法:(冒泡排序)
6.3 指针应用 例6-6:排序函数(按照字符顺序) 要求:对n个字符串进行排序 基本算法:(冒泡排序) 逐个比较相邻元素,交换前大后小的元素 重复以上处理(n-1次) void bSort( int n, char *a[ ] ); 采用指针数组指示 n 个字符串
33
程序实现 void bSort( int n, char *a[ ] ) { char **p, *tmp; int i;
for( i=0; i<n-1; i++ ) /* 重复n-1次 */ for( p=a; p<a+n-1; p++ ) /* 逐个检查 */ if( strcmp(*p, *(p+1)) > 0 ) { /* 比较相邻元素 */ tmp = *p; /* 交换 */ *p = *(p+1); *( p+1 ) = tmp; }
34
程序分析 char **p = a; a 是数组名(首元素地址),数组元素是字符指针 因此 a 是保存字符地址的内存单元的地址,
tmp = *p; 将变量 p 中保存的字符指针地址赋给 tmp
35
例6-7:将命令行参数排序后输出 程序处理过程的回顾 myProc.c myProc.obj myProc.exe 编译 连接
编译 连接 myProc.exe 可执行文件 鼠标双击 (如:TC.exe) 在DOS环境:myProc 回车 命令行(命令 + 若干个参数) C:\ myProc arg1 arg2 程序启动时指定;而不是运行中输入 也可以在资源管理器中文件属性中指定 或 TC 菜单Option/arguments中指定(调试中用)
36
命令行参数的组织 例:myProc arg1 arg2 空间已分配 m y P r o c ‘\0’ a r g 1 ‘\0’
void main( int ac, char *av[ ] ) m y P r o c ‘\0’ a r g ‘\0’ a r g ‘\0’ 3
37
程序实现 void main(int ac, char *av[ ]) { /* 给定命令行参数个数 ac */
bSort( ac-1, av+1 ); while( --ac ) /* 循环ac-1次 */ printf( “%s\n”, *(++av) ); } /* 指针位移后,取元素 */ av 是变量,保存字符指针的指针 系统负责统计命令行参数个数传递给 ac 同时构造命令行参数的指针数组,传递给 av
38
例6-8:单词统计 要求 数据对象 从键盘输入一篇英文文章,统计各单词的出现次数,以<单词:出现次数>的格式列表输出
单词表:字符串数组 wdTab 出现次数表:整数表 nmTab 用下标联系单词及其出现次数
39
读入一单词 读成功? 在wdTab中 查找该单词 找到? 添加到 wdTab 出现次数 加一 输出wdTab 和nmTab N Y 算法描述
40
模块设计(函数抽象) int wdRead( char buf[ ] ) 从键盘读入一单词(字母和数字组成),
读入失败时,返回0。 int wdLook( char buf[ ], char *tab[ ], int n ) 在单词表 tab 中查找单词 buf, (n 是 tab 中的单词个数) 如果找到,返回下标;未找到返回 –1。 void wdPrint( int n, char *wtab[ ], int ntab[ ] ) 输出单词表 wtab 和出现次数表 ntab 中的 n 个单词及其出现次数
41
主程序的实现 #include <stdio.h> #include <string.h> main( ) {
char *wdTab[ 1024 ], p; /* 最多1024个 */ int nmTab[ 1024 ], num = 0, idx, n; char buf[ 256 ]; /* 当前单词 */ while( 0 != (n = wdRead( buf )) ) { idx = wdLook( buf, wdTab, num ); if( idx >= 0 ) nmTab[ idx ] += 1; /* 出现次数加一 */ else { p = (char *)malloc( n+1 ); /* 分配空间 */ wdTab[ num ] = strcpy( p, buf ); nmTab[ num++ ] = 1; } } /* 保存复制后的单词 */ wdPrint( num, wdTab, nmTab ); } 主程序的实现
42
单词的读入 int wdRead( char buf[ ] ) { int ch, i=0;
do ch = getchar( ); /* 跳过其他字符 */ while( !isalnum(ch) && ch != EOF ); while( ch != EOF && isalnum(ch) ) { buf[ i++ ] = ch; /* 保存字符 */ ch = getchar( ); } buf[ i ] = ‘\0’; /* 字符串结尾 */ return i;
43
单词查找和输出 int wdLook( char buf[ ], char *tab[ ], int n ) { char **p;
for( p=tab; p<tab+n; p++ ) if( strcmp( *p, buf ) == 0 ) return p-tab; return -1; } void wdPrint( int n, char *ws[ ], int ns[ ] ) { int i; for( i=0; i<n; i++ ) printf( “<%s:%d>\n”, ws[ i ], ns[ i ] );
44
程序分析 结构化设计 实现技术 存在问题 功能分解、逐步求精 模块化函数抽象 数据处理局部化 指针数组、字符输入输出、指针索引
数组大小(1024)的限制 数组下标的关联作用(有限)
45
指针小结 指针: 指针运算: 用途: 安全性 保存地址,提供高效访问手段 以基类型数据单元为单位,不等于地址运算
函数参数、数组元素遍历、字符串处理 指针数组(含字符串数组)、命令行 安全性 可访问外部函数申请的内存单元 始终指向申请到的内存单元
46
本章作业 阅读教科书第七章和第八章 程序设计练习 上机题 完成自我测验练习 7.3 7.4 7.6 完成自我测验练习 8.3 8.4
完成自我测验练习 完成自我测验练习 程序设计练习 上机题
Similar presentations