文档介绍:第17章灵活却难以理解——指针进阶
前面章节中,读者已经领会到了指针的强大功用,可实际上,指针所能做的不仅仅是向函数传递变量的地址,本章将讨论一些指针进阶的知识,套用一句曾经很流行的话:欢迎走进内存这片雷区。
指针与数组
在字符串处理一章中已经介绍过“数组名指针”的概念,可以将数组名作为指针参数传递给函数,本节将深入讲述数组名指针、数组元素的表示形式,指针数组等内容。
数组名指针
从字符串一章中,可归纳出以下结论:
数组名是一种常指针(不能修改),其值等于数组占据内存单元的首地址,但其类型取决于数组的维数。
对三维数组A而言,有下面关系成立:
A=&A[0];
A+1=&A[1];
…
A[0]=&A[0][0];
A[0]+1=&(A[1][0]);
…
A[0][0]=&A[0][0][0];
A[0][0]+1=&A[0][0][1];
…
使用数组名常指针表示数组元素
数组与指针关系密切,数组元素除了可以使用下标来访问,还可用指针形式表示。数组元素可以很方便地用数组名常指针来表示,以3维int型数组A举例,其中的元素A[i][j][k]可用下述形式表示:
(1)*(A[i][j]+k)
A[i][j]是int型指针,其值为&A[i][j][0],因此,A[i][j][k]可表述为*(A[i][j]+k)。
(2)*(*(A[i]+j)+k)
和第一种形式比较,不难发现A[i][j]= *(A[i]+j),A[i]是二级指针,其值为&A[i][0]。
(3)*(*(*(A+i)+j)+k)
将第2种形式的A[i]替换成了*(A+i),此处A是三级指针,其值为&A[0]。
此处以3维数组举例,还可进一步推广到更高维的情况。
指向数组元素的指针变量
使用一个指向数组元素的指针变量可以很方便地访问数组中的元素,不过,在元素定位时需要考虑多维数组在内存中的存储形式,看一段示例:
指向数组的指针变量
请体会本小节和上小节标题的不同,仍以3维数组为例,数组名是3级常指针,那能否声明一个变量是3级常指针呢,先来看示例:
指针数组
指针也可作为数组中的元素,将一个个指针用数组形式组织起来,就构成了指针数组。指针数组的一个重要应用是处理字符串,见示例:
指针、结构体和结构体数组
结构体变量占据一定内存大小,可声明一个结构体类型的指针,其值为结构体占据内存空间的首地址,而且,一个个的结构体变量可以组织治安一起构成结构体数组,本节讨论下如何使用指针访问结构体和结构体数组。
两种访问形式
完成结构体的定义后,结构体名可以看成是一种新的类型,通过结构体名可以声明指向结构体类型的指针,并可用某个结构体变量的地址为其赋值,举例来说:
struct person
{
char name[20];
int age;
char email[50];
}zangsan={"Zang San", 24, "zs@"}, *pzs=&zangsan;
则pzs是一个指向结构体类型的指针,pzs的值为结构体变量zangsan所占据内存的首地址,使用指针访问结构体成员有两种形式:
(*指针).成员
指针->成员
两种访问方式是等价的。
声明创建一个结构数组
数组占据的是一片连续的内存空间,而且,数组元素的类型是一致的,由此可知,创建一个结构数组是完全可行的,和C语言内置类型(如int、char等)数组一样,结构数组同样要先“声明”,后“使用”。
数组声明用于通知编译器为该数组开辟特定大小的内存,数组所占内存的大小取决于数组声明时指定的元素数目和元素类型。和普通的数组声明一样,结构数组声明的一般格式为:
结构类型名结构数组名[元素个数];
仍以上面定义的结构person为例,下述语句用于声明一个person类型的数组psz:
struct person psz[5];
上述语句告诉编译器:psz是一个数组,其中存储的元素为person型,大小为5,请为这个数组开辟所需要的内存。