第十章 指 针

81
指指指指指指指 指指指指指指指 指指指指指指指指指 指指指指指指指指指 指指指指指指指指指指指 指指指指指指指指指指指 指指指指指指指指指指 指指指指指指指指指指 指指指指指指指指指 指指指指指指指指指 指指指指指指指指指指指指指指 指指指指指指指指指指指指指指 指指指 指指指

Upload: jerry-mays

Post on 13-Mar-2016

115 views

Category:

Documents


3 download

DESCRIPTION

第十章 指 针. 指针的基本概念 指向变量的指针变量 指向数组元素的指针变量 指向字符串的指针变量 指针变量做函数参数 指针数组和指向指针变量的指针. 10.1 地址和指针的概念. 地址和指针的概念. 首先回忆一下:内存单元的组织形式?. “ 位”和“字节”;“内存单元的内容”和“内存单元的地址”. “ 位”是最小的电子线路单元,每一个“位”可以保存一个二进制数,即 0 或 1 。 “字节”是由若干个“位”组成的,是基本的数据存储单元,即编译系统是以“字节”为基本单元为数据分配存储空间的。. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 第十章    指  针

指针的基本概念指针的基本概念 指向变量的指针变量指向变量的指针变量 指向数组元素的指针变量指向数组元素的指针变量 指向字符串的指针变量指向字符串的指针变量 指针变量做函数参数指针变量做函数参数 指针数组和指向指针变量的指针指针数组和指向指针变量的指针

第十章 指 针第十章 指 针

Page 2: 第十章    指  针

10.1 10.1 地址和指针的概念地址和指针的概念

Page 3: 第十章    指  针

地址和指针的概念地址和指针的概念首先回忆一下:内存单元的组织形式?首先回忆一下:内存单元的组织形式?““ 位”和“字节”;“内存单元的内容”和“内存单元的位”和“字节”;“内存单元的内容”和“内存单元的地址”地址” “ “ 位”是最小的电子线路单元,每一个“位”可以保存位”是最小的电子线路单元,每一个“位”可以保存一个二进制数,即一个二进制数,即 00 或或 11 。。 “ “ 字节”是由若干个“位”组成的,是基本的数据存储字节”是由若干个“位”组成的,是基本的数据存储单元,即编译系统是以“字节”为基本单元为数据分配存储单元,即编译系统是以“字节”为基本单元为数据分配存储空间的。空间的。 内存中每一个字节都有一个唯一的“编号”,这就是内内存中每一个字节都有一个唯一的“编号”,这就是内存单元的地址。存单元的地址。 字节单元中保存的数据,即是内存单元的内容。字节单元中保存的数据,即是内存单元的内容。

Page 4: 第十章    指  针

地址和指针的概念地址和指针的概念再回忆一下:数据在内存中是如何存储和如何读取的?再回忆一下:数据在内存中是如何存储和如何读取的?

程序定程序定义变量义变量int a;int a;编译系统为每一个编译系统为每一个定义的变量分配相定义的变量分配相应数目的字节单元应数目的字节单元

同时,编译系统把同时,编译系统把每个变量所占字节每个变量所占字节单元的地址信息和单元的地址信息和变量名对应起来变量名对应起来

程序中访程序中访问该变量问该变量a=234;a=234;编译系统首先根据编译系统首先根据变量名与地址的对变量名与地址的对应关系,找到变量应关系,找到变量aa 的地址的地址

然后,把常量然后,把常量 234234放到由该地址所指放到由该地址所指向的内存单元去向的内存单元去

1. 1. 定义变量,系统分配内存单元:定义变量,系统分配内存单元:

2. 2. 访问变量:首先确定变量的地址访问变量:首先确定变量的地址

Page 5: 第十章    指  针

地址和指针的概念地址和指针的概念这种利用变量名及其对应的地址信息(该信息由系统自动维这种利用变量名及其对应的地址信息(该信息由系统自动维护,无法操纵)存取变量值的方式称之为护,无法操纵)存取变量值的方式称之为“直接访问”“直接访问”方式。方式。我们也可以采取另一种称之为我们也可以采取另一种称之为“间接访问”“间接访问”的方式,即的方式,即把某把某个变量的地址保存在另一个变量里。个变量的地址保存在另一个变量里。

““ 直接访问”方式: 直接访问”方式: a = 234;a = 234;““ 间接访问”方式: 间接访问”方式: *p = 234;*p = 234;*p=234 ;*p=234 ;

首先取出变量 首先取出变量 pp的值(实际上就的值(实际上就是是 a a 的地址)的地址)然后把常量然后把常量 234234放到该值(地放到该值(地址)所指向的内址)所指向的内存单元去存单元去

程序定义普通变量 程序定义普通变量 int a ;int a ;程序定义指针变量 程序定义指针变量 int *p ;int *p ;

把变量把变量 a a 的地址保的地址保存到指针变量中存到指针变量中p = &a;p = &a;

Page 6: 第十章    指  针

地址和指针的概念地址和指针的概念指针就是内存单元的地址。指针就是内存单元的地址。专门用于保存地址的变量叫做专门用于保存地址的变量叫做指针变量。指针变量。指针变量的值就是地址。指针指针变量的值就是地址。指针变量指向该地址所指向的单元。变量指向该地址所指向的单元。

Page 7: 第十章    指  针

int a

010 23常量变量 int b int c

int* p指针变量 int* q&a

10

p 放的是 a 单元的地址 , 所以 *p==10

p 的内容是 &a: a 的地址 /a 的指针 , 所以 p 指向 a

地址常量 &a &b &c

a 单元的内容 a

a 单元的地址 (&a) 所指单元 (a) 的内容 *(&a)

指针变量的地址 &p &q

指针的指针变量 int** x int** y

&p**x 即 *(*x) 即 *p 即10

Page 8: 第十章    指  针

10.2 10.2 变量的指针和变量的指针和指向变量的指针变量指向变量的指针变量

Page 9: 第十章    指  针

变量的指针和指向变量的指针变量变量的指针和指向变量的指针变量 变量的指针就是变量的地址(编译系统分配给该变量的变量的指针就是变量的地址(编译系统分配给该变量的内存单元的地址)内存单元的地址) 用于保存变量的指针的变量就是指向变量的指针变量。用于保存变量的指针的变量就是指向变量的指针变量。 定义指针变量定义指针变量定义的一般形式: 定义的一般形式: 类型说明符 类型说明符 ** 指针变量名;指针变量名;

如: 如: intint *pa,*pa, *pb; *pb; floatfloat *pf1, *pf2; *pf1, *pf2; char *ch1, *ch2;char *ch1, *ch2;

说明:说明:① “① “ 类型说明符”表示该指针变量可以指向的变量的类型,类型说明符”表示该指针变量可以指向的变量的类型,即该指针变量可以而且只能保存这种类型的变量的地址;即该指针变量可以而且只能保存这种类型的变量的地址;② ② 一个“一个“ *”*” 号表示定义的变量是一个能够保存普通变量地号表示定义的变量是一个能够保存普通变量地址的指针变量,而不是一个保存具体数据的普通变量。址的指针变量,而不是一个保存具体数据的普通变量。

Page 10: 第十章    指  针

指针变量的赋值指针变量的赋值1. 1. 使用取地址运算符 使用取地址运算符 & & ,把地址值赋给指针变量,把地址值赋给指针变量比如:比如:指针变量只能保存地址。指针变量只能保存地址。可以用下面的三种方式给它赋值:可以用下面的三种方式给它赋值:

int *pa, *pb, a, b;int *pa, *pb, a, b;pa = &a;pa = &a; pb = &b; pb = &b;

2. 2. 把另一个指针变量的值赋给指针变量把另一个指针变量的值赋给指针变量比如:比如: int *pa, *pb, a, b;int *pa, *pb, a, b;

pa = &a; pa = &a; pb = pa;pb = pa; 3. 3. 给指针变量赋给指针变量赋 NULLNULL 值(值( NULLNULL 是在 是在 stdio.h stdio.h 头文件中头文件中定义的符号常量,该常量代表整数定义的符号常量,该常量代表整数 00 ),表示该指针变量本),表示该指针变量本身的值为身的值为 00 ,它不指向任何内存单元。,它不指向任何内存单元。比如:比如: int *pa, *pb, *pc;int *pa, *pb, *pc;

pa = NULL;pa = NULL; pb = 0; pc = ‘\0’;pb = 0; pc = ‘\0’;

Page 11: 第十章    指  针

&a&apapa aa

使一个指针变量指向同类型的变量使一个指针变量指向同类型的变量

方法:方法:只需把某个同类型变量的地址(指针)赋值给该指针只需把某个同类型变量的地址(指针)赋值给该指针变量即可。变量即可。然后,我们就可以说,该指针变量指向了某个普然后,我们就可以说,该指针变量指向了某个普通变量。通变量。比如:比如:

??定义指针变量后,怎样使它指向一个同类型的普通变量呢定义指针变量后,怎样使它指向一个同类型的普通变量呢

int *pa, *pb, a, b;int *pa, *pb, a, b;pa = &a; pb = &b; pa = &a; pb = &b;

&b&bpbpb bb

Page 12: 第十章    指  针

使一个指针变量指向同类型的变量使一个指针变量指向同类型的变量注意:注意:某个类型的指针变量只能保存同类型的变量的指针某个类型的指针变量只能保存同类型的变量的指针(即一个指针变量只能指向同类型的变量)(即一个指针变量只能指向同类型的变量)

int *pa, *pb, a, b;int *pa, *pb, a, b;float *p1, *p2, f1, f2;float *p1, *p2, f1, f2;pa = &a; pb = &b; /* pa = &a; pb = &b; /* 正确的赋值 正确的赋值 */*/

p1 = &a; p2 = &b; /* p1 = &a; p2 = &b; /* 错误的赋值 错误的赋值 */*/

p1 = a; p2 = 1234; /* p1 = a; p2 = 1234; /* 错误的赋值 错误的赋值 */*/

比如:比如:

Error: Cannot convert 'int *' to 'float *'Error: Cannot convert 'int *' to 'float *'

Error: Cannot convert 'int' to 'float *'Error: Cannot convert 'int' to 'float *'

Page 13: 第十章    指  针

两个有关指针的运算符两个有关指针的运算符① ① 取地址运算符:取地址运算符: & & ( 只能取变量或数组名,数组元素的地址)( 只能取变量或数组名,数组元素的地址)

比如:比如: int a, *pa; pa = &a;int a, *pa; pa = &a;则: 则: &a &a 为变量为变量 aa 的地址;等价于 的地址;等价于 papa *pa *pa 代表指针变量代表指针变量 papa 所指向的存储单元,等价于 所指向的存储单元,等价于 aa

② ② 指针运算符(间接访问运算符):指针运算符(间接访问运算符): * * (单目) (单目) 优先级:与取地址运算符优先级:与取地址运算符 && 同级同级 结合性:右结合性结合性:右结合性 用法: 用法: ** 指针变量名 指针变量名 (( 访问地址所指单元访问地址所指单元 (( 的内容的内容 ))))

&a&a papa aa *pa*pa

Page 14: 第十章    指  针

#include “stdio.h”#include “stdio.h”void main( )void main( ){ int a, b;{ int a, b; int *pa, *pb; int *pa, *pb; a = 100; b = 200;a = 100; b = 200; pa = &a; pb = &b; pa = &a; pb = &b; printf(“a=%d, b=%d\n”, a, b);printf(“a=%d, b=%d\n”, a, b); printf(“a=%d, b=%d\n”, *pa, *pb); printf(“a=%d, b=%d\n”, *pa, *pb); }}

通过指针变量访问变量:例通过指针变量访问变量:例 10-110-1

让让 papa 和和 pb pb 分别指向变分别指向变量量 aa 和和 bb使用变量名访使用变量名访问变量:问变量:输出输出变量变量 aa 和和 bb的值的值

使用变量的地址访问变量:使用变量的地址访问变量:输出输出 papa 和和 pbpb 指指向的内存单元的值。实际上就是变量向的内存单元的值。实际上就是变量 aa 和和 bb的值的值

运行结果: 运行结果: a=100, b=200a=100, b=200 a=100, b=200 a=100, b=200

Page 15: 第十章    指  针

有关指针变量的说明有关指针变量的说明① ① 一个指针变量可以指向任何一个同类型的普通变量;但是,一个指针变量可以指向任何一个同类型的普通变量;但是,在某一个时刻,它只能指向某一个同类型的变量在某一个时刻,它只能指向某一个同类型的变量

② ② 让指针变量指向某个同类型普通变量的方法是:把该普通让指针变量指向某个同类型普通变量的方法是:把该普通变量的地址保存到指针变量中。变量的地址保存到指针变量中。③ ③ 必须必须对指针变量进行了正确合法的初始化后对指针变量进行了正确合法的初始化后,才能使用该指针,才能使用该指针变量访问它所指向的内存单元。未指向内存单元的指针变量变量访问它所指向的内存单元。未指向内存单元的指针变量 ,, 不能用。不能用。

int *pa, *pb, a, b;int *pa, *pb, a, b;pa = &a; pb = &b; pa = &b; pb = &a;pa = &a; pb = &b; pa = &b; pb = &a;

int *pa, *pb, a, b;int *pa, *pb, a, b;pa=0x1000;*pa = 100;pa=0x1000;*pa = 100;*pb=200;*pb=200;或 或 printf(“*pa=%d\n”, *pa);printf(“*pa=%d\n”, *pa);

Page 16: 第十章    指  针

有关指针变量的说明有关指针变量的说明④ ④ 即使对指针变量进行了正确合法的初始化后,也只能用该即使对指针变量进行了正确合法的初始化后,也只能用该指针变量访问合法的允许访问的内存单元。不能使用该指针指针变量访问合法的允许访问的内存单元。不能使用该指针变量去随意访问其它不确定的内存单元,否则,结果是不可变量去随意访问其它不确定的内存单元,否则,结果是不可预料的。预料的。

int *pa, *pb, a, b;int *pa, *pb, a, b;

pa = &a; *pa = 100;pa = &a; *pa = 100;pb = &b; *pb = 200;pb = &b; *pb = 200;

printf(“value after a = %d\n”, printf(“value after a = %d\n”, *(pa + 1)*(pa + 1)););printf(“value after b = %d\n”, printf(“value after b = %d\n”, *(pb + 1)*(pb + 1)););

*(pa + 1) = 1000;*(pa + 1) = 1000;*(pb + 1) = 2000;*(pb + 1) = 2000;

正确安全使用指针变量正确安全使用指针变量错误使用指针变量错误使用指针变量

错误使用指针变量错误使用指针变量而且可能很危险而且可能很危险

Page 17: 第十章    指  针

有关运算符有关运算符 ** 和和 && 的说明的说明① ① 假设有 假设有 int a, *pa; pa = &a; int a, *pa; pa = &a; 则:则: &*pa &*pa 相当于 相当于 &a&a ,因为 ,因为 *pa *pa 就相当于 就相当于 aa *&a *&a 相当于 相当于 a a ,因为 ,因为 &a &a 就相当于 就相当于 papa ,, *pa *pa 相当于 相当于 aa (*pa) ++ (*pa) ++ 相当于 相当于 (a ++)(a ++)

100100200200

papa *(pa ++) *(pa ++) :得到:得到 100100 (未用(未用)),, papa 自增自增(*pa) ++ (*pa) ++ :: papa 所指向单元的值所指向单元的值 100100 加加 1,1, 得到得到 101101 ,而,而 papa 不变,不变,

② ② 注意 注意 (*pa) ++ (*pa) ++ 和 和 *pa ++ *pa ++ 的区别的区别由于 由于 ++ ++ 和 和 * * 为同一运算级别,则根据结合性, 为同一运算级别,则根据结合性, *pa ++ *pa ++ 相当于相当于 **(pa ++) (pa ++) ,与,与 (*pa) ++(*pa) ++ 是完全不同的。是完全不同的。*(pa ++)*(pa ++) :先得到:先得到 papa 当前指向的单元的值(未用)当前指向的单元的值(未用) ,, 再使再使 papa 自增自增(*pa) ++(*pa) ++ :是:是 papa 所指向的单元的值自增所指向的单元的值自增

Page 18: 第十章    指  针

指向变量的指针变量做函数参数指向变量的指针变量做函数参数??为什么要引入指针变量?指针变量的用途到底是什么?为什么要引入指针变量?指针变量的用途到底是什么?

指针变量的用途存储地址值,可以表示动态分配的构造型数据,处理指针变量的用途存储地址值,可以表示动态分配的构造型数据,处理字符串字符串 ,, 等等,其中一个用途是做函数的参数。等等,其中一个用途是做函数的参数。回忆一下:回忆一下:以前的函数,都是用普通变量做形参和实参。由于函数调用时以前的函数,都是用普通变量做形参和实参。由于函数调用时参数的传递过程是由“实参→形参”的“单向值传递”,函数参数的传递过程是由“实参→形参”的“单向值传递”,函数接收到的只是实参的值(准确的说,是实参的拷贝),所以在接收到的只是实参的值(准确的说,是实参的拷贝),所以在函数中我们无法改变实参的值。函数中我们无法改变实参的值。但是:但是:如果我们能把要修改的变量的地址传给函数,而函数又能接收如果我们能把要修改的变量的地址传给函数,而函数又能接收到这个地址,那么在函数中,我们就可以改变这个地址所指向到这个地址,那么在函数中,我们就可以改变这个地址所指向的单元,的单元,而这个单元实际上就是要修改的变量的单元,那么就而这个单元实际上就是要修改的变量的单元,那么就达到了修改该变量的目的。达到了修改该变量的目的。

Page 19: 第十章    指  针

#include “stdio.h”#include “stdio.h”void Swap(int *p1, int *p2)void Swap(int *p1, int *p2){{ int temp; int temp; temp = *p1; *p1 = *p2; *p2 = temp; temp = *p1; *p1 = *p2; *p2 = temp; }}void main( )void main( ){ int a, b; int *pa, *pb; { int a, b; int *pa, *pb; a = 100; b = 200; a = 100; b = 200; pa = &a; pb = &b; pa = &a; pb = &b; printf(“a=%d, b=%d\n”, *pa, *pb); printf(“a=%d, b=%d\n”, *pa, *pb); Swap(pa, pb);Swap(pa, pb); printf(“a=%d, b=%d\n”, a, b); printf(“a=%d, b=%d\n”, a, b); }}

指针变量做函数参数:例指针变量做函数参数:例 10-210-2

用指针变量作实参用指针变量作实参

用指针变量作形参用指针变量作形参交换两个形参指针变量交换两个形参指针变量所指向的内存单元的值所指向的内存单元的值

也可以直接用变量的地也可以直接用变量的地址作实参,即:址作实参,即:Swap(&a, &b);Swap(&a, &b);

Page 20: 第十章    指  针

指针变量做函数参数的执行过程指针变量做函数参数的执行过程&a&apapa aa

100100 &b&bpbpb

200200bb

&a&ap1p1

&b&bp2p2

调用函数之前:调用函数之前:

调用函数开始:调用函数开始:papa 和和 pbpb 分别分别单向单向值传递值传递给给 p1p1 和和 p2p2

调用函数返回:调用函数返回:p1p1 和和 p2p2 释放单元,释放单元,但但 aa 和和 bb 的值已经的值已经交换交换 &a&a

papa aa200200 &b&b

pbpb100100bb

然后交换然后交换 p1p1 和和 p2p2 所指向的内存单元所指向的内存单元 (( 实际上就是实际上就是 aa 和和 b)b) 的的值值

200200 100100

Page 21: 第十章    指  针

错误的错误的 SwapSwap 函数函数

#include “stdio.h”#include “stdio.h”void Swap(int, int);void Swap(int, int);void main( )void main( ){ int a =100, b = 200{ int a =100, b = 200 printf(“a=%d, b=%d\n”, a, b); printf(“a=%d, b=%d\n”, a, b); Swap(a, b);Swap(a, b); printf(“a=%d, b=%d\n”, a, b); printf(“a=%d, b=%d\n”, a, b); }}void Swap(int p1, int p2)void Swap(int p1, int p2){ int temp; { int temp; temp = p1; p1 = p2; p2 = temp; temp = p1; p1 = p2; p2 = temp; }}

用普通变量作实参用普通变量作实参

用普通变量作形参用普通变量作形参交换两个形参变量的值交换两个形参变量的值

考虑一下:如果不用指针变量做参数,仍然用普通变量做参数考虑一下:如果不用指针变量做参数,仍然用普通变量做参数实现实现 SwapSwap 函数,能否达到交换两个变量值的目的?函数,能否达到交换两个变量值的目的?

Page 22: 第十章    指  针

错误的错误的 SwapSwap 函数的执行过程函数的执行过程100100aa

200200bb

100100p1p1

200200p2p2

调用函数之前:调用函数之前:

调用函数开始:调用函数开始:aa 和和 bb 分别分别单向值单向值传递传递给给 p1p1 和和 p2p2

调用函数返回:调用函数返回:p1p1 和和 p2p2 释放单元,但释放单元,但 aa 和和bb 的值却没有变化,因为形的值却没有变化,因为形参不会把值传回给实参。参不会把值传回给实参。

aa100100 200200

bb

然后交换两个形参变量然后交换两个形参变量 p1p1 和和 p2p2 的值的值200200 100100

Page 23: 第十章    指  针

课堂练习课堂练习◆◆ 1. 1. 下若有语句 下若有语句 char *p1, *p2, *p3, *p4, chchar *p1, *p2, *p3, *p4, ch ;则不能;则不能正确赋值的语句是正确赋值的语句是 ( ) ( ) 。。(A) p1 = &ch; scanf(“%c”, p1);(A) p1 = &ch; scanf(“%c”, p1);(B) *p2 = getchar( );(B) *p2 = getchar( );(C) p3 = &ch; *p3 = getchar( );(C) p3 = &ch; *p3 = getchar( );(D) scanf(“%c”, p4);(D) scanf(“%c”, p4);

答案: 答案: (B) (D) (B) (D) ◆◆ 2. 2. 对于两个同类型的指针变量,它们之间不能进行的运对于两个同类型的指针变量,它们之间不能进行的运算是算是 ( ) ( ) 。。(A) +(A) + (B) -(B) - (C) = (C) = (D) ==(D) ==

答案: 答案: (A) (A)

Page 24: 第十章    指  针

课堂练习课堂练习#include “stdio.h”#include “stdio.h”void Swap(int*, int*);void Swap(int*, int*); void main( )void main( ){ int a =100, b = 200, *pa, *pb;{ int a =100, b = 200, *pa, *pb; pa = &a; pb = &b;pa = &a; pb = &b; printf(“a=%d, b=%d\n”, a, b); printf(“a=%d, b=%d\n”, a, b); Swap(pa, pb);Swap(pa, pb); printf(“a=%d, b=%d\n”, *pa, *pb); printf(“a=%d, b=%d\n”, *pa, *pb); }}void Swap(int *p1, int *p2)void Swap(int *p1, int *p2){ int *temp; { int *temp; temp = p1; p1 = p2; p2 = temp; temp = p1; p1 = p2; p2 = temp; }}

◆◆ 3. 3. 下面的下面的 SwapSwap 函数,能否达到交换两个变量值的目的?函数,能否达到交换两个变量值的目的?

答案:答案:不能。不能。因为该函数交因为该函数交换的是两个形换的是两个形参指针变量本参指针变量本身的值,而并身的值,而并没有交换它们没有交换它们所指向的变量所指向的变量的值。的值。变量的值,通过指针变量形参函数调用,有可能改变变量的值,通过指针变量形参函数调用,有可能改变

指针变量的值,通过指针变量形参函数调用,不可能改变指针变量的值,通过指针变量形参函数调用,不可能改变

Page 25: 第十章    指  针

10.3 10.3 数组和数组和指向数组指向数组元素元素的指针变量的指针变量

Page 26: 第十章    指  针

数组和指向数组和指向数组数组元素元素的指针的指针变量变量

◆ ◆ 数组的名字就是数组的首地址数组的名字就是数组的首地址 (( 即数组第一个元素的地址即数组第一个元素的地址 ))◆ ◆ 用于保存数组名字的变量就是指向数组元素的指针变量。用于保存数组名字的变量就是指向数组元素的指针变量。 指向数组元素的指针变量和指向变量的指针变量是同一种类型的指向数组元素的指针变量和指向变量的指针变量是同一种类型的 指针变量指针变量 ,, 指向数组元素的指针变量加指向数组元素的指针变量加 ,, 减数在一定范围内是安全减数在一定范围内是安全的的

◆ ◆ 数组的每一个元素都相当于一个同类型的变量,也都在内存中占数组的每一个元素都相当于一个同类型的变量,也都在内存中占 据存储单元,也都有相应的地址。据存储单元,也都有相应的地址。

◆ ◆ 引用数组元素既可以用数组名加下标法,也可以用指针法。引用数组元素既可以用数组名加下标法,也可以用指针法。 其本质都是使用指向数组元素的指针。其本质都是使用指向数组元素的指针。

◆ ◆ 既然可以定义指向变量的指针变量,当然也可以定义指向数组元既然可以定义指向变量的指针变量,当然也可以定义指向数组元 素的指针变量,用它保存的是数组中某一个元素的地址。 素的指针变量,用它保存的是数组中某一个元素的地址。

Page 27: 第十章    指  针

指向数组指向数组元素元素的的指针变量的定义指针变量的定义 指向数组元素的指针变量的定义指向数组元素的指针变量的定义定义方法: 定义方法: 类型说明符 类型说明符 ** 指针变量名;指针变量名;

可见,定义指向变量的指针变量和定义指向数组元素的指针可见,定义指向变量的指针变量和定义指向数组元素的指针变量的方法是一样的。变量的方法是一样的。

int *pa, *pb, a[10], b[10];int *pa, *pb, a[10], b[10];float *pf1, *pf2; float *pf1, *pf2; char *str1, *str2;char *str1, *str2;

Page 28: 第十章    指  针

使一个指针变量指向同类型的数组使一个指针变量指向同类型的数组元素元素方法:方法:只需把某个同类型的数组的首地址(指针)赋值给该只需把某个同类型的数组的首地址(指针)赋值给该指针变量即可。指针变量即可。然后,我们就可以说,这个指针变量指向了然后,我们就可以说,这个指针变量指向了该数组元素。注意:有的地方把它称为指向数组的指针变量该数组元素。注意:有的地方把它称为指向数组的指针变量其实,是数组元素的指针变量其实,是数组元素的指针变量

比如:比如:

?? 定义指针变量后,怎样使它指向一个同类型的数组元素呢定义指针变量后,怎样使它指向一个同类型的数组元素呢

int *pa, *pb, a[10], b[10];int *pa, *pb, a[10], b[10];pa = &a[0]; pb = &b[0];pa = &a[0]; pb = &b[0];/*/* 数组首地址就是数组第一个元素的地址数组首地址就是数组第一个元素的地址 */*/

或者:或者: int *pa, *pb, a[10], b[10];int *pa, *pb, a[10], b[10];pa = a; pb = b;pa = a; pb = b; /* /* 数组名代表首地址数组名代表首地址 */*/

pa=&apa=&a

Page 29: 第十章    指  针

通过指针变量引用数组元素通过指针变量引用数组元素 能够通过指针变量引用数组元素的前提:已经定义了一个能够通过指针变量引用数组元素的前提:已经定义了一个指针变量,并且已经给它赋值使它指向某个同类型的数组元指针变量,并且已经给它赋值使它指向某个同类型的数组元素。在有效的范围内对它加,减整数素。在有效的范围内对它加,减整数 , , 运算后它指向仍然是运算后它指向仍然是数组元素数组元素 访问方法: 指针法或下标法访问方法: 指针法或下标法如果指针变量如果指针变量 pp 的初值为的初值为 &a[0]&a[0] ,则:,则:① ① p+i p+i 或 或 a+ia+i 就是就是 a[i]a[i] 的地址,即它们指向的地址,即它们指向 aa 数组中下标为 数组中下标为 i i 的元素的元素 p + i ==&p + i ==&a[i]a[i] &a[i] == a + i &a[i] == a + i ② ② *(p+i) *(p+i) 或 或 *(a+i)*(a+i) 就是它们所指向的数组元素,即 就是它们所指向的数组元素,即 a[i]a[i] *(p + i) ==*(p + i) ==a[i]a[i] *(a + i) == a[i] *(a + i) == a[i]③ ③ 指针变量也可以使用下标法,如 指针变量也可以使用下标法,如 p[i] p[i] 和 和 *(p + i) *(p + i) 等价 等价

Page 30: 第十章    指  针

通过指针变量引用数组元素通过指针变量引用数组元素

……

a[0]a[0]a[1]a[1]

a[2]a[2]

a[9]a[9]

←←←←←←

←←

&a[0]&a[0]&a[1]&a[1]&a[2]&a[2]

&a[9]&a[9]

如有: 如有: int a[10], *p; p = a; int a[10], *p; p = a; 或 或 p = &a[0]; p = &a[0]; =a+0=a+0=a+1=a+1=a+2=a+2

=a+9=a+9

=p+0=p+0=p+1=p+1=p+2=p+2

=p+9=p+9

*(a+0)=*(a+0)=*(a+1)=*(a+1)=*(a+2)=*(a+2)=

*(a+9)=*(a+9)=

*(p+0)=*(p+0)=*(p+1)=*(p+1)=*(p+2)=*(p+2)=

*(p+9)=*(p+9)=

注意: 注意: 在对指向连续存储单元的指针变量进行加减运算在对指向连续存储单元的指针变量进行加减运算时,数字时,数字 11 并不代表加减一个字节,而是代表该指针变量并不代表加减一个字节,而是代表该指针变量所属数据类型的数据所占的字节单元的长度。如所属数据类型的数据所占的字节单元的长度。如 intint 型指针型指针变量加减变量加减 11 时,指针实际移动时,指针实际移动 44 个字节;个字节; float float 形指针变形指针变量加减量加减 11 时,指针实际移动时,指针实际移动 88 个字节;依次类推。个字节;依次类推。

Page 31: 第十章    指  针

通过指针变量引用数组元素:例通过指针变量引用数组元素:例 10-4-110-4-1◆ ◆ 编程从键盘接收数组中的全部元素并输出。编程从键盘接收数组中的全部元素并输出。/* /* 方法方法 11 :用数组名和下标 :用数组名和下标 */*/#include “stdio.h”#include “stdio.h”void main( )void main( ){ int a[10], n;{ int a[10], n; for(n = 0; n < 10; n ++)for(n = 0; n < 10; n ++) scanf(“%d”, scanf(“%d”, &a[n]&a[n]);); printf(“\n”); printf(“\n”); for(n = 0; n < 10; n ++)for(n = 0; n < 10; n ++) printf(“%d ”, printf(“%d ”, a[n]a[n]););}}

特点:特点:直观易懂。直观易懂。系统内部计算元系统内部计算元素地址。每访问素地址。每访问一个新的元素就一个新的元素就重新计算一次地重新计算一次地址。址。

Page 32: 第十章    指  针

通过指针变量引用数组元素:例通过指针变量引用数组元素:例 10-4-210-4-2◆ ◆ 编程从键盘接收数组中的全部元素并输出。编程从键盘接收数组中的全部元素并输出。

/* /* 方法方法 22 :用数组名+偏移量得到元素地址:用数组名+偏移量得到元素地址 */*/#include “stdio.h”#include “stdio.h”void main( )void main( ){ int a[10], n;{ int a[10], n; for(n = 0; n < 10; n ++)for(n = 0; n < 10; n ++) scanf(“%d”, scanf(“%d”, a + na + n);); printf(“\n”); printf(“\n”); for(n = 0; n < 10; n ++)for(n = 0; n < 10; n ++) printf(“%d ”, printf(“%d ”, *(a + n)*(a + n)););}}

特点:特点:利用数组名这个利用数组名这个指针常量加上一指针常量加上一个变化的地址偏个变化的地址偏移量来得到元素移量来得到元素地址。地址。每访问一个新的每访问一个新的元素就重新计算元素就重新计算一次地址。一次地址。

Page 33: 第十章    指  针

通过指针变量引用数组元素:例通过指针变量引用数组元素:例 10-4-310-4-3◆ ◆ 编程从键盘接收数组中的全部元素并输出。编程从键盘接收数组中的全部元素并输出。

/* /* 方法方法 33 :用指针变量得到元素地址:用指针变量得到元素地址 */*/#include “stdio.h”#include “stdio.h”void main( )void main( ){ int a[10], n; int { int a[10], n; int *pa = a;*pa = a; for(n = 0; n < 10; n ++)for(n = 0; n < 10; n ++) scanf(“%d”, scanf(“%d”, pa + npa + n);); printf(“\n”); printf(“\n”); for(n = 0; n < 10; n ++)for(n = 0; n < 10; n ++) printf(“%d ”, printf(“%d ”, *(pa + n)*(pa + n)););}}

特点:特点:利用一个指向数利用一个指向数组的指针变量加组的指针变量加上一个变化的地上一个变化的地址偏移量来得到址偏移量来得到元素地址。元素地址。指针变量本身的指针变量本身的值没有变化。值没有变化。

Page 34: 第十章    指  针

通过指针变量引用数组元素:例通过指针变量引用数组元素:例 10-4-410-4-4◆ ◆ 编程从键盘接收数组中的全部元素并输出。编程从键盘接收数组中的全部元素并输出。

/* /* 方法方法 44 :用指针变量得到元素地址:用指针变量得到元素地址 */*/#include “stdio.h”#include “stdio.h”void main( )void main( ){ int a[10], n; int { int a[10], n; int *pa = a;*pa = a; for(n = 0; n < 10; n ++)for(n = 0; n < 10; n ++) scanf(“%d”, scanf(“%d”, pa ++pa ++);); printf(“\n”); printf(“\n”); pa = a;pa = a; for(n = 0; n < 10; n ++)for(n = 0; n < 10; n ++) printf(“%d ”, printf(“%d ”, *(pa ++)*(pa ++)););}}

特点:特点:利用一个指向数组利用一个指向数组的指针变量来得到的指针变量来得到元素地址。元素地址。指针变量本身的值指针变量本身的值在变化。在变化。使用普通变量作循使用普通变量作循环控制变量。环控制变量。要时刻注意指针变要时刻注意指针变量的当前值。存在量的当前值。存在安全隐患安全隐患

Page 35: 第十章    指  针

通过指针变量引用数组元素:例通过指针变量引用数组元素:例 10-4-510-4-5◆ ◆ 编程从键盘接收数组中的全部元素并输出。编程从键盘接收数组中的全部元素并输出。

/* /* 方法方法 55 :用指针变量得到元素地址:用指针变量得到元素地址 */*/#include “stdio.h”#include “stdio.h”void main( )void main( ){ int a[10], n; int { int a[10], n; int *pa = a;*pa = a; for(n = 0; n < 10; n ++)for(n = 0; n < 10; n ++) scanf(“%d”, scanf(“%d”, pa ++pa ++);); printf(“\n”); printf(“\n”); for(pa = a; pa < a+10; )for(pa = a; pa < a+10; ) printf(“%d ”, printf(“%d ”, *(pa ++)*(pa ++)););}}

特点:特点:指针变量本身的指针变量本身的值在变化。值在变化。使用指针变量作使用指针变量作循环控制变量。循环控制变量。要时刻注意指针要时刻注意指针变量的当前值。变量的当前值。要正确确定循环要正确确定循环控制变量初值和控制变量初值和终值。终值。

Page 36: 第十章    指  针

通过指针变量访问数组通过指针变量访问数组元素元素时时的注意事项的注意事项注意:注意:在利用指针变量本身的值的改变来访问数组元素时,在利用指针变量本身的值的改变来访问数组元素时,要时刻注意指针变量的当前值。要时刻注意指针变量的当前值。

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

这一句不能少,这一句不能少,否则后面输出否则后面输出的结果就不对的结果就不对了。因为此时了。因为此时指针已经指向指针已经指向数组的有效范数组的有效范围之外去了。围之外去了。

Page 37: 第十章    指  针

指向数组指向数组元素元素的指针变量的有关运算的指针变量的有关运算如有: 如有: int a[10], *pa; pa = a; int a[10], *pa; pa = a; 或 或 pa = &a[0];pa = &a[0];① ① pa ++ pa ++ 或 或 pa += 1pa += 1 ,使 ,使 pa pa 指向下一个元素(即得到指向下一个元素(即得到下个元素的地址)下个元素的地址) pa -- pa -- 或 或 pa -= 1pa -= 1 ,使 ,使 pa pa 指向上一个元素(即得到上指向上一个元素(即得到上个元素的地址)个元素的地址)②② * pa ++ * pa ++ 等价于 等价于 * (pa ++) * (pa ++) ,即先得到 ,即先得到 pa pa 当前指当前指向的元素的值,然后再使 向的元素的值,然后再使 pa pa 自增,从而指向下一个元素自增,从而指向下一个元素③③ * ++ pa * ++ pa 等价于 等价于 * (++ pa) * (++ pa) ,即先使 ,即先使 pa pa 自增,然自增,然后得到 后得到 pa pa 当前指向的元素的值当前指向的元素的值④④ (* pa) ++ (* pa) ++ ,则是表示 ,则是表示 pa pa 当前指向的元素的值加当前指向的元素的值加 11

Page 38: 第十章    指  针

指向数组指向数组元素元素的指针变量做函数参数的指针变量做函数参数同指向变量的指针变量一样,指向数组元素的指针变量除了同指向变量的指针变量一样,指向数组元素的指针变量除了用于访问数组元素之外,用于访问数组元素之外,主要的用途仍然是用作函数的参数主要的用途仍然是用作函数的参数 这时:主调函数向被调函数传递的值是指针(即地址),这时:主调函数向被调函数传递的值是指针(即地址),因此可以在被调函数内访问(输出或改变)这些地址所指向因此可以在被调函数内访问(输出或改变)这些地址所指向的单元的内容。的单元的内容。 所谓用指针变量做函数参数,是指用可以保存和接收地址所谓用指针变量做函数参数,是指用可以保存和接收地址信息的数据来做函数参数,比如指针变量或数组的名字。因信息的数据来做函数参数,比如指针变量或数组的名字。因此有以下几种组合:此有以下几种组合:1. 1. 实参和形参都是数组名实参和形参都是数组名2. 2. 实参和形参都是指针变量实参和形参都是指针变量3. 3. 实参是数组名,形参是指针变量实参是数组名,形参是指针变量4. 4. 实参是指针变量,形参是数组名实参是指针变量,形参是数组名

Page 39: 第十章    指  针

现在用函数实现:现在用函数实现:把数组元素的输入和输出分别用两个函数来完成。这时就需把数组元素的输入和输出分别用两个函数来完成。这时就需要在主函数和用户自定义函数之间传递数组的首地址信息。要在主函数和用户自定义函数之间传递数组的首地址信息。而转递地址信息必须使用指针变量或数组名。而转递地址信息必须使用指针变量或数组名。

指向数组指向数组元素元素的指针变量做函数参数:例的指针变量做函数参数:例◆◆ 编程从键盘接收数组中的全部元素并输出。编程从键盘接收数组中的全部元素并输出。

Page 40: 第十章    指  针

指向数组指向数组元素元素的指针变量做函数参数:例的指针变量做函数参数:例#include “stdio.h”#include “stdio.h”void InputArray(int arr[ ], int n)void InputArray(int arr[ ], int n){ for(int i = 0; i < n; i ++){ for(int i = 0; i < n; i ++) scanf(“%d”, &arr[i]);scanf(“%d”, &arr[i]);}}void OutputArray(int arr[ ], int n)void OutputArray(int arr[ ], int n){ for(int i = 0; i < n; i ++){ for(int i = 0; i < n; i ++) printf(“%d ”, arr[i]);printf(“%d ”, arr[i]);}} void main( )void main( ){{ int a[10], n = 10; int *pa = a;int a[10], n = 10; int *pa = a; InputArray(a, n);InputArray(a, n); OutputArray(a, n);OutputArray(a, n);}} 数组名作实参数组名作实参

数组名作形参数组名作形参

Page 41: 第十章    指  针

指向数组指向数组元素元素的指针变量做函数参数:例的指针变量做函数参数:例#include “stdio.h” #include “stdio.h” void InputArray(int arr[ ], int n)void InputArray(int arr[ ], int n){ for(int i = 0; i < n; i ++){ for(int i = 0; i < n; i ++) scanf(“%d”, &arr[i]);scanf(“%d”, &arr[i]);}}void OutputArray(int arr[ ], int n)void OutputArray(int arr[ ], int n){ for(int i = 0; i < n; i ++){ for(int i = 0; i < n; i ++) printf(“%d ”, arr[i]);printf(“%d ”, arr[i]);}}void main( )void main( ){ int a[10], n = 10; int *pa = a;{ int a[10], n = 10; int *pa = a; InputArray(pa, n);InputArray(pa, n); OutputArray(pa, n);OutputArray(pa, n);}}

指针变量作实参指针变量作实参

数组名作形参数组名作形参

Page 42: 第十章    指  针

指向数组指向数组元素元素的指针变量做函数参数:例的指针变量做函数参数:例#include “stdio.h”#include “stdio.h”void InputArray(int *arr, int n)void InputArray(int *arr, int n){ for(int i = 0; i < n; i ++){ for(int i = 0; i < n; i ++) scanf(“%d”, arr + i);scanf(“%d”, arr + i);}}void OutputArray(int *arr, int n)void OutputArray(int *arr, int n){ for(int i = 0; i < n; i ++){ for(int i = 0; i < n; i ++) printf(“%d ”, *(arr + i));printf(“%d ”, *(arr + i));}}void main( )void main( ){ int a[10], n = 10; int *pa = a;{ int a[10], n = 10; int *pa = a; InputArray(a, n);InputArray(a, n); OutputArray(a, n);OutputArray(a, n);}} 数组名作实参数组名作实参

指针变量作形参指针变量作形参

Page 43: 第十章    指  针

指向数组指向数组元素元素的指针变量做函数参数:例的指针变量做函数参数:例#include “stdio.h”#include “stdio.h”void InputArray(int *arr, int n)void InputArray(int *arr, int n){ for(int i = 0; i < n; i ++){ for(int i = 0; i < n; i ++) scanf(“%d”, arr + i);scanf(“%d”, arr + i);}}void OutputArray(int *arr, int n)void OutputArray(int *arr, int n){ for(int i = 0; i < n; i ++){ for(int i = 0; i < n; i ++) printf(“%d ”, *(arr + i));printf(“%d ”, *(arr + i));} } void main( )void main( ){ int a[10], n = 10; int *pa = a;{ int a[10], n = 10; int *pa = a; InputArray(pa, n);InputArray(pa, n); OutputArray(pa, n);OutputArray(pa, n);}} 指针变量作实参指针变量作实参

指针变量作形参指针变量作形参

Page 44: 第十章    指  针

课堂练习课堂练习◆◆ 1. 1. 若有语句 若有语句 int a[ ]={1, 2, 3, 4, 5}, *pa, n;int a[ ]={1, 2, 3, 4, 5}, *pa, n;pa = a; 0≤n≤4pa = a; 0≤n≤4 ,则,则 ( )( ) 是对是对 aa 数组元素地址的正确数组元素地址的正确引用。引用。(A) a + n(A) a + n (B) a ++(B) a ++(C) & pa(C) & pa (D) &*pa(D) &*pa

答案: 答案: (A) (D) (A) (D) ◆◆ 2. 2. 若有语句 若有语句 int a[ ]={1, 2, 3, 4, 5}, *pa, n;int a[ ]={1, 2, 3, 4, 5}, *pa, n;pa = a; 0≤n≤4pa = a; 0≤n≤4 ,则,则 ( )( ) 是对是对 aa 数组元素的错误引用。数组元素的错误引用。(A) *(a + n)(A) *(a + n) (B) *(&a[n])(B) *(&a[n])(C) pa + n(C) pa + n (D) pa[n](D) pa[n] 答案: 答案: (C) (C)

Page 45: 第十章    指  针

课堂练习课堂练习◆◆ 3. 3. 写出下面程序执行的结果。写出下面程序执行的结果。#include “stdio.h”#include “stdio.h”void main( )void main( ){ int a[5] ={1, 2, 3, 4, 5}, n, *pa;{ int a[5] ={1, 2, 3, 4, 5}, n, *pa; for(pa = a; pa < a + 5; pa ++) for(pa = a; pa < a + 5; pa ++) printf(“%d ”, *pa); printf(“%d ”, *pa); *pa = a[0]; printf(“\n”); *pa = a[0]; printf(“\n”); for(n = 0; n < 5; n ++) for(n = 0; n < 5; n ++) *(a + n) = *(a + n + 1); *(a + n) = *(a + n + 1); for(n = 0; n < 5; n ++) for(n = 0; n < 5; n ++) printf(“%d ”, a[n]); printf(“%d ”, a[n]);}}

第一个打印循环结束时第一个打印循环结束时 papa 指向指向 a+5,a+5, 系统并没有分配该地址给系统并没有分配该地址给 aa 数数组组将将 a[0] a[0] 赋给该地址上的单元,冲掉了其他内存程序内容。这种破坏赋给该地址上的单元,冲掉了其他内存程序内容。这种破坏的影响,有后发性,要到运行那个程序时才看得出来的影响,有后发性,要到运行那个程序时才看得出来

运行结果:运行结果:1 2 3 4 51 2 3 4 522 3 4 5 13 4 5 1报应用程序出错报应用程序出错

Page 46: 第十章    指  针

10.4 10.4 字符串的指针和字符串的指针和指向字符串的指针变量指向字符串的指针变量

Page 47: 第十章    指  针

字符串的指针和指向字符串的指针变量字符串的指针和指向字符串的指针变量◆ ◆ CC 语言是用连续的字节单元来保存字符串的,字符串以指针表示。语言是用连续的字节单元来保存字符串的,字符串以指针表示。 如果字符数组的初始化值是字符串,字符串就保存在字符数组中如果字符数组的初始化值是字符串,字符串就保存在字符数组中 字符串指针和字符数组名是同一个东西。字符串指针和字符数组名是同一个东西。◆ ◆ 字符串的指针就是字符串中第一个字符所在内存单元的地址字符串的指针就是字符串中第一个字符所在内存单元的地址◆ ◆ 可以定义指向字符串的指针变量,如果给它赋值字符串,它保存可以定义指向字符串的指针变量,如果给它赋值字符串,它保存 的是字符串中第一个字符所在内存单元的地址。与字符数组名的是字符串中第一个字符所在内存单元的地址。与字符数组名 类似,也是指向连续的字节单元的首地址类似,也是指向连续的字节单元的首地址◆ ◆ 指向字符串的指针变量实际就是一个指向字符型变量的字符指针指向字符串的指针变量实际就是一个指向字符型变量的字符指针变量,它用来存放字符串的指针常量变量,它用来存放字符串的指针常量

Page 48: 第十章    指  针

指向字符串的指向字符串的字符字符指针变量的定义指针变量的定义 指向字符串的指针变量的定义 指向字符串的指针变量的定义 char *char * 指针变量名;指针变量名;

其中,其中, string1string1 和和 string2string2 、、 str1str1 和和 str2str2 都是字符指针类型的数据,都是字符指针类型的数据,都可以保存字符的地址信息,所不同的是:都可以保存字符的地址信息,所不同的是:string1string1 和和 string2string2 是字符指针常量是字符指针常量 ,, 它们表示的值无法改变它们表示的值无法改变 ,, 指向指向固定的内存单元固定的内存单元 ,, 可以通过改变数组元素值可以通过改变数组元素值 ,, 在不同时间放不同的字在不同时间放不同的字符串到这个固定的内存单元,这些不同的字符串有相同的地址指针常符串到这个固定的内存单元,这些不同的字符串有相同的地址指针常量值量值而而 str1str1 和和 str2str2 是字符指针变量是字符指针变量 ,, 它们存储的值可以改变它们存储的值可以改变 ,, 可以指向可以指向任何字符数组指针常量任何字符数组指针常量 ,, 字符串指针常量字符串指针常量 ,, 任何字符变量的地址任何字符变量的地址 .. 如如果不给它赋值果不给它赋值 ,, 它不指向任何地址它不指向任何地址 ,, 不指向任何内存单元。不指向任何内存单元。

char string1[80], string2[80];char string1[80], string2[80];char *str1, *str2;char *str1, *str2;

Page 49: 第十章    指  针

使一个字符指针变量指向字符串使一个字符指针变量指向字符串方法:方法:只需把某个字符串的指针赋值给该字符指针变量即可。只需把某个字符串的指针赋值给该字符指针变量即可。然后,我们就可以说,这个字符指针变量指向了该字符串。然后,我们就可以说,这个字符指针变量指向了该字符串。

1.1. 先定义字符数组保存字符串,然后再定义字符型指针变先定义字符数组保存字符串,然后再定义字符型指针变量指向该数组首元素;这时量指向该数组首元素;这时指针变量指向的内容可以改变指针变量指向的内容可以改变

??定义字符指针变量后,怎样使它指向一个字符串呢定义字符指针变量后,怎样使它指向一个字符串呢

2.2. 不定义字符数组,直接定义字符型指针变量,然后把字不定义字符数组,直接定义字符型指针变量,然后把字符串赋值给该指针变量。这时符串赋值给该指针变量。这时指针变量指向的内容不能改变指针变量指向的内容不能改变注意区分 :指针变量的内容,指针变量指向的内容注意区分 :指针变量的内容,指针变量指向的内容

上述操作有两种情况:上述操作有两种情况:

Page 50: 第十章    指  针

使一个指针变量指向字符串:第一种情况使一个指针变量指向字符串:第一种情况1.1. 先定义字符数组保存字符串,然后再定义字符型指针变量先定义字符数组保存字符串,然后再定义字符型指针变量指向该数组首元素,也就是指向该字符串;指向该数组首元素,也就是指向该字符串;#include “stdio.h”#include “stdio.h”void main( )void main( ){ char str[80] = “Hello, World”;{ char str[80] = “Hello, World”; char *pstr = str;char *pstr = str; /* /* 定义字符型指针变量 定义字符型指针变量 */*/ printf(“%s\n”, str); /* printf(“%s\n”, str); /* 用数组名访问用数组名访问 */*/ printf(“%s\n”, pstr); /* printf(“%s\n”, pstr); /* 用指针变量访问用指针变量访问 */*/ str[0]=‘A’; *pstr=‘a’;}str[0]=‘A’; *pstr=‘a’;}

strstr 是字符数组名,包含了字符数组的首元素地址信息,相当是字符数组名,包含了字符数组的首元素地址信息,相当于一个字符型数据的指针,所以可以把它赋值给一个字符型于一个字符型数据的指针,所以可以把它赋值给一个字符型的指针变量。然后就可以用该指针变量来访问字符串。的指针变量。然后就可以用该指针变量来访问字符串。

Page 51: 第十章    指  针

使一个指针变量指向字符串:第二种情况使一个指针变量指向字符串:第二种情况2.2. 不定义字符数组,直接定义字符型指针变量,然后把字符不定义字符数组,直接定义字符型指针变量,然后把字符串赋值给该指针变量。串赋值给该指针变量。#include “stdio.h”#include “stdio.h”void main( )void main( ){ /* { /* 定义字符型指针变量,同时用字符串常量初始化 定义字符型指针变量,同时用字符串常量初始化 */*/ /* /* 表示把字符串的指针保存在指针变量里 表示把字符串的指针保存在指针变量里 */ */ char char *pstr *pstr = “Hello, World”;= “Hello, World”; printf(“%s\n”, pstr);/* printf(“%s\n”, pstr);/* 用字符指针变量访问用字符指针变量访问 */*/ // *pstr=‘A’;// *pstr=‘A’;}}CC 语言把字符串常量处理成一个字符型指针常量,因此把一语言把字符串常量处理成一个字符型指针常量,因此把一个指针常量赋值给一个字符指针变量当然是正确的。个指针常量赋值给一个字符指针变量当然是正确的。字符字符串常量不能修改串常量不能修改

Page 52: 第十章    指  针

…… …… char char str[80];str[80]; str str = “Hello, World”;= “Hello, World”; …… ……

使一个字符指针变量指向字符串:第二种情况使一个字符指针变量指向字符串:第二种情况◆ ◆ 也可以先定义字符型指针变量,然后再把字符串常量赋值也可以先定义字符型指针变量,然后再把字符串常量赋值给它,同样正确。比如:给它,同样正确。比如: …… …… char char *pstr;*pstr; pstr pstr = “Hello, World”;= “Hello, World”; …… ……但是对字符数组却不能这样操作。比如:但是对字符数组却不能这样操作。比如:

为什么不对?为什么不对?

Page 53: 第十章    指  针

通过字符指针变量访问字符串通过字符指针变量访问字符串 能够通过字符指针变量访问字符串的前提是:已经定义了一能够通过字符指针变量访问字符串的前提是:已经定义了一个字符型指针变量,并且已经把某个字符串的指针赋值给它。个字符型指针变量,并且已经把某个字符串的指针赋值给它。 访问方法: 指针法或下标法访问方法: 指针法或下标法如果如果 pstrpstr 的初值为字符串的第一个字符的地址,比如有的初值为字符串的第一个字符的地址,比如有char *pstr = “Hello, World”; char *pstr = “Hello, World”; 则有以下事实:则有以下事实:① ① pstr + i pstr + i 就是第 就是第 i i 个字符的地址,即它指向字符串中的第 个字符的地址,即它指向字符串中的第 i i 个字符个字符② ② *(pstr + i) *(pstr + i) 或者 或者 pstr[i] pstr[i] 就是它所指向的字符就是它所指向的字符③ ③ 指针变量也可以使用下标法,即 指针变量也可以使用下标法,即 pstr[i] pstr[i] 和和 *(pstr + i) *(pstr + i) 是等价的是等价的*(pstr + i) *(pstr + i) 或者 或者 pstr[i]pstr[i] 都只能够读指向位置的字符,不都只能够读指向位置的字符,不能够向指向位置送值能够向指向位置送值

Page 54: 第十章    指  针

通过字符指针变量访问字符串通过字符指针变量访问字符串#include “stdio.h”#include “stdio.h”#include “string.h”#include “string.h”void main( )void main( ){ char *s1= “hello, world”;{ char *s1= “hello, world”; int n;int n; printf( “%s\n”, s1); printf( “%s\n”, s1); for(n = 0; s1 [n] != ‘\0’; n ++) for(n = 0; s1 [n] != ‘\0’; n ++) printf( “%c”, s1[n]); printf( “%c”, s1[n]); scanf(“%s”,s1);scanf(“%s”,s1); strcat(s1,”1234”); strcat(s1,”1234”); s1[0]=‘H’;s1[0]=‘H’;}}

Page 55: 第十章    指  针

通过字符指针变量引用字符数组元素:例通过字符指针变量引用字符数组元素:例 10-6-210-6-2◆ ◆ 编程把字符串编程把字符串 s2s2 复制到字符串复制到字符串 s1s1 中中

/* /* 用数组名+偏移量得到元素地址,访问元素用数组名+偏移量得到元素地址,访问元素 */*/#include “stdio.h”#include “stdio.h”void main( )void main( ){ char s1[80] = “”, s2[ ] = “hello, world”;{ char s1[80] = “”, s2[ ] = “hello, world”; int n;int n; for(n = 0; s2[n] != ‘\0’; n ++)for(n = 0; s2[n] != ‘\0’; n ++) *(s1 + n) = *(s2 + n);*(s1 + n) = *(s2 + n); *(s1 + n) = ‘\0’;*(s1 + n) = ‘\0’; printf( “s1=%s\ns2=%s”, s1, s2); printf( “s1=%s\ns2=%s”, s1, s2); }}

特点:特点:数组名本身不变数组名本身不变也不可能被改变也不可能被改变

Page 56: 第十章    指  针

通过字符指针变量引用字符数组元素:例通过字符指针变量引用字符数组元素:例 10-6-310-6-3◆ ◆ 编程把字符串编程把字符串 s2s2 复制到字符串复制到字符串 s1s1 中中

/* /* 用指针变量+偏移量得到元素地址,访问元素用指针变量+偏移量得到元素地址,访问元素 */*/#include “stdio.h”#include “stdio.h”void main( )void main( ){ char s1[80] = “”, s2[ ] = “hello, world”;{ char s1[80] = “”, s2[ ] = “hello, world”; int n; int n; char *p1 = s1, *p2 = s2;char *p1 = s1, *p2 = s2; for(n = 0; *(p2 + n) != ‘\0’; n ++)for(n = 0; *(p2 + n) != ‘\0’; n ++) *(p1 + n) = *(p2 + n);*(p1 + n) = *(p2 + n); *(p1 + n) = ‘\0’;*(p1 + n) = ‘\0’; printf( “s1=%s\ns2=%s”, s1, s2); printf( “s1=%s\ns2=%s”, s1, s2); }}

特点:特点:指针变量指针变量本身的值本身的值没有变化没有变化

Page 57: 第十章    指  针

通过字符指针变量访问字符串时的注意事项通过字符指针变量访问字符串时的注意事项注意:注意:在利用指针变量本身的值的改变来访问字符串时,在利用指针变量本身的值的改变来访问字符串时,要时刻注意指针变量的当前值。要时刻注意指针变量的当前值。

#include “stdio.h”#include “stdio.h”void main( )void main( ){ char s1[80] = “”, s2[ ] = “hello, world”;{ char s1[80] = “”, s2[ ] = “hello, world”; char *p1 = s1, *p2 = s2;char *p1 = s1, *p2 = s2; for( ; *p2 != ‘\0’; p1 ++, p2 ++)for( ; *p2 != ‘\0’; p1 ++, p2 ++) *p1 = *p2;*p1 = *p2; *p1 = ‘\0’;*p1 = ‘\0’; printf( “s1=%s\ns2=%s”, printf( “s1=%s\ns2=%s”, p1, p2p1, p2); ); }} 此处用此处用 p1p1 和和 p2p2 来输出字符来输出字符串,是得不到正确结果的。串,是得不到正确结果的。

Page 58: 第十章    指  针

指向字符串的字符指针变量做函数参数指向字符串的字符指针变量做函数参数同指向数组元素的指针变量一样,指向字符串的指针变量除了同指向数组元素的指针变量一样,指向字符串的指针变量除了用于访问字符串之外,用于访问字符串之外,主要的用途仍然是用作函数的参数。主要的用途仍然是用作函数的参数。 这时:主调函数向被调函数传递的值是字符串的指针(即这时:主调函数向被调函数传递的值是字符串的指针(即地址),因此可以在被调函数内访问(输出或改变)这些地址地址),因此可以在被调函数内访问(输出或改变)这些地址所指向的单元的内容(实际上就是这个字符串)。所指向的单元的内容(实际上就是这个字符串)。

需要强调的是:不管是用数组名做函数参数,还是需要强调的是:不管是用数组名做函数参数,还是用指针变量做函数参数,在进行参数传递时,仍然用指针变量做函数参数,在进行参数传递时,仍然是是由“实参→形参”的“单向值传递”。由“实参→形参”的“单向值传递”。 只不过 只不过与用普通变量做函数参数不同的是,用指针变量做与用普通变量做函数参数不同的是,用指针变量做函数参数时,传递的这个“值”不是普通的具体的函数参数时,传递的这个“值”不是普通的具体的数据,而是地址。数据,而是地址。

Page 59: 第十章    指  针

指向字符串的指针变量做函数参数:例指向字符串的指针变量做函数参数:例 10-710-7◆ ◆ 编写函数:编写函数:void StringCat(char *str1, char *str2)void StringCat(char *str1, char *str2)实现与系统库函数 实现与系统库函数 strcat strcat 同样的功能,把字符串同样的功能,把字符串 str2str2 复制复制到字符串到字符串 str1str1 中中分析:分析:

……

……00str1str1

……str2str2

字符串连接的实际过程:字符串连接的实际过程:

00

……

Page 60: 第十章    指  针

指向字符串的指针变量做函数参数:例指向字符串的指针变量做函数参数:例 10-710-7

/* StringCat/* StringCat :字符串连接 :字符串连接 */*//* str1/* str1 :目标字符串; :目标字符串; str2str2 :源字符串 :源字符串 */*/void StringCat(char *str1, char *str2)void StringCat(char *str1, char *str2){{ while (*str1 != ‘\0’ ) str1 ++;while (*str1 != ‘\0’ ) str1 ++; while (*str2 != ‘\0’ )while (*str2 != ‘\0’ ) {{ *str1 = *str2; *str1 = *str2; str1 ++; str2 ++; str1 ++; str2 ++; }} *str1 = ‘\0’ ;*str1 = ‘\0’ ;}}

实现代码:用指针变量作函数的形参实现代码:用指针变量作函数的形参

Page 61: 第十章    指  针

字符串,字符数组比较字符串,字符数组比较 指 针 值 指 针 值字符串 常指针 常值字符串 常指针 常值字符数组 常指针 变化值字符数组 常指针 变化值字符数组如果初始化值是字符串字符数组如果初始化值是字符串 ,, 二者的地址合而为一二者的地址合而为一 ,, 是数组地址是数组地址值值不能取字符串地址不能取字符串地址 ,, 但可以把字符串赋给字符指针变量看到其地址但可以把字符串赋给字符指针变量看到其地址 ,,可以取字符数组地址(可以取字符数组地址( && 数组名)。数组名)。由于字符串的常值性,它是能看不能改的。由于字符串的常值性,它是能看不能改的。#include"stdio.h"#include"stdio.h"void main()void main(){{ char *p; char *p; char *s="1234";char *s="1234";char a[10]="1234";char a[10]="1234";scanf("%s",p);scanf("%s",p);scanf("%s",s);scanf("%s",s);scanf("%s",a);scanf("%s",a);*(s+1)='x';*(s+1)='x'; *(a+1)='y';}*(a+1)='y';}

Page 62: 第十章    指  针

10.5 10.5 指向函数的指针变量指向函数的指针变量函数也有地址 , 可以定义指针变量存放函数的地址 .

当其用函数指针变量作为形参时 , 在一个函数中可以调用以函数地址实参传给的若干函数由于应用不多 , 简单举例说明如下

Page 63: 第十章    指  针

int max(int x,int y){return(x>y?x:y);}

int add(int x,int y){return(x+y);}

int ret(intx){return(x);}

float sub(float x,float y){return(x-y);}

int(*f)(int,int);// 定义函数指针变量 f, 由于函数有返回类型 , 形参表 , // 所以定义 f 时要带上 void main()

{ int a=10,b=2,c;

f=max; //f=add ; //f=ret; //f=sub;

c=f(a,b);

}

Page 64: 第十章    指  针

10.6 10.6 指针作为函数返回值指针作为函数返回值

Page 65: 第十章    指  针

函数的返回值是一个指针,是字符串处理,动态使用内存等工作经常遇到的情况。在 string.h 中声明的许多函数返回字符指针 ,如 char *strcat(char*,const char*);

动态分配的内存,是用地址值表示的,要带到函数外即要返回地址值。#include<windows.h>

int *dar (int num)

{ int * ar;

ar=(int *)malloc(sizeof(int)*num);

return ar;

}

malloc 向系统请求分配指定数量的内存单元 ,

若成功 , 返回分配单元首地址 是单纯地址信息 ,

没有该单元的结构信息 , 是 void 类型要把单元用于整数存储 , 要强制成 int *

Page 66: 第十章    指  针

函数定义时,将其返回值类型定义为某种数据类型的指针类型,在 return 语句中返回该种数据类型变量的地址值 数据类型 * 函数名(形参表)

{ 。。。return 数据类型变量地址值 ;}能够从函数中返回的地址值,可以是函数中动态申请的地址值,用 static 定义的局部静态变量的地址 , 形参中传入地址处理后的地址。不能是自动变量的地址。

如果编函数求 A 矩阵加 B 矩阵结果放入 C 矩阵,函数中 C 矩阵的存储结构要么同 A,B 一样,作为形参传入 , 要么定义为 static 。如果函数中定义 C 数组时未加 static ,返回 C ,编译连接不会错,可能运行也不会错。但某次内存紧张,回收的空间又用于别处,运行结果会出错。这就是安全隐患

Page 67: 第十章    指  针

10.7 10.7 指向指针变量的指针指向指针变量的指针和指针数组 和指针数组

Page 68: 第十章    指  针

简单地说指针即地址,任何变量都占有内存单元,都有地址。变量一但产生,占有内存的位置就固定了,所以任何变量的地址都是常量,如果要用内存单元在不同的时刻存放不同的地址常量值,这种内存单元称为指针变量,指针变量也有常量地址值,存放指针变量常量地址值的变量称为指针的指针变量。根据变量的地址访问变量内容称为间接访问,成功的间接访问必须是指针变量有所指。指针变量是存地址值的,间接访问往往是通过指针变量实现的。如果定义了一个指针变量,它没有赋以某一常量地址,即没有让它指向某一变量 , 通过指针变量去间接访问某一变量就要失败。如果把某一变量的地址存入一个指针变量,又把指针变量的地址存入一个指针的指针变量,对某一变量的内容的访问,要经过两次间接访问

Page 69: 第十章    指  针

通过指针的指针变量存储的指针变量地址间接访问指针变量,通过指针变量存储的变量地址间接访问变量。 int a=100,*p,**pp,b;

p=&a; pp=&p;

b=*p;b=**pp;

如果没有给 p,pp 以地址值就试图作 b=*p;b=**pp 是错误的。没有变量写不出直接访问的表达式,而指针变量没有指向变量,指针的指针变量没有指向指针变量,是可以写出间接访问的表达式的,一执行即访问不存在的内存单元,所以发生错误

Page 70: 第十章    指  针

前面已经介绍,希望通过函数调用修改外部变量值时,要用指针变量作为形参,外部变量地址作为实参。如果希望通过函数调用修改外部指针变量的值,则要用指针变量的指针作为形参,指针变量的地址作为实参。例 : 有一字符指针变量 , 希望通过函数调用 , 使它指向字符串 void f(char **x) { statica char *s1=“12345”; *x=s1;

} void main() { char *s; f(&s); }

Page 71: 第十章    指  针

指针数组 :用来存放指针变量值 ( 指针值 ) 的数组。一维指针数组 作为一维数组,其数组名是首元素的指针而元素存放的又是指针变量值,所以 指针数组名就是一个指针的指针。 定义方法: int *p[10]

由于 [] 优先级高于 * ,所以首先 [] 集合 p, 定义了一维数组,然后定义其元素类型是整型变量的指针,一个整型变量指针占 4 字节, 10 个元素连续分配了 40 个字节。有别于 int(*p)[10]:

它定义的是有 10 个整型变量的行的指针 , 只分配了 4 个字节。

Page 72: 第十章    指  针

字符串是指针 , 可以存放在字符指针变量中 , 如果有一组字符串 , 就要用字符指针数组存储了 .

char *p,*s[3]={“ 张衫衫” ,” 李示” ,” 王陈树声” };

p=“ 张衫衫” ;s[0]=“ 张衫衫” ;s[0]=“asdfghjkl”;

如果用动态分配产生 3 个有 10 个元素的行 , 这 3 个整型指针用整型指针数组存放 int *a[3],i;

for(i=0;i<3;i++)a[i]=(int *)malloc(sizeof(int)*10);

*(a[0])=100;a[0][1]=200;

动态分配时 , 一维整型指针数组名 , 可以当作二维整型数组名使用 ,要注意 a[i] 是静态分配的指针数组中的单元 , 和 a[i][0] 动态分配的一维数组中的单元 , 是不同的单元

Page 73: 第十章    指  针

简单地说指针即地址,深入地说指针比地址有更多的信息。指针除了可以表示地址外,还包含有地址上的变量的类型信息,构造信息。地址仅仅代表内存的位置,以字节为单位。而指针还表示了这个位置上的内存的划分方法,使用方法。在定义指针变量时这些信息就附带上了实型指针,整型指针 ; 变量指针,指针变量指针,行指针,等包含的信息切然不同。然而,指针的类型也是可以强制转换的,有意义的是通过类型转换,改变所指内存的构造信息。实型,整型的转换意义不大如有一维数组,希望当成二维数组访问,或者相反,就需要改变指针附带的内存的构造信息 int a[100], b[10][10], *p, (*pt)[10]; pt=(int(*)[10])a; p=(int*)b; pt[9][9]=123;p[99]=345;

printf(“%d %d”,a[99],b[9][9]); 123 345

Page 74: 第十章    指  针

数组和指针 (10.3.4)数组 ( 名 ) 表示连续分配的 N 个单元 , 用首单元的指针表示,一维 type 数组, 单元是 type 类型变量 , 其指针 type *

二维 type 数组, 单元是 行 , 其指针 type(*)[N]

一维 type 指针数组 , 单元是 type 类型指针变量 , 其指针 type **

所以简单地说: 一维数组是用变量指针表示 二维数组是用行指针表示一维指针数组是用指针的指针表示

Page 75: 第十章    指  针

行这种单元 , 是构造类型单元 , 是 N 个 type 类型变量的连续分配 ,

用 type 和 N 才表示得出行这种类型 . 然而没有独立的表示行类型( type [N] )的方法 , 只有在定义行类型变量时 type v [N]

才表示得出这种类型 . 行的指针类型 type (*)[N], 也不是独立的类型 , 也要在定义行指针变量时 type (*p)[10] 才表示得出来 .但是行类型 , 行指针类型 , 在有些时候 , 如形参数定义 , 强制类型时是可以独立使用的 int a[10],b[10][10],*c[10]; int *pa, (*pb)[10], **pc;

pa=a, pb=b, pc=c;

pa=(int *)b, pa=(int *)c;

pb=(int(*)[10])a, pb=(int(*)[10])c;

Page 76: 第十章    指  针

数组名间接访问 (* 数组名 ) 通过首单元的指针间接访问首单元一维 type 数组 , 首单元指针指向 type 类型变量 , 访问到变量值二维 type 数组 , 首单元指针指向行 , 访问到行 ,

行是用 type 类型变量指针表示的 , 所以访问到的是 type 类型变量的指针 .

一维 type 指针数组 , 首单元指针指向 type 类型指针变量 , 访问到 type 类型变量指针二维 type 数组 , 一维 type 指针数组都要进行两次间接访问才可以得到 type 类型变量值

int a[10]={1},b[10][10]={2},*c[10]={a}; int I;

I=*a,I=**b,I=**c; for(I=0;I<10;I++)c[I]=b[I];

c 是 int 指针数组 , 只能接受 int * 类型数据 , a, b[I] 是 int * 类型数据 b 不是 int * 类型数据 ,c[0]=b 会出现类型错误

I=**c *c 访问到 a, *a 访问到 1

Page 77: 第十章    指  针

数组名取地址 (& 名 ) 数组名表示连续分配的 N 个单元,取地址即对它表示的东西取地址 . 我们知道对变量取地址得到变量指针 .一维数组 , 相当于行 , 取地址得到行指针 ( 一维数组指针 ) type v[N] type (*vp)[N]二维数组 , 相当于矩阵 , 取地址得到矩阵指针 ( 二维数组指针 ) type v [M][N], type (*vp)[M][N]一维指针数组 , 相当于存放指针的行, 取地址得到存放指针的行指针 ( 一维指针数组指针 ) type* v[N] type *(*vp)[M]数组指针对本维数组没有意义 , 对高一维数组有很大的用处 .任何高维数组 , 都可以看作一维数组 , 其元素是低一维的数组。低一维的数组的指针 , 就是指向高维数组元素的指针例如 , 如行指针 , 就是指向二维数组元素”行”的指针int a[10]={1},b[10][10]={2},*c[10]={a};int (*ppa)[10],(*ppb)[10][10],*(*ppc)[10];ppa=&a,ppb=&b,ppc=&c;

Page 78: 第十章    指  针

int b[10][10] 中 b , b[0], &b, &b[0], &b[0][0] 五个量都表示连续分配的内存块的首地址,作地址值比较是等的 , 但是各自的特征是不同的,表示的指针是不同的 b 二维 int 数组名 int(*)[10] 行指针 b[0] 二维 int 数组行名 int * 变量指针 &b 二维 int 数组地址 int (*)[10][10] 矩阵指针 &b[0] 二维 int 数组行地址 int(*)[10] 行指针 &b[0][0] 二维 int 数组下标变量地址 int * 变量指针

Page 79: 第十章    指  针

指针变量与一维数组一维数组名表示首元素 ( 普通变量 ) 的地址 , 指针变量也表示普通变量的地址 , 除了一个是常量一个是变量外,表达的意义,使用的用法完全相同。在函数的形参与实参的角色中是可以互相变换的,在函数中对一维数组元素访问时下标法的直接访问与指针法的间接访问是可以互换的行指针变量与二维数组二维数组名表示首元素 ( 行 , 一维数组 ) 的地址 , 在这个地址上有一个行,行元素是普通变量,行地址是普通变量的地址,指针变量是否可以同二维数组名等效呢?不行,因为指针变量的附加信息只有行元素类型,没有行元素个数信息。行指针变量 则表示连续分配的 N 个普通变量 ( 行 )的地址。二维数组名同行指针变量等效。在函数的定义和调用中可以角色互换,访问时下标法,指针法可以混用

Page 80: 第十章    指  针

指针的指针变量与一维指针数组一维指针数组名表示首元素 ( 指针变量 ) 的地址。普通变量的一维数组 , 二维数组 , 首元素不管是单个还是一行,它们都是普通变量,而指针数组的首元素是指针变量,表示普通变量地址的指针变量或者行指针变量 ,都不能够表示指针变量的地址。因为它们不含存放的地址是指针变量地址的信息。而指针的指针变量,就有这个附加信息。一维指针数组名与指针的指针变量等效。在函数的定义和调用中可以角色互换,访问时下标法,指针法可以混用一维指针数组,可以用来存放静态 / 动态产生二维数组的行地址。

Page 81: 第十章    指  针

数组指针和函数参数各种数组直接对应的指针变量,以及他们作为函数参数的基本情况变量,数组, 指针 形式参数 实际参数int i, *pi; // f(int *) ; f(&i)

int a[100], *pa=a; // f(int[]),f(int *) ; f(a)

int b[10][10] , (*pb)[10]=b; // f(int[][10]),f(int(*)[10]) ; f(b),f(&b[0])

int *c[10] , **pc=c; // f(int *[]),f(int **) ; f(c)

这种基本情况也不是绝对的,只要正确进行数组指针的转换,任何一种数组,可以作为上面任何一种函数的参数