4. 记录的指针
大多数情况下,存储结构化数据的变量通常被定义为记录的指针,而不是记录本身。
指向记录的指针通常比记录小且较容易操作。
更重要的是,当把记录的指针传递给某个过程时,过程可以改变记录的内容。
即,传递记录的指针可以对记录中的字段进行操作。
例如,employeeT的定义如下:
typedef struct{
string name;
string title;
string ssnum;
double salary;
int withholding;
} employeeT;
有了这个定义,就可以声明一个employeeT类型的指针变量,代码如下:
employeeT *empptr;
进行声明后,仅仅为指针提供了空间。在使用指针empptr前,仍然需要为记录中的字段提供完整的空间。
4.1 定义一个指向记录类型的指针
可以采用另外一种声明策略。
如果希望通过地址访问记录类型的值,可以将employeeT定义为一个指针而不是一个记录。
如下所示:
typedef struct{
string name;
string title;
string ssnum;
double salary;
int withholding;
} *employeeT;
这个定义表明employeeT类型为指向所说明记录的指针。
在建立了这个新类型定义之后,就可以用与前面类似的方法来声明employeeT的对象。声明如下:
employeeT emp;
emp又一次被定义为employeeT类型的变量。而区别在于目前它是一个指针类型,而不再是一个记录类型。
对于employeeT类型的定义,有两种选择:或者定义为记录,或者定义为指向记录的指针。
当从两种选择中做出决定时,需要考虑下面几个方面的因素:
-
记录类型的大小。
当把记录传递给函数时,或者当给记录变量赋值时,需复制记录中所有字段的内容。对于较小的记录来讲,复制的时间不会明显超过整型值或其他基本类型值所需的时间。如果记录很大,则进行复制需要花费很多的时间。而使用指向记录的指针时,就可以避免进行复制。因此,记录越大,使用指向记录的指针的优势就越明显。 -
内存分配的消耗。
虽然使用记录指针会减少数据复制的需求,但使用指针时要仔细考虑如何为数据分配内存空间。内存分配的过程会使程序的逻辑更加复杂。 -
对类型进行操作的函数有何规定。
当我们定义了一个新的数据类型后,我们往往需要定义能够对该类型值进行操作的函数。如果将记录作为参数传递给一个函数,该函数无法改变记录的内容。而如果传递的是一个指针,则函数可以直接访问记录中的数据。如果需要通过函数对记录中的内容作出修改,则必须选择指针类型;如果不希望通过函数更改记录内容,则需要定义记录类型以确保记录的安全,因为在这种情况下函数不可能对传递的参数做出修改。
在employeeT类型的定义中,最好将其定义为指针类型,因为记录的内容比较复杂。
4.2 为记录数据分配空间
当定义一个新类型时,可以选择定义为记录类型,也可以选择定义为指向记录的指针类型。
但是选择任何一个类型都会影响到如何使用定义的变量。如果按照下述方式定义employeeT类型:
typedef struct{
string name;
string title;
string ssnum;
double salary;
int withholding;
} *employeeT;
并通过下面的语句声明该类型的变量:
employeeT emp;
编译器只为指针预留空间,而不为实际的记录分配空间。在使用变量emp前,必须确保该指针确实指向内存中的一个有效地址。
为记录分配空间的最简单的方法是利用前文介绍的动态分配方法。可以用sizeof操作符决定存储员工记录需要多少空间。常用的方法是:
emp = (employeeT) GetBlock(sizeof *emp);
这条语句利用GetBlock为emp指向的类型请求足够的空间。
基于更简单方式的考虑,genlib.h接口定义了函数New,该函数以指针类型名作为参数,并返回一个指针指向足够大的空间来存储可能的数值。
因此,如果有下列语句:
emp = New(employeeT);
则系统会从堆中为一个员工记录分配空间,并返回一个指向该记录的指针。
这一操作的结果可以通过下面的描述来说明。
在执行对New的调用前,emp中存储了一个指针,但却并未进行初始化。
因而变量emp与任何记录的存储都没有关联,可以用一个空的方框表示(如下图所示):
赋值语句
emp = New(employeeT);
为记录分配了空间,并将指针的地址赋给emp,这一过程可以用下面的图表示:
对记录指针进行操作
当使用记录指针时,应该怎样访问某个字段呢?
对于下面的声明
employeeT emp;
(*emp).salary
可以达到希望的效果,但在应用中则显得太过笨拙。
指向结构的指针随时都在使用,强制用在每次选取时都使用括号会使记录的使用变得很麻烦。
基于此种考虑,C语言中定义了运算符“->”将选择和间接引用结合成一个运算符。
因此,常用的访问员工薪水的方法为:
emp -> salary
参考
《C语言的科学和艺术》 —— 16 记录