我们应该只对数组使用指针算术运算,因为数组肯定分配在连续的内存块上,指针算术运算可以得到有效的偏移量。不过,不应该将它们用在结构体内,因为结构体的字段可能分配在不连续的内存区域。
下面这个结构体说明了这一点。为name
字段分配10字节,之后是一个整数。然而,整数是对齐到4字节边界的,所以两个字段之间会有空隙。这类空隙在6.1节的“结构体的内存如何分配”中解释过了。
typedef struct _employee {
char name[10];
int age;
} Employee;
下面的代码试图用指针来访问结构体的age
字段:
Employee employee;
// 初始化employee
char *ptr = employee.name;
ptr += sizeof(employee.name);
指针包含地址110,这是两个字段之间的2字节的地址,解引指针会把地址110处的4字节当做整数,如下图所示。
警告 误用对齐的指针可能会导致程序非正常终止或是取到错误数据。此外,如果编译器需要生成额外的机器码来弥补不恰当的对齐,那么指针访问也可能变慢。
即使结构体内的内存是连续的,用指针算术运算来访问结构体的字段也不是好做法。下面的结构体定义了由三个整数组成的Item
,通常会将三个整数字段分配在连续的内存位置,不过也不一定:
typedef struct _item {
int partNumber;
int quantity;
int binNumber;
}Item;
下面的代码片段声明了一个部件,然后用指针算术运算访问每个字段:
Item part = {12345, 35, 107};
int *pi = &part.partNumber;
printf("Part number: %d\n",*pi);
pi++;
printf("Quantity: %d\n",*pi);
pi++;
printf("Bin number: %d\n",*pi);
通常,输出就是我们所期望的那样,但也有例外。更好的办法是把每个字段赋给pi
:
int *pi = &part.partNumber;
printf("Part number: %d\n",*pi);
pi = &part.quantity;
printf("Quantity: %d\n",*pi);
pi = &part.binNumber;
printf("Bin number: %d\n",*pi);
更好的办法是根本不用指针,如下所示:
printf("Part number: %d\n",part.partNumber);
printf("Quantity: %d\n",part.quantity);
printf("Bin number: %d\n",part.binNumber);