Download presentation
Presentation is loading. Please wait.
Published byHendra Lesmana Modified 6年之前
1
第8章 善于利用指针 8.1 指针是什么 8.2 指针变量 8.3 通过指针引用数组 8.4 通过指针引用字符串 8.5 指向函数的指针
8.6 返回指针值的函数 8.7 指针数组和多重指针 8.8 动态内存分配与指向它的指针变量 8.9 有关指针的小结
2
8.1 指針是什么 如果在程序中定义了一个变量,在对程序进行编译时,系统就会给该变量分配内存单元
例如,VC++为整型变量分配4个字节,对单精度浮点型变量分配4个字节,对字符型变量分配1个字节
3
8.1 指针是什么 内存区的每一个字节有一个编号,这就是“地址”,它相当于旅馆中的房间号。
在地址所标识的内存单元中存放数据,这相当于旅馆房间中居住的旅客一样。 由于通过地址能找到所需的变量单元,我们可以说,地址指向该变量单元。 将地址形象化地称为“指针”
4
务必弄清楚存储单元的地址和存储单元的内容这两个概念的区别
例如:
5
int i=3,j=6,k; printf(“%d”,i); 通过变量名i 找到i的地址2000,从而从存储单元读取3
6
int i=3,j=6,k; k=i+j; 从这里取3 从这里取6 将9送到这里 直接存取
7
间接存取 int i=3,j=6,k; 定义特殊变量i_pointer i_pointer=&i; 50 *i_pointer=50;
8
i 直接存取 3 2000 i_pointer *i_pointer 间接存取 2000 3 2000
9
为了表示将数值3送到变量中,可以有两种表达方法:
(1) 将3直接送到变量i所标识的单元中,例如:i=3; (2) 将3送到变量i_pointer所指向的单元(即变量i的存储单元),例如:*i_pointer=3; 其中*i_pointer表示i_pointer指向的对象
10
指向就是通过地址来体现的 假设i_pointer中的值是变量i的地址(2000),这样就在i_pointer和变量i之间建立起一种联系,即通过i_pointer能知道i的地址,从而找到变量i的内存单元
11
一个变量的地址称为该变量的“指针” 例如,地址2000是变量i的指针 如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量” i_pointer就是一个指针变量。指针变量就是地址变量,用来存放地址的变量,指針变量的值是地址(即指针)
12
“指针”和“指针变量”是不同的概念 可以说变量i的指针是2000,而不能说i的指针变量是2000 指针是一个地址,而指针变量是存放地址的变量
13
8.2 指针变量 8.2.1使用指针变量的例子 8.2.2 怎样定义指针变量 8.2.3 怎样引用指针变量 8.2.4 指针变量作为函数参数
14
8.2.1使用指针变量的例子 例8.1 通过指针变量访问整型变量。
解题思路:先定义2个整型变量,再定义2个指针变量,分别指向这两个整型变量,通过访问指针变量,可以找到它们所指向的变量,从而得到这些变量的值。
15
#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的值
16
此处*与类型名在一起。此时共同定义指针变量
#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; } 此处*与指针变量一起使用。此时代表指针变量所指向的变量
17
8.2.2 怎样定义指针变量 定义指针变量的一般形式为: 类型 * 指针变量名; 如:int *pointer_1, *pointer_2;
类型 * 指针变量名; 如:int *pointer_1, *pointer_2; int是为指针变量指定的“基类型” 基类型指定指针变量可指向的变量类型 如pointer_1可以指向整型变量,但不能指向浮点型变量
18
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; 错误
19
8.2.3 怎样引用指针变量 在引用指针变量时,可能有三种情况: 使p指向a 给指针变量赋值。如:p=&a; 引用指针变量指向的变量。如有
则执行printf(“%d”,*p); 将输出1 引用指针变量的值。如:printf(“%o”,p); *p相当于a 以八进制输出a的地址
20
8.2.3 怎样引用指针变量 要熟练掌握两个有关的运算符: &a是变量a的地址 (1) & 取地址运算符。
(1) & 取地址运算符。 &a是变量a的地址 (2) * 指针运算符(“间接访问”运算符) 如果: p指向变量a,则*p就代表a。 k=*p; (把a的值赋给k) *p=1; (把1赋给a)
21
例8.2 输入a和b两个整数,按先大后小的顺序输出a和b。
解题思路:用指针方法来处理这个问题。不交换整型变量的值,而是交换两个指针变量的值。
22
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 成立
23
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
24
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
25
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;?
26
注意: a和b的值并未交换,它们仍保持原值 但p1和p2的值改变了。p1的值原为&a,后来变成&b,p2原值为&b,后来变成&a
这样在输出*p1和*p2时,实际上是输出变量b和a的值,所以先输出9,然后输出5
27
8.2.4 指针变量作为函数参数 例8.3 题目要求同例8.2,即对输入的两个整数按大小顺序输出。现用函数处理,而且用指针类型的数据作函数参数。 解题思路:定义一个函数swap,将指向两个整型变量的指针变量作为实参传递给swap函数的形参指针变量,在函数中通过指针实现交换两个变量的值。
28
#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
29
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
30
void swap(int *p1,int *p2) { int temp; temp=*p1; *p1=*p2; *p2=temp; }
错!!! 无确定的指向
31
错!!! 无法交换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
32
例8.4 对输入的两个整数按大小顺序输出。 解题思路:尝试调用swap函数来实现题目要求。在函数中改变形参(指针变量)的值,希望能由此改变实参(指针变量)的值
33
#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; } 错!!! 只交换形参指向
34
注意:函数的调用可以(而且只可以)得到一个返回值(即函数值),而使用指针变量作参数,可以得到多个变化了的值。如果不用指针变量是难以做到这一点的。
要善于利用指针法。
35
8.3通过指针引用数组 8.3.1 数组元素的指针 8.3.2 在引用数组元素时指针的运算 8.3.3 通过指针引用数组元素
8.3.4 用数组名作函数参数 8.3.5 通过指针引用多维数组
36
8.3.1 数组元素的指针 一个变量有地址,一个数组包含若干元素,每个数组元素都有相应的地址
数组元素的指针 一个变量有地址,一个数组包含若干元素,每个数组元素都有相应的地址 指针变量可以指向数组元素(把某一元素的地址放到一个指针变量中) 所谓数组元素的指针就是数组元素的地址
37
可以用一个指针变量指向一个数组元素 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];
38
8.3.2 在引用数组元素时指针的运算 在指针指向数组元素时,允许以下运算: 加一个整数(用+或+=),如p+1
自加运算,如p++,++p 自减运算,如p--,--p 两个指针相减,如p1-p2 (只有p1和p2都指向同一数组中的元素时才有意义)
39
(1) 如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素,p-1指向同一数组中的上一个元素。
float a[10],*p=a; 假设a[0]的地址为2000,则 p的值为2000 p+1的值为2004 P-1的值为1996 越界
40
(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
41
(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
42
(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
43
8.3.3 通过指针引用数组元素 引用一个数组元素,可用下面两种方法: (1) 下标法,如a[i]形式
(2) 指针法,如*(a+i)或*(p+i) 其中a是数组名,p是指向数组元素的指针变量,其初值p=a
44
8.3.3 通过指针引用数组元素 例8.6 有一个整型数组a,有10个元素,要求输出数组中的全部元素。
解题思路:引用数组中各元素的值有3种方法:(1)下标法;(2)通过数组名计算数组元素地址,找出元素的值;(3) 用指针变量指向数组元素
45
(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; }
46
(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);
47
(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); 错!
48
3种方法的比较: ① 第(1)和第(2)种方法执行效率相同 C编译系统是将a[i]转换为*(a+i)处理的,即先计算元素地址。
因此用第(1)和第(2)种方法找数组元素费时较多。
49
3种方法的比较: ② 第(3)种方法比第(1)、第(2)种方法快
用指针变量直接指向元素,不必每次都重新计算地址,像p++这样的自加操作是比较快的 这种有规律地改变地址值(p++)能大大提高执行效率
50
3种方法的比较: ③ 用下标法比较直观,能直接知道是第几个元素。 用地址法或指针变量的方法不直观,难以很快地判断出当前处理的是哪一个元素。
51
例8.7 通过指针变量输出整型数组a的10个元素。 解题思路: 用指针变量p指向数组元素,通过改变指针变量的值,使p先后指向a[0]到a[9]各元素。
52
#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]后面的存储单元 因此执行此循环出问题
53
8.3.4 用数组名作函数参数 用数组名作函数参数时,因为实参数组名代表该数组首元素的地址,形参应该是一个指针变量
C编译都是将形参数组名作为指针变量来处理的
54
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)
55
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数组
56
实参数组名是指针常量,但形参数组名是按指针变量处理 在函数调用进行虚实结合后,它的值就是实参数组首元素的地址
在函数执行期间,形参数组可以再被赋值 void fun (arr[ ],int n) { printf(″%d\n″, *arr); arr=arr+3; printf(″%d\n″, *arr); }
57
例8.8 将数组a中n个整数按相反顺序存放 解题思路:将a[0]与a[n-1]对换,……将a[4]与a[5]对换。 i j
58
例8.8 将数组a中n个整数按相反顺序存放 解题思路:将a[0]与a[n-1]对换,……将a[4]与a[5]对换。 i j
59
例8.8 将数组a中n个整数按相反顺序存放 解题思路:将a[0]与a[n-1]对换,……将a[4]与a[5]对换。 i j
60
例8.8 将数组a中n个整数按相反顺序存放 解题思路:将a[0]与a[n-1]对换,……将a[4]与a[5]对换。 i j
61
例8.8 将数组a中n个整数按相反顺序存放 解题思路:将a[0]与a[n-1]对换,……将a[4]与a[5]对换。 i j
62
#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; }
63
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; } } 优化
64
例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; } 不可少!!!
65
例8.10 用指针方法对10个整数按由大到小顺序排序。 解题思路:
在主函数中定义数组a存放10个整数,定义int *型指针变量p指向a[0] 定义函数sort使数组a中的元素按由大到小的顺序排列 在主函数中调用sort函数,用指针p作实参 用选择法进行排序
66
#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; }
67
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;}
68
8.3.5 通过指针引用多维数组 指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。但在概念上和使用方法上,多维数组的指针比一维数组的指针要复杂一些。
69
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 行指针
70
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 行指针
71
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 行指针
72
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 行指针
73
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 行指针
74
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 行指针
75
例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};
76
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; }
77
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; }
78
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; }
79
2. 指向多维数组元素的指针变量 (1) 指向数组元素的指针变量 例8
2. 指向多维数组元素的指针变量 (1) 指向数组元素的指针变量 例8.12 有一个3×4的二维数组,要求用指向元素的指针变量输出二维数组各元素的值。
80
解题思路: 二维数组的元素是整型的,它相当于整型变量,可以用int*型指针变量指向它
二维数组的元素在内存中是按行顺序存放的,即存放完序号为0的行中的全部元素后,接着存放序号为1的行中的全部元素,依此类推 因此可以用一个指向整型元素的指针变量,依次指向各个元素
81
#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; 逐个访问各元素时常用此类指针 控制换行
82
(2) 指向由m个元素组成的一维数组的指针变量
例8.13 输出二维数组任一行任一列元素的值。 解题思路:假设仍然用例8.12程序中的二维数组,例8.12中定义的指针变量是指向变量或数组元素的,现在改用指向一维数组的指针变量。
83
#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]
84
3. 用指向数组的指针作函数参数 一维数组名可以作为函数参数,多维数组名也可作函数参数。 用指针变量作形参,以接受实参数组名传递来的地址。 可以有两种方法: ①用指向变量的指针变量 ②用指向一维数组的指针变量
85
例8.14 有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩。
解题思路:这个题目是很简单的。本例用指向数组的指针作函数参数。用函数average求总平均成绩,用函数search找出并输出第i个学生的成绩。
86
#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]的地址
87
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
88
#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; } 二维数组首行地址
89
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
90
例8.15 在上题基础上,查找有一门以上课程不及格的学生,输出他们的全部课程的成绩。
解题思路:在主函数中定义二维数组score,定义search函数实现输出有一门以上课程不及格的学生的全部课程的成绩,形参p的类型是float(*)[4]。在调用search函数时,用score作为实参,把score[0]的地址传给形参p。
91
#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; }
92
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
93
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语句如何改程序?
94
8.4 通过指针引用字符串 8.4.1 字符串的引用方式 8.4.2 字符指针作函数参数 8.4.3 使用字符指针变量和字符数组的比较
95
8.4.1 字符串的引用方式 字符串是存放在字符数组中的。引用一个字符串,可以用以下两种方法。
(1) 用字符数组存放一个字符串,可以通过数组名和格式声明“%s”输出该字符串,也可以通过数组名和下标引用字符串中一个字符。 (2) 用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量。
96
例8.16 定义一个字符数组,在其中存放字符串“I love China!”,输出该字符串和第8个字符。
97
#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
98
例8.17 通过字符指针变量输出一个字符串。
99
#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!”;
100
#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
101
#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
102
例8.18 将字符串a复制为字符串b,然后输出字符串b。
103
#include <stdio. h> int main() { char a[ ]=“I am a student
#include <stdio.h> int main() { char a[ ]=“I am a student.”,b[20]; int i; for(i=0;*(a+i)!='\0';i++) *(b+i)=*(a+i); *(b+i)=‘\0’; printf(“string a is:%s\n”,a); printf("string b is:"); for(i=0;b[i]!='\0';i++) printf(“%c”,b[i]); printf("\n"); return 0; } printf("string b is:%s\n“,b);
104
例8.19 用指针变量来处理例8.18问题。
105
#include <stdio. h> int main() {char a[]="I am a boy. ",b[20],
#include <stdio.h> int main() {char a[]="I am a boy.",b[20],*p1,*p2; p1=a; p2=b; for( ; *p1!=‘\0’; p1++,p2++) *p2=*p1; *p2=‘\0’; printf(“string a is:%s\n”,a); printf(“string b is:%s\n”,b); return 0; }
106
8.4.2 字符指针作函数参数 如果想把一个字符串从一个函数“传递”到另一个函数,可以用地址传递的办法,即用字符数组名作参数,也可以用字符指针变量作参数。 在被调用的函数中可以改变字符串的内容 在主调函数中可以引用改变后的字符串。
107
8.4.2 字符指针作函数参数 例8.20 用函数调用实现字符串的复制。
108
(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; }
109
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';
110
(2)用字符型指针变量作实参 copy_string不变,在main函数中定义字符指针变量from和to,分别指向两个字符数组a,b。 仅需要修改主函数代码
111
#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; }
112
(3)用字符指针变量作形参和实参
113
#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; }
114
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'; } 函数体有多种简化写法,请见主教材
115
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。
116
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (2) 赋值方式。可以对字符指针变量赋值,但不能对数组名赋值。 char *a; a=”I love China!”; 对 char str[14];str[0]=’I’; 对 char str[14]; str=”I love China!”; 错
117
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (3)初始化的含义 char *a=”I love China!”;与 char *a; a=”I love China!”;等价
118
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (3)初始化的含义 char str[14]= ”I love China!”;与 char str[14]; str[]=”I love China!”; 不等价
119
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (4) 存储单元的内容 编译时为字符数组分配若干存储单元,以存放各元素的值,而对字符指针变量,只分配一个存储单元
120
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (4) 存储单元的内容 char *a; scanf(“%s”,a); 错 char *a,str[10]; a=str; scanf (“%s”,a); 对
121
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (5) 指针变量的值是可以改变的,而数组名代表一个固定的值(数组首元素的地址),不能改变。
122
#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!”;
123
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (6) 字符数组中各元素的值是可以改变的,但字符指针变量指向的字符串常量中的内容是不可以被取代的。 char a[]=”House”,*b=” House”; a[2]=’r’; 对
124
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (6) 字符数组中各元素的值是可以改变的,但字符指针变量指向的字符串常量中的内容是不可以被取代的。 char a[]=”House”,*b=”House”; b[2]=’r’; 错
125
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (7) 引用数组元数 对字符数组可以用下标法和地址法引用数组元素(a[5],*(a+5))。如果字符指针变量p=a,则也可以用指针变量带下标的形式和地址法引用(p[5],*(p+5))。
126
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 char *a=″I love China!″; 则a[5]的值是第6个字符,即字母’e’
127
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 (8) 用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。
128
8.4.3 使用字符指针变量和字符数组的比较 用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。 char *format; format=”a=%d,b=%f\n”; printf(format,a,b); 相当于 printf(“a=%d,b=%f\n”,a,b);
129
练习 B A 对于类型相同的指针变量,不能进行哪种运算()? A) - B) + C) = D) >
若有以下说明,则正确的赋值表达式是()。 int x,*p; A) p=&x B) p=x C) *p=&x D) *p=*x B A
130
B 有如下语句int a=10,b=20,*p1,*p2;p1=&a;p2=&b;若要实现p1,p2均指向b,可选用的赋值语句是()。
A) *p1=*p2; B) p1=p2; C) p1=*p2; D) *p1=p2; B
131
B 以下程序的运行结果是()。 sub(int x,int y,int *z) {*z=y-x;} main() {int a,b,c;
sub(10,5,&a); sub(7,a,&b); sub(a,b,&c); printf("%4d,%4d,%4d\n",a,b,c);} A)5,2,3 B)-5,-12,-7 C)-5,-12,-17 D)5,-2,-7
132
B 以下程序中调用scanf函数给变量a输入数值的方法是错误的,其错误原因是()。 main() {int *p,*q,a,b; p=&a;
printf("input a:"); scanf("%d",*p); …… } A) *p表示的是指针变量p的地址 B) *p表示的是变量a的值,而不是变量a的地址 C) *p表示的是指针变量p的值 D) *p只能用来说明p是一个指针变量。 B
133
B D 变量的指针,其含义是指该变量的()。 A) 值 B) 地址 C)名 D)一个标志
若有说明:int *p,m=5,n; 以下正确的程序段是()。 A)p=&n; B)p=&n; scanf("%d",&p); scanf("%d",*p); C)scanf("%d",&n); D)p=&n; *p=n *p=m; D
134
8.5 指向函数的指针 8.5.1什么是函数指针 8.5.2用函数指针变量调用函数 8.5.3怎样定义和使用指向函数的指针变量
8.5.4用指向函数的指针作函数参数
135
8.5.1什么是函数指针 如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间的起始地址,称为这个函数的指针。
136
8.5.1什么是函数指针 可以定义一个指向函数的指针变量,用来存放某一函数的起始地址。例如: int (*p)(int,int);
定义p是指向函数的指针变量,它可以指向类型为整型且有两个整型参数的函数。p的类型用int (*)(int,int)表示
137
8.5.2 用函数指针变量调用函数 例8.22 用函数求整数a和b中的大者。
138
(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; }
139
int max(int x,int y) { int z; if(x>y) z=x; else z=y; return(z); }
140
(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); 错
141
8.5.3 怎样定义和使用指向函数的指针变量 定义指向函数的指针变量的一般形式为 数据类型 (*指针变量名)(函数参数表列);
如 int (*p)(int,int); p=max; 对 p=max(a,b); 错 p+n,p++,p--等运算无意义
142
例8.23 输入两个整数,然后让用户选择1或2,选1时调用max函数,输出二者中的大数,选2时调用min函数,输出二者中的小数。
143
#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; } 只看此行看不出调用哪函数
144
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; }
145
8.5.4 用指向函数的指针作函数参数 指向函数的指针变量的一个重要用途是把函数的地址作为参数传递到其他函数
指向函数的指针可以作为函数参数,把函数的入口地址传递给形参,这样就能够在被调用的函数中使用实参函数
146
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);
147
例8.24 有两个整数a和b,由用户输入1,2或3。如输入1,程序就给出a和b中大者,输入2,就给出a和b中小者,输入3,则求a与b之和。
解题思路:与例8.23相似,但现在用一个函数fun来实现以上功能。
148
#include <stdio. h> int main() {void fun(int x,int y, int (
#include <stdio.h> int main() {void fun(int x,int y, int (*p)(int,int)); int max(int,int); int min(int,int); int add(int,int); int a=34,b=-21,n; printf("please choose 1,2 or 3:"); scanf(“%d”,&n); if (n==1) fun(a,b,max); else if (n==2) fun(a,b,min); else if (n==3) fun(a,b,add); return 0; }
149
int fun(int x,int y,int (. p)(int,int)) { int resout; resout=(
int fun(int x,int y,int (*p)(int,int)) { int resout; resout=(*p)(x,y); printf(“%d\n”,resout); } int max(int x,int y) { int z; if(x>y) z=x; else z=y; printf("max=" ); return(z); } 输入的选项为1时 相当于max(x,y)
150
int fun(int x,int y,int (. p)(int,int)) { int resout; resout=(
int fun(int x,int y,int (*p)(int,int)) { int resout; resout=(*p)(x,y); printf(“%d\n”,resout); } int max(int x,int y) { int z; if(x>y) z=x; else z=y; printf("max=" ); return(z); } 输入的选项为2时 相当于min(x,y)
151
int fun(int x,int y,int (. p)(int,int)) { int result; result=(
int fun(int x,int y,int (*p)(int,int)) { int result; result=(*p)(x,y); printf(“%d\n”,result); } int max(int x,int y) { int z; if(x>y) z=x; else z=y; printf("max=" ); return(z); } 输入的选项为3时 相当于add(x,y)
152
int min(int x,int y) { int z; if(x<y) z=x; else z=y; printf("min="); return(z); } int add(int x,int y) z=x+y; printf("sum="); }
153
8.6 返回指针值的函数 一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已 定义返回指针值的函数的一般形式为 类型名 *函数名(参数表列);
154
例8.25有a个学生,每个学生有b门课程的成绩。要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数实现。
155
解题思路: 定义二维数组score存放成绩 定义输出某学生全部成绩的函数search,它是返回指针的函数,形参是行指针和整型
主函数将score和要找的学号k传递给形参 函数的返回值是&score[k][0](k号学生的序号为0的课程地址) 在主函数中输出该生的全部成绩
156
#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号学生课程首地址
157
float. search(float (. pointer)[4],int n) { float. pt; pt=
float *search(float (*pointer)[4],int n) { float *pt; pt=*(pointer+n); return(pt); }
158
例8.26对例8.25中的学生,找出其中有不及格的课程的学生及其学生号。
159
解题思路: 在例8.25程序基础上修改。 main函数不是只调用一次search函数,而是先后调用3次search函数,其中检查3个学生有无不及格的课程,如果有,就返回该学生的0号课程的地址&score[i][0],否则返回NULL 在main函数中检查返回值,输出有不及格学生4门课的成绩
160
…… float. search(float (. pointer)[4]); float
…… float *search(float (*pointer)[4]); float *p; int i,j; for(i=0;i<3;i++) { p=search(score+i); if(p==*(score+i)) { printf("No.%d score:",i); for(j=0;j<4;j++) printf(“%5.2f ”,*(p+j)); printf("\n"); } 相当于if(p!=NULL)
161
float. search(float (. pointer)[4]) { int i=0; float
float *search(float (*pointer)[4]) { int i=0; float *pt; pt=NULL; for( ;i<4;i++) if(*(*pointer+i)<60) pt=*pointer; return(pt); }
162
8.7 指针数组和多重指针 8.7.1 什么是指针数组 8.7.2 指向指针数据的指针 8.7.3 指针数组作main函数的形参
163
8.7.1 什么是指针数组 一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都存放一个地址,相当于一个指针变量。 定义一维指针数组的一般形式为 类型名*数组名[数组长度]; int *p[4];
164
8.7.1 什么是指针数组 指针数组比较适合用来指向若干个字符串,使字符串处理更加方便灵活
可以分别定义一些字符串,然后用指针数组中的元素分别指向各字符串 由于各字符串长度一般是不相等的,所以比用二维数组节省内存单元
165
8.7.1 什么是指针数组 例8.27 将若干字符串按字母顺序(由小到大)输出。
解题思路:定义一个指针数组,用各字符串对它进行初始化,然后用选择法排序,但不是移动字符串,而是改变指针数组的各元素的指向。
166
#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
167
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
168
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
169
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
170
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
171
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
172
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
173
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
174
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
175
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
176
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
177
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); }
178
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
179
例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
180
例8.29 有一个指针数组,其元素分别指向一个整型数组的元素,用指向指针数据的指针变量,输出整型数组各元素的值。
181
#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; p &a[0] &a[1] &a[2] &a[3] &a[4] 1 2 3 4 5
182
B 下面程序段运行结果是()。 char *p="%d,a=%d,b=%d\n"; int a=111,b=10,c;
c=a%b; p+=3; printf(p,c,a,b); A)1,a=111,b= B)a=1,b= C)a=111,b=10 D) 以上结果都不对。 B
183
B A 执行下面程序段后,ab的值为()。 int *var,ab; ab=100;var=&ab; ab=*var+10;
A) B) C) D)90 若有定义:int x,*pb;则正确的赋值表达式是()。 A)pb=&x B)pb=x C)*pb=&x D)*pb=*x B A
184
D 下面程序运行结果是( )。 void fun(int x) { printf("%d\n",++*x); main()
下面程序运行结果是( )。 void fun(int x) { printf("%d\n",++*x); main() {int a=25;fun(&a);} A)23 B)24 C)25 D)26 D
185
C 以下程序的输出结果是()。 a. 4 b. 6 c. 8 d. 10 main( ) { int k=2,m=4,n=6;
int *pk=&k,*pm=&m,*p; *(p=&n)=*pk*(*pm); printf("%d\n",n); } C
186
B D 若有int a[10]={1,2,3,4,5,6,7,8,9,10};*p=a;则数值为9的表达式是( )。
A. *p B. *(p+8) C. *p+=9 D. p+8 若有以下说明和语句,且0≤i<5,则下面哪个答案( )是对数组元素地址的正确表示? float a[]={1.6,3.0,-5.4,7.8,94.0},*p,i; p=a; A) &(p+i) B) a++ C) &p D) &p[i] B D
187
C 若有以下说明, 则a[*(a+a[3])]的值为()。 int a[]={8,1,2,5,0,4,7,6,3,9};
A) 8 B) 3 C) 0 D) 不合法 C
188
C 以下程序运行后,输出结果是()。 main( ) { int a[10]={19,23,44,17,37,28,49,36},*p;
p=a; printf("%d\n",(p+=3)[3]); } A) 44 B) 17 C) 49 D) 运行时出错,无定值 C
189
8.7.3 指针数组作main函数的形参 指针数组的一个重要应用是作为main函数的形参。在以往的程序中,main函数的第一行一般写成以下形式: int main() 或 int main(void) 表示main函数没有参数,调用main函数时不必给出实参。 这是一般程序常采用的形式。
190
8.7.3 指针数组作main函数的形参 实际上,在某些情况下,main函数可以有参数,例如:
int main(int argc,char *argv[]) 其中,argc和argv就是main函数的形参,它们是程序的“命令行参数”。 argv是*char指针数组,数组中每一个元素(其值为指针)指向命令行中的一个字符串。
191
8.7.3 指针数组作main函数的形参 通常main函数和其他函数组成一个文件模块,有一个文件名。
对这个文件进行编译和连接,得到可执行文件(后缀为.exe)。用户执行这个可执行文件,操作系统就调用main函数,然后由main函数调用其他函数,从而完成程序的功能。
192
8.7.3 指针数组作main函数的形参 main函数的形参是从哪里传递给它们的呢? 显然形参的值不可能在程序中得到。
193
#include <stdio.h>
int main(int argc,char *argv[]) { while(argc>1) { ++argv; printf(“%s\n”, *argv); --argc; } return 0; 在VC++环境下编译、连接后,“工程”—“设置”—“调试”—“程序变量”中输入“China Beijing”,再运行就可得到结果
194
8.8 动态内存分配与指向它的指针变量 8.8.1 什么是内存的动态分配 8.8.2 怎样建立内存的动态分配 8.8.3 void指针类型
195
8.8.1 什么是内存的动态分配 非静态的局部变量是分配在内存中的动态存储区的,这个存储区是一个称为栈的区域
C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据需要时随时开辟,不需要时随时释放。这些数据是临时存放在一个特别的自由存储区,称为堆区
196
8.8.2 怎样建立内存的动态分配 对内存的动态分配是通过系统提供的库函数来实现的,主要有malloc,calloc,free,realloc这4个函数。
197
8.8.2 怎样建立内存的动态分配 1.malloc函数 其函数原型为 malloc(100);
void *malloc(unsigned int size); 其作用是在内存的动态存储区中分配一个长度为size的连续空间 函数的值是所分配区域的第一个字节的地址 malloc(100); 开辟100字节的临时分配域,函数值为其第1个字节的地址
198
8.8.2 怎样建立内存的动态分配 2.calloc函数 其函数原型为
void *calloc(unsigned n,unsigned size); 其作用是在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组。
199
8.8.2 怎样建立内存的动态分配 p=calloc(50,4); 开辟50×4个字节的临时分配域,把起始地址赋给指针变量p
200
8.8.2 怎样建立内存的动态分配 3.free函数 其函数原型为 void free(void *p);
其作用是释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。p应是最近一次调用calloc或malloc函数时得到的函数返回值。
201
8.8.2 怎样建立内存的动态分配 4. realloc函数 其函数原型为
void *realloc(void *p,unsigned int size); 如果已经通过malloc函数或calloc函数获得了动态空间,想改变其大小,可以用recalloc函数重新分配。
202
8.8.2 怎样建立内存的动态分配 realloc(p,50); 将p所指向的已分配的动态空间改为50字节
203
8.8.2 怎样建立内存的动态分配 以上4个函数的声明在stdlib.h头文件中,在用到这些函数时应当用“#include <stdlib.h>”指令把stdlib.h头文件包含到程序文件中。
204
8.8.3 void指针类型 例8.30 建立动态数组,输入5个学生的成绩,另外用一个函放数检查其中有无低于60分的,输出不合格的成绩。
205
#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); return 0; }
206
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"); }
207
8.9有关指针的小结 1.首先要准确地弄清楚指针的含义。指针就是地址,凡是出现“指针”的地方,都可以用“地址”代替,
要区别指针和指针变量。指针就是地址本身,而指针变量是用来存放地址的变量。
208
8.9有关指针的小结 2. 什么叫“指向”?对于指针变量来说,把谁的地址存放在指针变量中,就说此指针变量指向谁。
注意:只有与指针变量的基类型相同的数据的地址才能存放在相应的指针变量中。
209
8.9有关指针的小结 void *指针是一种特殊的指针,不指向任何类型的数据,如果需要用此地址指向某类型的数据,应先对地址进行类型转换。
210
8.9有关指针的小结 3. 要深入掌握在对数组的操作中怎样正确地使用指针。一维数组名代表数组首元素的地址
211
8.9有关指针的小结 int *p,a[10]; p=a;
p是指向int类型的指针变量,p只能指向数组中的元素,而不是指向整个数组。在进行赋值时一定要先确定赋值号两侧的类型是否相同,是否允许赋值。 对“p=a;”,准确地说应该是:p指向a数组的首元素
212
8.9有关指针的小结 4.有关指针变量的定义形式的归纳比较,见主教材中表8.4。
213
8.9有关指针的小结 5.指针运算 指针变量加(减)一个整数
例如:p++,p--,p+i,p-i,p+=i,p-=i等均是指针变量加(减)一个整数。 将该指针变量的原值(是一个地址)和它指向的变量所占用的存储单元的字节数相加(减)。
214
8.9有关指针的小结 5.指针运算 (2)指针变量赋值 将一个变量地址赋给一个指针变量 不应把一个整数赋给指针变量
215
8.9有关指针的小结 5.指针运算 (3) 两个指针变量可以相减
如果两个指针变量都指向同一个数组中的元素,则两个指针变量值之差是两个指针之间的元素个数
216
8.9有关指针的小结 5.指针运算 (4) 两个指针变量比较 指向前面的元素的指针变量“小于”指向后面元素的指针变量
如果p1和p2不指向同一数组则比较无意义
217
8.9有关指针的小结 5.指针运算 (5) 指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示: p=NULL;
Similar presentations