直接通过指针访问结构和他们的成员是非常简单的,但是他们应用与复杂的情形是就容易混淆。下面有个例子:
typedef struct
{
int a;
short b[2];
}Ex2;
typedef struct Ex
{
int a;
char b[3];
Ex2 c;
Ex *d;
}Ex;
类型Ex的结构可以用下面的图表示:
我们用图来表示上面的结构,可以使上面的例子看上去更清晰些。事实上,这张图并不完全正确,因为编译器只要有可能就会设法避免成员之间的空间浪费问题。
上面的例子用这个声明:
Ex x = { 10, "Hi" , { 5, { -1, 25 } }, 0 };
Ex *px = &x;
它们将产生下面这些变量
访问指针
让我们从指针变量开始,表达式px的右值是:
px是一个指针变量,但此处就不存在任何间接访问操作符,所以这个表达式的值就是px的内容。这个表达式的左值:
它显示了px的旧值将被一个左值取代。
现在考虑px+1.这个表达式并不是一个合法的左值,因为它的值并不储存于任何可标识的内存位置。这个表达式的右值更为有趣。如果px指向一个结构数组的元素,这个表达式将指向该数组的下一个结构。但就算如此,这个表达式任然是非法的,因为我们没办法分辨内存的下一个位置所存储的是这些元素之一还是其他东西。编译器无法检测到这类错误,所以必须你自己判断指针运算是否有意义。
访问结构
我们可以用*操作符对指针执行间接访问,表达式*px的右值是px所指向的整个结构。
间接访问操作随箭头访问结构,你可以把这个表达式赋值给另一个类型相同的结构,你也可以把它作为点操作符的左操作数,访问一个指定的成员。你也可以把它作为参数赋值给函数,也可以把它作为函数的返回值返回,(不过,关于最后两个操作,需要考虑效率问题)。表达式*px的左值是:
这里,结构将接受一个新值,或者更准确的说,它将接受它所有成员的新值。作为左值,重要的是位置,而不是这个位置所保存的值。
表达式*px+1是非法的,因为*px的结果是一个结构。C语言中并没有定义结构和整型值之间的加法运算.
但*(px+1)又如何?如果x是一个数组元素,这个表达式表示它后面的那个结构,但是,x是一个标量,所以这个表达式实际上是非法的。
访问结构成员
表达式px->a的右值是:
->操作符对px执行间接访问操作,它首先得到它指向数据的结构,然后访问成员a。当你有一个指向结构的指针但又不知道结构的名字时,可以使用表达式px->a,如果你知道结构的名字,你也可以用x.a。
在这里我们相互比较一下*px和px->a。在这两个表达式中,px所保存的地址都用于寻找它的结构。但结构的第一个成员是a,所以a的地址和结构的地址相同,这样px看上去是指向整个结构,同时指向结构的第一个成员,毕竟,他们具有相同的地址。但是,这个分析只有一半正确,尽管两个地址的值相同,但它们的类型不同。px被声明是指向一个结构的指针,所以*px是指向整个结构,而不是他的第一个成员。
比如我们创建一个指向整形的指针:
int *pi;
我们能不能让pi指向整型成员a?如果pi的值和px相同,那么表达式*pi的结果将是成员a。
但是,表达式
pi = px;
是非法的,因为它们的类型不相同,使用强制类型转换就能奏效:
pi = (int *)px;
但是这种方法是很危险的,因为它避开了编译器的类型检查。正确的表达式更为简单————使用&操作符取得一个指向px->a的指针:
pi = &pi->a;
让我们看一下&pi->a的图:
注意椭圆里的值是如何直接访问成员a的,这与px相反,后者指向整个结构。在上面的赋值操作之后,pi和px具有相同的值。但它们的类型是不同的,所以对它们进行间接访问操作所得的值也不同,*px是整个结构,*pi是一个单一的整型值。
下面我们看一个例子。px->b的值是一个指针常量,因为b是一个数组,这个表达式不是一个合法的左值。下面它的右值是:
如果对这个表达式执行间接访问操作,它将访问数组的第一个元素,使用下标引用或指针运算,我们还可以访问数组的其他元素,表达式px->b[1]访问数组第二个元素。
访问嵌套的结构
为了访问本身也是结构的成员c,我们可以使用px->c,它的左值是整个结构。
这个表达式可以用点操作法访问c的特定成员。列如,px->c.a具有以下的右值:
访问指针成员
表达式px->d它的右值是0,它的左值是它本身的内存位置。表达式*px->d这个是错误的。这里间接访问操作符作用于成员d所存储的指针值,但d包含了一个NULL指针,所以它 不指向任何东西,对一个空指针解引用操作是错误的。
表达式px->d->c.b[1]的右值如下图: