第8章 善于利用指针 8.1 指针是什么 8.2 指针变量 8.3 通过指针引用数组 8.4 通过指针引用字符串 8.5 指向函数的指针

Slides:



Advertisements
Similar presentations
第九章 指针 西安工程大学.
Advertisements

第七章 指针 计算机公共教学部.
第六章 指针 指针的概念 指针变量 指针与数组 指针与函数 返回指针值的函数.
第十章 指针 分析C程序的变量所存放的数据:  数值型数据:整数、实数  字符型数据:字符、字符串 这些变量具有以下性质:
第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错
第七章 指针 教 材: C程序设计导论 主 讲: 谭 成 予 武汉大学计算机学院.
二级指针与二维数组.
C语言程序设计基础 第10章 指针进阶 刘新国.
10.1 二级指针 10.2 指针与二维数组 10.3 指针的动态存储分配 10.4 函数指针 10.5 main函数的参数
C语言程序设计教程 单位:德州学院计算机系.
第 6 章 第 6 章 指 针 指 针 1.
C语言程序设计.
C语言基础——指针的高级应用 Week 05.
第6章 指针 6.1 指针的概念 6.2 变量与指针 6.3 数组与指针 6.4 字符串与指针 6.5 函数与指针 6.6 返回指针值的函数
6.4 字符串与指针 1. 用字符数组存放一个字符串.
第六节 二维数组和指针 二维数组的地址 对于一维数组: (1)数组名array表示数组的首地址, 即array[0]的地址;
C++中的声音处理 在传统Turbo C环境中,如果想用C语言控制电脑发声,可以用Sound函数。在VC6.6环境中如果想控制电脑发声则采用Beep函数。原型为: Beep(频率,持续时间) , 单位毫秒 暂停程序执行使用Sleep函数 Sleep(持续时间), 单位毫秒 引用这两个函数时,必须包含头文件
第九章 指针 目录 指针与指针变量的概念 变量的指针和指向变量的指针变量 数组的指针和指向数组的指针变量
第5章 函数与模块化设计 学习目的与要求: 掌握函数的定义及调用方法 理解并掌握参数的传递方法 理解函数的嵌套与递归调用
程序设计基础.
第6章 指针 学习目的与要求: 了解指针的概念和相关术语 熟练掌握指向变量、数组和字符串的指针变量的使用方法 了解指向函数的指针变量
指 针 为什么要使用指针 指针变量 指针与数组 返回指针值的函数 动态内存分配 通过指针引用字符串 指向函数的指针 小 结 习 题.
目录 10.1 指针的基本概念 10.2 指向变量的指针变量 10.3 指向数组的指针变量 10.4 指向函数的指针变量和指针型函数
第八章 指 针 8.1 指针的概念与定义 8.2 指针作函数参数 8.3 指针与数组 8.4 指针与函数 8.5 复杂指针.
第 十 章 指 针.
项目六 用指针优化学生成绩排名 项目要求 项目分析
第七章 函数 目录 有参的加法函数的开发 函数定义的一般形式 函数参数和函数的值 函数的调用
Chap 8 指针 8.1 寻找保险箱密码 8.2 角色互换 8.3 冒泡排序 8.4 电码加密 8.5 任意个整数求和*
C语言程序设计 李祥.
第8章 善于利用指针 8.1 指针是什么 8.2 指针变量 8.3 通过指针引用数组 8.4 通过指针引用字符串 8.5 指向函数的指针
第8章 善于利用指针 8.1 指针是什么 8.2 指针变量 8.3 通过指针引用数组 8.4 通过指针引用字符串 8.5 指向函数的指针
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
二维数组的指针表示 与复杂的指针例子 专题研讨课之三.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
C语言程序设计基础 第8章 指针 刘新国.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第8章 指针.
第八章 使用指针.
第十章 指针.
欲穷千里,更上层楼 第十章 指 针 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; 能很方便地使用数组和字符串; 并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C语言的功能。 学习指针是学习C语言中最重要的一环,
第五章 习题课 电子信息与计算机科学系 曾庆尚.
9.1 地址、指针和变量 9.2 指针运算 9.3 指针与数组 9.4 函数与指针 9.5 程序综合举例 9.6 上机实训.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C语言复习3----指针.
第一章 程序设计和C语言 主讲人:高晓娟 计算机学院.
C语言大学实用教程 第6章 数组 西南财经大学经济信息工程学院 刘家芬
C语言大学实用教程 第7章 指针 西南财经大学经济信息工程学院 刘家芬
第二章 Java基本语法 讲师:复凡.
函数 概述 模块化程序设计 基本思想:将一个大的程序按功能分割成一些小模块, 特点: 开发方法: 自上向下,逐步分解,分而治之
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第6讲 指针与引用 6.1 指针 6.2 引用.
<编程达人入门课程> 本节内容 内存的使用 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群: ,
C语言程序设计 第一章 数据类型, 运算符与表达式 第二章 顺序程序设计 第三章 选择结构程序设计 第四章 循环控制 第五章 数组.
第六章 指针 C++程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值.
第6章 指针 6.1 指针的概念 6.2 变量与指针 6.3 数组与指针 6.4 字符串与指针 6.5 函数与指针 6.6 返回指针值的函数
第十章 指针 指针是C语言的重要概念,是C语言的特色,是C语言的精华。 10.1 地址和指针的概念 内存中的每一个字节都有一个地址。
第九章 指针.
第九节 赋值运算符和赋值表达式.
3.16 枚举算法及其程序实现 ——数组的作用.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C程序设计.
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C语言程序设计 第8章 指针.
第九章 指针 C程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值.
第八章 指 针 北京邮电大学出版社.
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
第7章 地址和指针 7.1 地址和指针的概念 7.2 指针变量的定义和指针变量的基类型 7.3 给指针变量赋值 7.4 对指针变量的操作
Presentation transcript:

第8章 善于利用指针 8.1 指针是什么 8.2 指针变量 8.3 通过指针引用数组 8.4 通过指针引用字符串 8.5 指向函数的指针 8.6 返回指针值的函数 8.7 指针数组和多重指针 8.8 动态内存分配与指向它的指针变量

8.1 指針是什么 如果在程序中定义了一个变量,在对程序进行编译时,系统就会给该变量分配内存单元 编译系统根据程序中定义的变量类型,分配一定长度的空间 例如,VC++为整型变量分配4个字节,对单精度浮点型变量分配4个字节,对字符型变量分配1个字节

8.1 指针是什么 内存区的每一个字节有一个编号,这就是“地址”,它相当于旅馆中的房间号。 在地址所标识的内存单元中存放数据,这相当于旅馆房间中居住的旅客一样。 由于通过地址能找到所需的变量单元,我们可以说,地址指向该变量单元。 将地址形象化地称为“指针”

务必弄清楚存储单元的地址和存储单元的内容这两个概念的区别 例如:

int i=3,j=6,k; printf(“%d”,i); 通过变量名i 找到i的地址2000,从而从存储单元读取3

int i=3,j=6,k; k=i+j; 从这里取3 从这里取6 将9送到这里 直接存取

间接存取 int i=3,j=6,k; 定义特殊变量i_pointer i_pointer=&i; 50 *i_pointer=50;

i 直接存取 3 2000 i_pointer *i_pointer 间接存取 2000 3 2000

为了表示将数值3送到变量中,可以有两种表达方法: 将3直接送到变量i所标识的单元中,例如: i=3; 将3送到变量i_pointer所指向的单元(即变量i的存储单元),例如: *i_pointer=3; 其中*i_pointer表示i_pointer指向的对象

指向就是通过地址来体现的 假设i_pointer中的值是变量i的地址(2000),这样就在i_pointer和变量i之间建立起一种联系,即通过i_pointer能知道i的地址,从而找到变量i的内存单元

由于通过地址能找到所需的变量单元,因此说,地址指向该变量单元 将地址形象化地称为“指针”。意思是通过它能找到以它为地址的内存单元

一个变量的地址称为该变量的“指针” 例如,地址2000是变量i的指针 如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量” i_pointer就是一个指针变量。指针变量就是地址变量,用来存放地址的变量,指針变量的值是地址(即指针)

“指针”和“指针变量”是不同的概念 可以说变量i的指针是2000,而不能说i的指针变量是2000 指针是一个地址,而指针变量是存放地址的变量

8.2 指针变量 8.2.1 使用指针变量的例子 8.2.2 怎样定义指针变量 8.2.3 怎样引用指针变量 8.2.4 指针变量作为函数参数

8.2.1使用指针变量的例子 例8.1 通过指针变量访问整型变量。 解题思路:先定义2个整型变量,再定义2个指针变量,分别指向这两个整型变量,通过访问指针变量,可以找到它们所指向的变量,从而得到这些变量的值。

#include <stdio. h> int main() { int a=100,b=10; int. pointer_1, #include <stdio.h> int main() { int a=100,b=10; int *pointer_1, *pointer_2; pointer_1=&a; pointer_2=&b; printf(“a=%d,b=%d\n”,a,b); printf(“*pointer_1=%d,*pointer_2= %d\n”,*pointer_1,*pointer_2); return 0; } 定义两个指针变量 直接输出变量a和b的值 使pointer_1指向a 使pointer_2指向b 间接输出变量a和b的值

此处*与类型名在一起。此时共同定义指针变量 #include <stdio.h> int main() { int a=100,b=10; int *pointer_1, *pointer_2; pointer_1=&a; pointer_2=&b; printf(“a=%d,b=%d\n”,a,b); printf(“*pointer_1=%d,*pointer_2= %d\n”,*pointer_1,*pointer_2); return 0; } 此处*与指针变量一起使用。此时代表指针变量所指向的变量

8.2.2 怎样定义指针变量 定义指针变量的一般形式为: 类型 * 指针变量名; 如:int *pointer_1, *pointer_2; 类型 * 指针变量名; 如:int *pointer_1, *pointer_2; int是为指针变量指定的“基类型” 基类型指定指针变量可指向的变量类型 如pointer_1可以指向整型变量,但不能指向浮点型变量

8.2.2 怎样定义指针变量 下面都是合法的定义和初始化: pointer_3=2000; 错误 pointer_1=&a; 正确 float *pointer_3; char *pointer_4; int a,b; int *pointer_1=&a,*pointer_2=&b; pointer_3=2000; 错误 pointer_1=&a; 正确 *pointer_1=&a; 错误 pointer_3=&a; 错误

8.2.3 怎样引用指针变量 在引用指针变量时,可能有三种情况: 使p指向a 给指针变量赋值。如:p=&a; 引用指针变量指向的变量。如有 则执行printf(“%d”,*p); 将输出1 引用指针变量的值。如:printf(“%o”,p); *p相当于a 以八进制输出a的地址

8.2.3 怎样引用指针变量 要熟练掌握两个有关的运算符: &a是变量a的地址 (1) & 取地址运算符。 (1) & 取地址运算符。 &a是变量a的地址 (2) * 指针运算符(“间接访问”运算符) 如果: p指向变量a,则*p就代表a。 k=*p; (把a的值赋给k) *p=1; (把1赋给a)

例8.2 输入a和b两个整数,按先大后小的顺序输出a和b。 解题思路:用指针方法来处理这个问题。不交换整型变量的值,而是交换两个指针变量的值。

p1 a p &a 5 #include <stdio.h> int main() { int *p1,*p2,*p,a,b; printf(“integer numbers:"); scanf(“%d,%d”,&a,&b); p1=&a; p2=&b; if(a<b) { p=p1; p1=p2; p2=p; } printf(“a=%d,b=%d\n”,a,b); printf(“%d,%d\n”,*p1,*p2); return 0; } &b 9 p2 b 成立

p1 a p &a &b 5 #include <stdio.h> int main() { int *p1,*p2,*p,a,b; printf(“integer numbers:"); scanf(“%d,%d”,&a,&b); p1=&a; p2=&b; if(a<b) { p=p1; p1=p2; p2=p; } printf(“a=%d,b=%d\n”,a,b); printf(“%d,%d\n”,*p1,*p2); return 0; } &b &a 9 p2 b

p1 a p &a &b 5 #include <stdio.h> int main() { int *p1,*p2,*p,a,b; printf(“integer numbers:"); scanf(“%d,%d”,&a,&b); p1=&a; p2=&b; if(a<b) { p=p1; p1=p2; p2=p; } printf(“a=%d,b=%d\n”,a,b); printf(“%d,%d\n”,*p1,*p2); return 0; } &b &a 9 p2 b

p1 a p &a &b 5 #include <stdio.h> int main() { int *p1,*p2,*p,a,b; printf(“integer numbers:"); scanf(“%d,%d”,&a,&b); p1=&a; p2=&b; if(a<b) { p=p1; p1=p2; p2=p; } printf(“a=%d,b=%d\n”,a,b); printf(“%d,%d\n”,*p1,*p2); return 0; } &b &a 9 p2 b 可否改为p1=&b; p2=&a;?

注意: a和b的值并未交换,它们仍保持原值 但p1和p2的值改变了。p1的值原为&a,后来变成&b,p2原值为&b,后来变成&a 这样在输出*p1和*p2时,实际上是输出变量b和a的值,所以先输出9,然后输出5

8.2.4 指针变量作为函数参数 例8.3 题目要求同例8.2,即对输入的两个整数按大小顺序输出。现用函数处理,而且用指针类型的数据作函数参数。 解题思路:定义一个函数swap,将指向两个整型变量的指针变量作为实参传递给swap函数的形参指针变量,在函数中通过指针实现交换两个变量的值。

#include <stdio. h> int main() {void swap(int. p1,int #include <stdio.h> int main() {void swap(int *p1,int *p2); int a,b; int*pointer_1,*pointer_2; printf("please enter a and b:"); scanf(“%d,%d”,&a,&b); pointer_1=&a; pointer_2=&b; if (a<b) swap(pointer_1,pointer_2); printf(“max=%d,min=%d\n”,a,b); return 0; } b pointer_1 a pointer_2 &a 5 &b 9

void swap(int *p1,int *p2) { int temp; temp=*p1; *p1=*p2; *p2=temp; } b pointer_1 a pointer_2 &a 9 5 &b 5 9 p1 &a p2 &b

void swap(int *p1,int *p2) { int temp; temp=*p1; *p1=*p2; *p2=temp; } 错!!! 无确定的指向

错!!! 无法交换a,b #include <stdio.h> int main() {…… if (a<b) swap(a,b); printf(“max=%d,min=%d\n”,a,b); return 0; } void swap(int x,int y) { int temp; temp=x; x=y; y=temp; a b 5 9 9 5 5 9 x y

例8.4 对输入的两个整数按大小顺序输出。 解题思路:尝试调用swap函数来实现题目要求。在函数中改变形参(指针变量)的值,希望能由此改变实参(指针变量)的值

#include <stdio. h> int main() {void swap(int. p1,int #include <stdio.h> int main() {void swap(int *p1,int *p2); int a,b; int*pointer_1,*pointer_2; scanf("%d,%d",&a,&b); pointer_1=&a; pointer_2=&b; if (a<b) swap(pointer_1,pointer_2); printf("max=%d,min=%d\n",a,b); return 0; } void swap(int *p1,int *p2) { int *p; p=p1; p1=p2; p2=p; } 错!!! 只交换形参指向

例8.5 输入3个整数a,b,c,要求按由大到小的顺序将它们输出。用函数实现。 解题思路:采用例8.3的方法在函数中改变这3个变量的值。用swap函数交换两个变量的值,用exchange函数改变这3个变量的值。

#include <stdio. h> int main() { void exchange(int. q1, int #include <stdio.h> int main() { void exchange(int *q1, int *q2, int *q3); int a,b,c,*p1,*p2,*p3; scanf("%d,%d,%d",&a,&b,&c); p1=&a;p2=&b;p3=&c; exchange(p1,p2,p3); printf(“%d,%d,%d\n",a,b,c); return 0; } 调用结束后不会改变指针的指向

void exchange(int. q1, int. q2, int. q3) { void swap(int. pt1, int void exchange(int *q1, int *q2, int *q3) { void swap(int *pt1, int *pt2); if(*q1<*q2) swap(q1,q2); if(*q1<*q3) swap(q1,q3); if(*q2<*q3) swap(q2,q3); } void swap(int *pt1, int *pt2) { int temp; temp=*pt1; *pt1=*pt2; *pt2=temp; 交换指针指向的变量值

8.3 通过指针引用数组 8.3.1 数组元素的指针 8.3.2 在引用数组元素时指针的运算 8.3.3 通过指针引用数组元素 8.3.4 用数组名作函数参数 8.3.5 通过指针引用多维数组

8.3.1 数组元素的指针 一个变量有地址,一个数组包含若干元素,每个数组元素都有相应的地址 8.3.1 数组元素的指针 一个变量有地址,一个数组包含若干元素,每个数组元素都有相应的地址 指针变量可以指向数组元素(把某一元素的地址放到一个指针变量中) 所谓数组元素的指针就是数组元素的地址

可以用一个指针变量指向一个数组元素 int a[10]={1,3,5,7,9,11,13,15,17,19}; int *p; p=&a[0]; 注意:数组名a不代表整个数组,只代表数组首元素的地址。“p=a;”的作用是“把a数组的首元素的地址赋给指针变量p”,而不是“把数组a各元素的值赋给p”。 等价于p=a; 等价于int *p=a; 或int *p=&a[0];

8.3.2 在引用数组元素时指针的运算 在指针指向数组元素时,允许以下运算: 加一个整数(用+或+=),如p+1 自加运算,如p++,++p 自减运算,如p--,--p 两个指针相减,如p1-p2 (只有p1和p2都指向同一数组中的元素时才有意义)

(1) 如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素,p-1指向同一数组中的上一个元素。 float a[10],*p=a; 假设a[0]的地址为2000,则 p的值为2000 p+1的值为2004 P-1的值为1996 越界

(2) 如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,或者说,它们指向a数组序号为i的元素 p+1,a+1 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] (2) 如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,或者说,它们指向a数组序号为i的元素 p+i,a+i p+9,a+9

(3) *(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。 p+1,a+1 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] (3) *(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。 p+i,a+i *(p+i) p+9,a+9

(4) 如果指针p1和p2都指向同一数组 p2-p1的值是4 不能p1+p2 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] (4) 如果指针p1和p2都指向同一数组 p2-p1的值是4 不能p1+p2 p1 p2

8.3.3 通过指针引用数组元素 引用一个数组元素,可用下面两种方法: (1) 下标法,如a[i]形式 (2) 指针法,如*(a+i)或*(p+i) 其中a是数组名,p是指向数组元素的指针变量,其初值p=a

8.3.3 通过指针引用数组元素 例8.6 有一个整型数组a,有10个元素,要求输出数组中的全部元素。 解题思路:引用数组中各元素的值有3种方法:(1)下标法;(2)通过数组名计算数组元素地址,找出元素的值;(3) 用指针变量指向数组元素 分别写出程序,以资比较分析。

(1) 下标法。 #include <stdio (1) 下标法。 #include <stdio.h> int main() { int a[10]; int i; printf(“enter 10 integer numbers:\n"); for(i=0;i<10;i++) scanf("%d",&a[i]); for(i=0;i<10;i++) printf(“%d ”,a[i]); printf("%\n"); return 0; }

(2) 通过数组名计算数组元素地址,找出元素的值 #include <stdio (2) 通过数组名计算数组元素地址,找出元素的值 #include <stdio.h> int main() { int a[10]; int i; printf(“enter 10 integer numbers:\n"); for(i=0;i<10;i++) scanf("%d",&a[i]); for(i=0;i<10;i++) printf(“%d ”,*(a+i)); printf("\n"); return 0; } scanf("%d",a+i);

(3) 用指针变量指向数组元素 #include <stdio. h> int main() { int a[10]; int (3) 用指针变量指向数组元素 #include <stdio.h> int main() { int a[10]; int *p,i; printf(“enter 10 integer numbers:\n"); for(i=0;i<10;i++) scanf("%d",&a[i]); for(p=a;p<(a+10);p++) printf(“%d ”,*p); printf("\n"); return 0; } for(p=a;p<(a+10);p++) scanf("%d",p); for(p=a;p<(a+10);a++) printf(“%d ”,*a); 错!

3种方法的比较: ① 第(1)和第(2)种方法执行效率相同 C编译系统是将a[i]转换为*(a+i)处理的,即先计算元素地址。 因此用第(1)和第(2)种方法找数组元素费时较多。

3种方法的比较: ② 第(3)种方法比第(1)、第(2)种方法快 用指针变量直接指向元素,不必每次都重新计算地址,像p++这样的自加操作是比较快的 这种有规律地改变地址值(p++)能大大提高执行效率

3种方法的比较: ③ 用下标法比较直观,能直接知道是第几个元素。 用地址法或指针变量的方法不直观,难以很快地判断出当前处理的是哪一个元素。

例8.7 通过指针变量输出整型数组a的10个元素。 解题思路: 用指针变量p指向数组元素,通过改变指针变量的值,使p先后指向a[0]到a[9]各元素。

#include <stdio. h> int main() { int #include <stdio.h> int main() { int *p,i,a[10]; p=a; printf(“enter 10 integer numbers:\n"); for(i=0;i<10;i++) scanf(“%d”,p++); for(i=0;i<10;i++,p++) printf(“%d ”,*p); printf("\n"); return 0; } 重新执行 p=a; 退出循环时p指向a[9]后面的存储单元 因此执行此循环出问题

8.3.4 用数组名作函数参数 用数组名作函数参数时,因为实参数组名代表该数组首元素的地址,形参应该是一个指针变量 C编译都是将形参数组名作为指针变量来处理的

int main() { void fun(int arr[],int n]; int array[10];  ┇ fun (array,10); return 0; } void fun(int arr[ ],int n) { ┇ } fun(int *arr,int n)

array[0] arr arr[0] array[3] arr+3 arr[3] array数组 int main() { void fun(int arr[],int n]; int array[10]; ┇ fun (array,10); return 0; } void fun(int *arr,int n) { ┇ } array[3] arr+3 arr[3] array数组

实参数组名是指针常量,但形参数组名是按指针变量处理 在函数调用后,形参的值就是实参数组首元素的地址 在函数执行期间,形参数组可以再被赋值 void fun (int arr[ ],int n) { printf(″%d\n″, *arr); arr=arr+3; printf(″%d\n″, *arr); }

例8.8 将数组a中n个整数按相反顺序存放 解题思路:将a[0]与a[n-1]对换,……将a[4]与a[5]对换。 i j

例8.8 将数组a中n个整数按相反顺序存放 解题思路:将a[0]与a[n-1]对换,……将a[4]与a[5]对换。 i j

例8.8 将数组a中n个整数按相反顺序存放 解题思路:将a[0]与a[n-1]对换,……将a[4]与a[5]对换。 i j

例8.8 将数组a中n个整数按相反顺序存放 解题思路:将a[0]与a[n-1]对换,……将a[4]与a[5]对换。 i j

例8.8 将数组a中n个整数按相反顺序存放 解题思路:将a[0]与a[n-1]对换,……将a[4]与a[5]对换。 i j

#include <stdio.h> int main() { void inv(int x[ ],int n); int i, a[10]={3,7,9,11,0,6,7,5,4,2}; for(i=0;i<10;i++) printf(“%d ”,a[i]); printf("\n"); inv(a,10); return 0; }

void inv(int x[ ],int n) { int temp,i,j,m=(n-1)/2; for(i=0;i<=m;i++) { j=n-1-i; temp=x[i];x[i]=x[j];x[j]=temp; } void inv(int x[ ],int n) { int temp,*i,*j; i=x; j=x+n-1; for( ; i<j; i++,j--) { temp=*i; *i=*j; *j=temp; } } 优化

例8.9 改写例8.8,用指针变量作实参。 #include <stdio.h> 不可少!!! int main() { void inv(int *x,int n); int i, arr[10],*p=arr; for(i=0;i<10;i++,p++) scanf(“%d”,p); inv(p,10); for(p=arr;p<arr+10;p++) printf(“%d ”,*p); printf("\n"); return 0; } 不可少!!!

例8.10 用指针方法对10个整数按由大到小顺序排序。 解题思路: 在主函数中定义数组a存放10个整数,定义int *型指针变量p指向a[0] 定义函数sort使数组a中的元素按由大到小的顺序排列 在主函数中调用sort函数,用指针p作实参 用选择法进行排序

#include <stdio.h> int main() { void sort(int x[ ],int n); int i,*p,a[10]; p=a; for(i=0;i<10;i++) scanf(“%d”,p++); sort(p,10); for(p=a,i=0;i<10;i++) { printf(“%d ”,*p); p++; } printf("\n"); return 0; }

if (*(x+j)>*(x+k)) k=j; {t=*(x+i);*(x+i)=*(x+k);*(x+k)=t;} void sort(int *x,int n) void sort(int x[],int n) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(x[j]>x[k]) k=j; if(k!=i) { t=x[i];x[i]=x[k];x[k]=t; } } if (*(x+j)>*(x+k)) k=j; {t=*(x+i);*(x+i)=*(x+k);*(x+k)=t;}

8.3.5 通过指针引用多维数组 指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。但在概念上和使用方法上,多维数组的指针比一维数组的指针要复杂一些。

8.3.5 通过指针引用多维数组 1. 多维数组元素的地址 int a[3][4]={{1,3,5,7}, {9,11,13,15},{17,19,21,23}}; a[0] a[0]+1 a[0]+2 a[0]+3 列指针 a a+1 a[0] a[1] a[2] 1 3 5 7 9 11 13 15 17 19 21 23 a+2 行指针

a a+1 a+2 行指针每加1,走一行 a代表第0行首地址 a+1代表第1行首地址 a+2代表第2行首地址 a[0] a[0]+1 列指针 a a+1 a[0] a[1] a[2] 1 3 5 7 9 11 13 15 17 19 21 23 a+2 行指针

a a+1 a+2 a+i代表行号为i的行首地址(按行变化) *(a+i)代表什么? 相当于a[i] a[0] a[0]+1 a[0]+2 列指针 a a+1 a[0] a[1] a[2] 1 3 5 7 9 11 13 15 17 19 21 23 a+2 行指针

a a+1 a+2 列指针每加1,走一列 a[0]代表a[0][0]的地址 a[0]+1代表a[0][1]的地址 3 5 7 9 11 13 15 17 19 21 23 a+2 行指针

a a+1 a+2 a[1]代表谁的地址? a[1]+1代表谁的地址? a[1]+2代表谁的地址? a[1]+3代表谁的地址? a[0] 列指针 a a+1 a[0] a[1] a[2] 1 3 5 7 9 11 13 15 17 19 21 23 a+2 行指针

a a+1 a+2 a[i]+j代表谁的地址? 代表a[i][j]的地址 *(a[i]+j)代表什么? 代表元素a[i][j] 列指针 a a+1 a[0] a[1] a[2] 1 3 5 7 9 11 13 15 17 19 21 23 a+2 行指针

例8. 11 二维数组的有关数据(地址和值) #include <stdio 例8.11 二维数组的有关数据(地址和值) #include <stdio.h> int main() { int a[3][4]={1,3,5,7,9,11,13,15, 17,19,21,23};

printf(“%d,%d\n”,a,. a); printf(“%d,%d\n”,a[0], printf(“%d,%d\n”,a,*a); printf(“%d,%d\n”,a[0],*(a+0)); printf(“%d,%d\n”,&a[0],&a[0][0]); printf(“%d,%d\n”,a[1],a+1); printf(“%d,%d\n”,&a[1][0],*(a+1)+0); printf(“%d,%d\n”,a[2],*(a+2)); printf(“%d,%d\n”,&a[2],a+2); printf(“%d,%d\n”,a[1][0],*(*(a+1)+0)); printf(“%d,%d\n”,*a[2],*(*(a+2)+0)); return 0; }

printf(“%d,%d\n”,a,. a); printf(“%d,%d\n”,a[0], printf(“%d,%d\n”,a,*a); printf(“%d,%d\n”,a[0],*(a+0)); printf(“%d,%d\n”,&a[0],&a[0][0]); printf(“%d,%d\n”,a[1],a+1); printf(“%d,%d\n”,&a[1][0],*(a+1)+0); printf(“%d,%d\n”,a[2],*(a+2)); printf(“%d,%d\n”,&a[2],a+2); printf(“%d,%d\n”,a[1][0],*(*(a+1)+0)); printf(“%d,%d\n”,*a[2],*(*(a+2)+0)); return 0; }

printf(“%d,%d\n”,a,. a); printf(“%d,%d\n”,a[0], printf(“%d,%d\n”,a,*a); printf(“%d,%d\n”,a[0],*(a+0)); printf(“%d,%d\n”,&a[0],&a[0][0]); printf(“%d,%d\n”,a[1],a+1); printf(“%d,%d\n”,&a[1][0],*(a+1)+0); printf(“%d,%d\n”,a[2],*(a+2)); printf(“%d,%d\n”,&a[2],a+2); printf(“%d,%d\n”,a[1][0],*(*(a+1)+0)); printf(“%d,%d\n”,*a[2],*(*(a+2)+0)); return 0; }

2. 指向多维数组元素的指针变量 (1) 指向数组元素的指针变量 例8 2. 指向多维数组元素的指针变量 (1) 指向数组元素的指针变量 例8.12 有一个3×4的二维数组,要求用指向元素的指针变量输出二维数组各元素的值。

解题思路: 二维数组的元素是整型的,它相当于整型变量,可以用int*型指针变量指向它 二维数组的元素在内存中是按行顺序存放的,即存放完序号为0的行中的全部元素后,接着存放序号为1的行中的全部元素,依此类推 因此可以用一个指向整型元素的指针变量,依次指向各个元素

#include <stdio.h> int main() { int a[3][4]={1,3,5,7,9,11,13,15, 17,19,21,23}; int *p; for(p=a[0];p<a[0]+12;p++) { if((p-a[0])%4==0) printf(“\n”); printf(“%4d”,*p); } printf("\n"); return 0; 逐个访问各元素时常用此类指针 控制换行

(2) 指向由m个元素组成的一维数组的指针变量 例8.13 输出二维数组任一行任一列元素的值。 解题思路:假设仍然用例8.12程序中的二维数组,例8.12中定义的指针变量是指向变量或数组元素的,现在改用指向一维数组的指针变量。

#include <stdio.h> int main() {int a[3][4]={1,3,5,7,9,11,13,15, 17,19,21,23}; int (*p)[4],i,j; p=a; printf(“enter row and colum:"); scanf(“%d,%d”,&i,&j); printf(“a[%d,%d]=%d\n”, i,j,*(*(p+i)+j)); return 0; } 行指针 a[i][j]

3. 用指向数组的指针作函数参数 一维数组名可以作为函数参数,多维数组名也可作函数参数。 用指针变量作形参,以接受实参数组名传递来的地址。 可以有两种方法: ①用指向变量的指针变量 ②用指向一维数组的指针变量

例8.14 有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩。 解题思路:这个题目是很简单的。本例用指向数组的指针作函数参数。用函数average求总平均成绩,用函数search找出并输出第i个学生的成绩。

#include <stdio. h> int main() { void average(float #include <stdio.h> int main() { void average(float *p,int n); void search(float (*p)[4],int n); float score[3][4]={{65,67,70,60}, {80,87,90,81},{90,99,100,98}}; average(*score,12); search(score,2); return 0; } score[0][0]的地址

p 65 67 70 60 80 87 90 81 99 100 98 p+1 void average(float *p,int n) { float *p_end; float 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); } p_end

#include <stdio. h> int main() { void average(float #include <stdio.h> int main() { void average(float *p,int n); void search(float (*p)[4],int n); float score[3][4]={{65,67,70,60}, {80,87,90,81},{90,99,100,98}}; average(*score,12); search(score,2); return 0; } 二维数组首行地址

void search(float (. p)[4],int n) { int i; printf("The score of No void search(float (*p)[4],int n) { int i; printf("The score of No.%d are:\n",n); for(i=0;i<4;i++) printf("%5.2f ",*(*(p+n)+i)); printf("\n"); } p 65 67 70 60 80 87 90 81 99 100 98 p+2

例8.15 在上题基础上,查找有一门以上课程不及格的学生,输出他们的全部课程的成绩。 解题思路:在主函数中定义二维数组score,定义search函数实现输出有一门以上课程不及格的学生的全部课程的成绩,形参p的类型是float(*)[4]。在调用search函数时,用score作为实参,把score[0]的地址传给形参p。

#include <stdio. h> int main() { void search(float ( #include <stdio.h> int main() { void search(float (*p)[4],int n); float score[3][4]={{65,57,70,60}, {58,87,90,81},{90,99,100,98}}; search(score,3); return 0; }

void search(float (*p)[4],int n) { int i,j,flag; for(j=0;j<n;j++) { flag=0; for(i=0;i<4;i++) if(*(*(p+j)+i)<60) flag=1; if(flag==1) { printf("No.%d fails\n",j+1); printf(“%5.1f ”,*(*(p+j)+i)); printf("\n"); } p 65 57 70 60 58 87 90 81 99 100 98

void search(float (*p)[4],int n) { int i,j,flag; for(j=0;j<n;j++) { flag=0; for(i=0;i<4;i++) if(*(*(p+j)+i)<60) flag=1; if(flag==1) { printf("No.%d fails\n",j+1); printf(“%5.1f ”,*(*(p+j)+i)); printf("\n"); } 发现不及格,赋1 若有不及格,则输出 不用flag,而用break语句如何改程序?

8.4 通过指针引用字符串 8.4.1 字符串的引用方式 8.4.2 字符指针作函数参数 8.4.3 使用字符指针变量和字符数组的比较

8.4.1 字符串的引用方式 字符串是存放在字符数组中的。引用一个字符串,可以用以下两种方法。 (1) 用字符数组存放一个字符串,可以通过数组名和格式声明“%s”输出该字符串,也可以通过数组名和下标引用字符串中一个字符。 (2) 用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量。

例8.16 定义一个字符数组,在其中存放字符串“I love China!”,输出该字符串和第8个字符。 解题思路:定义字符数组string,对它初始化,由于在初始化时字符的个数是确定的,因此可不必指定数组的长度。用数组名string和输出格式%s可以输出整个字符串。用数组名和下标可以引用任一数组元素。

#include <stdio. h> int main() { char string[]=“I love China #include <stdio.h> int main() { char string[]=“I love China!”; printf(“%s\n”,string); printf(“%c\n”,string[7]); return 0; } string string+7

例8.17 通过字符指针变量输出一个字符串。 解题思路:可以不定义字符数组,只定义一个字符指针变量,用它指向字符串常量中的字符。通过字符指针变量输出该字符串。

#include <stdio. h> int main() { char. string=“I love China #include <stdio.h> int main() { char *string=“I love China!”; printf(“%s\n”,string); return 0; } string char *string; string=” I love China!”;

#include <stdio. h> int main() { char. string=“I love China #include <stdio.h> int main() { char *string=“I love China!”; printf(“%s\n”,string); string=”I am a student.”; return 0; } string

#include <stdio. h> int main() { char. string=“I love China #include <stdio.h> int main() { char *string=“I love China!”; printf(“%s\n”,string); string=”I am a student.”; return 0; } string

8.4.2 字符指针作函数参数 如果想把一个字符串从一个函数“传递”到另一个函数,可以用地址传递的办法,即用字符数组名作参数,也可以用字符指针变量作参数。 在被调用的函数中可以改变字符串的内容 在主调函数中可以引用改变后的字符串。

8.4.2 字符指针作函数参数 例8.20 用函数调用实现字符串的复制。 解题思路:定义一个函数copy_string用来实现字符串复制的功能,在主函数中调用此函数,函数的形参和实参可以分别用字符数组名或字符指针变量。分别编程,以供分析比较。

(1) 用字符数组名作为函数参数 #include <stdio (1) 用字符数组名作为函数参数 #include <stdio.h> int main() {void copy_string(char from[],char to[]); char a[]="I am a teacher."; char b[]="you are a student."; printf(“a=%s\nb=%s\n",a,b); printf("copy string a to string b:\n"); copy_string(a,b); return 0; }

void copy_string(char from[], char to[]) { int i=0; while(from[i] void copy_string(char from[], char to[]) { int i=0; while(from[i]!='\0') { to[i]=from[i]; i++; } to[i]='\0';

(2)用字符型指针变量作实参 copy_string不变,在main函数中定义字符指针变量from和to,分别指向两个字符数组a,b。 仅需要修改主函数代码

#include <stdio.h> int main() {void copy_string(char from[], char to[]); char a[]=“I am a teacher.”; char b[]=“you are a student.”; char *from=a,*to=b; printf(“a=%s\nb=%s\n",a,b); printf("\ncopy string a to string b:\n"); copy_string(from,to); return 0; }

(3)用字符指针变量作形参和实参

#include <stdio.h> int main() {void copy_string(char *from, char *to); char *a=“I am a teacher.”; char b[]=“You are a student.”; char *p=b; printf(“a=%s\nb=%s\n”,a,b); printf("\ncopy string a to string b:\n"); copy_string(a,p); return 0; }

void copy_string(char. from, char. to) { for( ;. from void copy_string(char *from, char *to) { for( ;*from!='\0'; from++,to++) { *to=*from; } *to='\0'; } 函数体有多种简化写法,请见主教材

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (2) 赋值方式。可以对字符指针变量赋值,但不能对数组名赋值。 char *a; a=”I love China!”; 对 char str[14];str[0]=’I’; 对 char str[14]; str=”I love China!”; 错

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (3)初始化的含义 char *a=”I love China!”;与 char *a; a=”I love China!”;等价

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (3)初始化的含义 char str[14]= ”I love China!”;与 char str[14]; str[]=”I love China!”; 不等价

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (4) 存储单元的内容 编译时为字符数组分配若干存储单元,以存放各元素的值,而对字符指针变量,只分配一个存储单元

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (4) 存储单元的内容 char *a; scanf(“%s”,a); 错 char *a,str[10]; a=str; scanf (“%s”,a); 对

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (5) 指针变量的值是可以改变的,而数组名代表一个固定的值(数组首元素的地址),不能改变。

#include <stdio.h> 例8.21 改变指针变量的值。 #include <stdio.h> int main() { char *a="I love China!"; a=a+7; printf(“%s\n”,a); return 0; } 不能改为 char a[]=“I love China!”;

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (6) 字符数组中各元素的值是可以改变的,但字符指针变量指向的字符串常量中的内容是不可以被取代的。 char a[]=”House”,*b=”House”; a[2]=’r’; 对

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (6) 字符数组中各元素的值是可以改变的,但字符指针变量指向的字符串常量中的内容是不可以被取代的。 char a[]=”House”,*b=”House”; b[2]=’r’; 错

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (7) 引用数组元数 对字符数组可以用下标法和地址法引用数组元素(a[5],*(a+5))。如果字符指针变量p=a,则也可以用指针变量带下标的形式和地址法引用(p[5],*(p+5))。

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 char *a=″I love China!″; 则a[5]的值是第6个字符,即字母’e’

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (8) 用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。

8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 char *format; format=”a=%d,b=%f\n”; printf(format,a,b); 相当于 printf(“a=%d,b=%f\n”,a,b);

8.5 指向函数的指针 8.5.1什么是函数指针 8.5.2用函数指针变量调用函数 8.5.3怎样定义和使用指向函数的指针变量 8.5.4用指向函数的指针作函数参数

8.5.1什么是函数指针 如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间的起始地址,称为这个函数的指针。

8.5.1什么是函数指针 可以定义一个指向函数的指针变量,用来存放某一函数的起始地址,这就意味着此指针变量指向该函数。例如: int (*p)(int,int); 定义p是指向函数的指针变量,它可以指向类型为整型且有两个整型参数的函数。p的类型用int (*)(int,int)表示

8.5.2 用函数指针变量调用函数 例8.22 用函数求整数a和b中的大者。 解题思路:定义一个函数max,实现求两个整数中的大者。在主函数调用max函数,除了可以通过函数名调用外,还可以通过指向函数的指针变量来实现。分别编程并作比较。

(1)通过函数名调用函数 #include <stdio.h> int main() { int max(int,int); int a,b,c; printf("please enter a and b:"); scanf("%d,%d",&a,&b); c=max(a,b); printf(“%d,%d,max=%d\n",a,b,c); return 0; }

int max(int x,int y) { int z; if(x>y) z=x; else z=y; return(z); }

(2)通过指针变量访问它所指向的函数 #include <stdio.h> 只能指向函数返回值为整型且有两个整型参数的函数 int main() { int max(int,int); int (*p)(int,int); int a,b,c; p=max; printf("please enter a and b:"); scanf("%d,%d",&a,&b); c=(*p)(a,b); printf(“%d,%d,max=%d\n",a,b,c); return 0; } 只能指向函数返回值为整型且有两个整型参数的函数 必须先指向,若写成 p=max(a,b); 错

8.5.3 怎样定义和使用指向函数的指针变量 定义指向函数的指针变量的一般形式为 数据类型 (*指针变量名)(函数参数表列); 如 int (*p)(int,int); p=max; 对 p=max(a,b); 错 p+n,p++,p--等运算无意义

例8.23 输入两个整数,然后让用户选择1或2,选1时调用max函数,输出二者中的大数,选2时调用min函数,输出二者中的小数。 解题思路:定义两个函数max和min,分别用来求大数和小数。在主函数中根据用户输入的数字1或2,使指针变量指向max函数或min函数。

#include <stdio.h> int main() {int max(int,int); int min(int x,int y); int (*p)(int,int); int a,b,c,n; scanf("%d,%d",&a,&b); scanf(“%d”,&n); if (n==1) p=max; else if (n==2) p=min; c=(*p)(a,b); printf("a=%d,b=%d\n",a,b); if (n==1) printf("max=%d\n",c); else printf("min=%d\n",c); return 0; } 只看此行看不出调用哪函数

int max(int x,int y) { int z; if(x>y) z=x; else z=y; return(z); } int min(int x,int y) if(x<y) z=x; }

8.5.4 用指向函数的指针作函数参数 指向函数的指针变量的一个重要用途是把函数的地址作为参数传递到其他函数 指向函数的指针可以作为函数参数,把函数的入口地址传递给形参,这样就能够在被调用的函数中使用实参函数

8.5.4 用指向函数的指针作函数参数 …… int main() { …… fun(f1,f2) …… } void fun(int (*x1)(int),int (*x2)(int,int)) { int a,b,i=3,j=5; a=(*x1)(i); b=(*x2)(i,j); } 相当于a=f1(i); 相当于b=f2(i,j);

8.6 返回指针值的函数 一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已 定义返回指针值的函数的一般形式为 类型名 *函数名(参数表列);

例8.25有a个学生,每个学生有b门课程的成绩。要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数实现。

解题思路: 定义二维数组score存放成绩 定义输出某学生全部成绩的函数search,它是返回指针的函数,形参是行指针和整型 主函数将score和要找的学号k传递给形参 函数的返回值是&score[k][0](k号学生的序号为0的课程地址) 在主函数中输出该生的全部成绩

#include <stdio.h> int main() {float score[ ][4]={{60,70,80,90}, {56,89,67,88},{34,78,90,66}}; float *search(float (*pointer)[4],int n); float *p; int i,k; scanf(“%d”,&k); printf("The scores of No.%d are:\n",k); p=search(score,k); for(i=0;i<4;i++) printf(“%5.2f\t”,*(p+i)); printf("\n"); return 0; } 返回k号学生课程首地址

float. search(float (. pointer)[4],int n) { float. pt; pt= float *search(float (*pointer)[4],int n) { float *pt; pt=*(pointer+n); return(pt); }

8.7 指针数组和多重指针 8.7.1 什么是指针数组 8.7.2 指向指针数据的指针 8.7.3 指针数组作main函数的形参

8.7.1 什么是指针数组 一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都存放一个地址,相当于一个指针变量。 定义一维指针数组的一般形式为 类型名*数组名[数组长度]; int *p[4];

8.7.1 什么是指针数组 指针数组比较适合用来指向若干个字符串,使字符串处理更加方便灵活 可以分别定义一些字符串,然后用指针数组中的元素分别指向各字符串 由于各字符串长度一般是不相等的,所以比用二维数组节省内存单元

8.7.1 什么是指针数组 例8.27 将若干字符串按字母顺序(由小到大)输出。 解题思路:定义一个指针数组,用各字符串对它进行初始化,然后用选择法排序,但不是移动字符串,而是改变指针数组的各元素的指向。

#include <stdio. h> #include <string #include <stdio.h> #include <string.h> int main() {void sort(char *name[ ],int n); void print(char *name[ ],int n); char *name[ ]={“Follow”,“Great”, “FORTRAN”,“Computer”}; int n=4; sort(name,n); print(name,n); return 0; } F o l w \0 G r e a t \0 F O R T A N \0 C o m p u t e r \0

void sort(char. name[ ],int n) {char void sort(char *name[ ],int n) {char *temp; int i,j,k; for (i=0;i<n-1;i++) { k=i; for (j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if (k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp; } F o l w \0 name[0] name[1] name[2] name[3] G r e a t \0 F O R T A N \0 C o m p u t e r \0

void sort(char. name[ ],int n) {char void sort(char *name[ ],int n) {char *temp; int i,j,k; for (i=0;i<n-1;i++) { k=i; for (j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if (k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp; } 执行后k变为3 i=0时 F o l w \0 name[0] name[1] name[2] name[3] G r e a t \0 F O R T A N \0 C o m p u t e r \0

void sort(char. name[ ],int n) {char void sort(char *name[ ],int n) {char *temp; int i,j,k; for (i=0;i<n-1;i++) { k=i; for (j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if (k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp; } F o l w \0 name[0] name[1] name[2] name[3] G r e a t \0 F O R T A N \0 C o m p u t e r \0

void sort(char. name[ ],int n) {char void sort(char *name[ ],int n) {char *temp; int i,j,k; for (i=0;i<n-1;i++) { k=i; for (j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if (k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp; } 执行后k变为2 i=1时 F o l w \0 name[0] name[1] name[2] name[3] G r e a t \0 F O R T A N \0 C o m p u t e r \0

void sort(char. name[ ],int n) {char void sort(char *name[ ],int n) {char *temp; int i,j,k; for (i=0;i<n-1;i++) { k=i; for (j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if (k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp; } F o l w \0 name[0] name[1] name[2] name[3] G r e a t \0 F O R T A N \0 C o m p u t e r \0

void sort(char. name[ ],int n) {char void sort(char *name[ ],int n) {char *temp; int i,j,k; for (i=0;i<n-1;i++) { k=i; for (j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if (k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp; } 执行后k变为3 i=2时 F o l w \0 name[0] name[1] name[2] name[3] G r e a t \0 F O R T A N \0 C o m p u t e r \0

void sort(char. name[ ],int n) {char void sort(char *name[ ],int n) {char *temp; int i,j,k; for (i=0;i<n-1;i++) { k=i; for (j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if (k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp; } F o l w \0 name[0] name[1] name[2] name[3] G r e a t \0 F O R T A N \0 C o m p u t e r \0

void sort(char. name[ ],int n) {char void sort(char *name[ ],int n) {char *temp; int i,j,k; for (i=0;i<n-1;i++) { k=i; for (j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if (k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp; } F o l w \0 name[0] name[1] name[2] name[3] G r e a t \0 F O R T A N \0 C o m p u t e r \0

void sort(char. name[ ],int n) {char void sort(char *name[ ],int n) {char *temp; int i,j,k; for (i=0;i<n-1;i++) { k=i; for (j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if (k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp; } F o l w \0 name[0] name[1] name[2] name[3] G r e a t \0 F O R T A N \0 C o m p u t e r \0

void print(char *name[ ],int n) { int i; for(i=0;i<n;i++) printf(“%s\n”,name[i]); } w \0 name[0] name[1] name[2] name[3] G r e a t \0 F O R T A N \0 C o m p u t e r \0

void print(char *name[ ],int n) { int i; for(i=0;i<n;i++) printf(“%s\n”,name[i]); } { int i=0; char *p; p=name[0]; while(i<n) { p=*(name+i++); printf("%s\n",p); }

8.7.2 指向指针数据的指针 在了解了指针数组的基础上,需要了解指向指针数据的指针变量,简称为指向指针的指针。 name F o l w \0 name[0] name[1] name[2] name[3] p G r e a t \0 F O R T A N \0 C o m p u t e r \0

例8.28 使用指向指针数据的指针变量。 char *name[]={“Follow”,,“Great”, “FORTRAN”,“Computer”}; char **p; int i; for(i=0;i<5;i++) { p=name+i; printf("%s\n",*p); } name F o l w \0 name[0] name[1] name[2] name[3] p G r e a t \0 F O R T A N \0 C o m p u t e r \0

例8.29 有一个指针数组,其元素分别指向一个整型数组的元素,用指向指针数据的指针变量,输出整型数组各元素的值。

#include <stdio. h> int main() {int a[5]={1,3,5,7,9}; int #include <stdio.h> int main() {int a[5]={1,3,5,7,9}; int *num[5]={&a[0],&a[1],&a[2], &a[3],&a[4]}; int **p,i; p=num; for(i=0;i<5;i++) { printf("%d ",**p); p++; } printf("\n"); return 0; num a p &a[0] &a[1] &a[2] &a[3] &a[4] 1 2 3 4 5

8.7.3 指针数组作main函数的形参 指针数组的一个重要应用是作为main函数的形参。在以往的程序中,main函数的第一行一般写成以下形式: int main() 或 int main(void) 表示main函数没有参数,调用main函数时不必给出实参。 这是一般程序常采用的形式。

8.7.3 指针数组作main函数的形参 实际上,在某些情况下,main函数可以有参数,例如: int main(int argc,char *argv[]) 其中,argc和argv就是main函数的形参,它们是程序的“命令行参数”。 argv是*char指针数组,数组中每一个元素(其值为指针)指向命令行中的一个字符串。

8.7.3 指针数组作main函数的形参 通常main函数和其他函数组成一个文件模块,有一个文件名。 对这个文件进行编译和连接,得到可执行文件(后缀为.exe)。用户执行这个可执行文件,操作系统就调用main函数,然后由main函数调用其他函数,从而完成程序的功能。

8.7.3 指针数组作main函数的形参 main函数的形参是从哪里传递给它们的呢? 显然形参的值不可能在程序中得到。

#include <stdio.h> int main(int argc,char *argv[]) { while(argc>1) { ++argv; printf(“%s\n”, *argv); --argc; } return 0; 在VC++环境下编译、连接后,“工程”—“设置”—“调试”—“程序变量”中输入“China Beijing”,再运行就可得到结果。 或在命令行输入 exe_file China Beijing

8.8 动态内存分配与指向它的指针变量 8.8.1 什么是内存的动态分配 8.8.2 怎样建立内存的动态分配 8.8.3 void指针类型

8.8.1 什么是内存的动态分配 非静态的局部变量是分配在内存中的动态存储区的,这个存储区是一个称为栈的区域 C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据需要时随时开辟,不需要时随时释放。这些数据是临时存放在一个特别的自由存储区,称为堆区

8.8.2 怎样建立内存的动态分配 对内存的动态分配是通过系统提供的库函数来实现的,主要有malloc,calloc,free,realloc这4个函数。

8.8.2 怎样建立内存的动态分配 1.malloc函数 其函数原型为 void *malloc(unsigned int size); 函数的值是所分配区域的第一个字节的地址,或者说,此函数是一个指针型函数,返回的指针指向该分配域的开头位置

8.8.2 怎样建立内存的动态分配 malloc(100); 注意指针的基类型为void,即不指向任何类型的数据,只提供一个地址 开辟100字节的临时分配域,函数值为其第1个字节的地址 注意指针的基类型为void,即不指向任何类型的数据,只提供一个地址 如果此函数未能成功地执行(例如内存空间不足),则返回空指针(NULL)

8.8.2 怎样建立内存的动态分配 2.calloc函数 其函数原型为 void *calloc(unsigned n,unsigned size); 其作用是在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组。

8.8.2 怎样建立内存的动态分配 用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。这就是动态数组。函数返回指向所分配域的起始位置的指针;如果分配不成功,返回NULL。如: p=calloc(50,4); 开辟50×4个字节的临时分配域,把起始地址赋给指针变量p

8.8.2 怎样建立内存的动态分配 3.free函数 其函数原型为 void free(void *p); 其作用是释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。p应是最近一次调用calloc或malloc函数时得到的函数返回值。

8.8.2 怎样建立内存的动态分配 free(p); 释放指针变量p所指向的已分配的动态空间 free函数无返回值

8.8.2 怎样建立内存的动态分配 4. realloc函数 其函数原型为 void *realloc(void *p,unsigned int size); 如果已经通过malloc函数或calloc函数获得了动态空间,想改变其大小,可以用recalloc函数重新分配。

8.8.2 怎样建立内存的动态分配 用realloc函数将p所指向的动态空间的大小改变为size。p的值不变。如果重分配不成功,返回NULL。如 realloc(p,50); 将p所指向的已分配的动态空间改为50字节

8.8.2 怎样建立内存的动态分配 以上4个函数的声明在stdlib.h头文件中,在用到这些函数时应当用“#include <stdlib.h>”指令把stdlib.h头文件包含到程序文件中。

8.8.3 void指针类型 例8.30 建立动态数组,输入5个学生的成绩,另外用一个函数检查其中有无低于60分的,输出不合格的成绩。

#include <stdio. h> #include <stdlib #include <stdio.h> #include <stdlib.h> int main() { void check(int *); int *p1,i; p1=(int *)malloc(5*sizeof(int)); for(i=0;i<5;i++) scanf("%d",p1+i); check(p1); free(p1); return 0; }

void check(int *p) { int i; printf("They are fail:"); for(i=0;i<5;i++) if (p[i]<60) printf("%d ",p[i]); printf("\n"); }