结构、指针和成员

直接通过指针访问结构和他们的成员是非常简单的,但是他们应用与复杂的情形是就容易混淆。下面有个例子:

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]的右值如下图:

时间: 2024-10-08 22:04:05

结构、指针和成员的相关文章

C语言中结构体内部成员的对齐

说明: ******不同的编译器和处理器,其结构体内部的成员有不同的对齐方式. ******使用sizeof()运算符计算结构体的长度. ###结构体中每个成员相对于结构首地址的偏移量都是成员大小的整数倍,如果有需要编译器会在成员之间加上填充字. ###结构体的总大小是结构体最宽基本类型成员大小的整数倍.如果需要编译器会在最后一个成员之后加上填充字. struct A { <span style="white-space:pre"> </span>unsigne

结构体访问成员变量什么时候该用“-&gt;”或者是&quot;.&quot;呢?的困惑

煎蛋栗子: typedef struct Node{int data;struct Node *next;}LinkList; LinkList *p=(LinkList *)malloc(sizeof(LinkList)); 在这里,变量p是[LinkList *]类型的[指针变量]它的值是指向某一个[结点]的[地址] 而[(*p)]表示的则是[结点变量],它的值指向的是一个[结点]p是指针,(*p)是结点变量.我们要访问结构体内的成员data 通过指针变量访问:[p]->data通过结点变量

OpenCV 中结构体IplImage 成员width widthStep使用注意事项

OpenCV 中结构体IplImage 成员width,widthStep使用注意事项 width 是指的图片宽度是多少个像素,而这里widthStep是指的图片中的每一行占用多少个字节. 而且,widthStep会有字节对齐. 当需要对每个像素进行操作的时候,这里最好用widthStep做行递增变换. 比方说这里就是一个例子,明显,ptr_pixel_tmp是指向double类型的三通道图像,而ptr_pixel_img是unsigned char类型的三通道图像,double占八个字节. 于

结构体全局变量成员赋值

struct s { int a; }; s g; g.a = 1; // 编译错误 void f() { g.a = 1; // 编译正确 } 为什么全局结构体变量成员只能在函数内调用? 因为g.a=1是赋值语句,不是初始化语句,赋值语句只能运行的时候可以执行.

c++ 结构指针和双向链表

?结构指针 ?为结构指针动态分配内存 ?结构中的结构 ?双向链表 ?结构指针 struct mytime { //char name[256]; int hour;//时 int min; //分 int sec; //秒 }; struct stu_data { char name[256];//学生名字 struct mytime stuTime;//签到时间 } ; struct stu_data *stu; // int *pi; ?为结构动态分配内存 stu=malloc(sizeof

【C++】const、volatile不能修饰没有this指针的成员函数

一般所有的成员函数都只有一个复本,当不同的对象调用成员函数时,为了区分是哪个成员在调用,会传入this指针. 当调用有const.volatile修饰的成员函数时,会相应的传入一个const.volatile修饰的this指针. 故const.volatile不能修饰没有this指针的成员函数. 如:const.volatile不能修饰static成员函数

类 this指针 const成员函数

C++ Primer 第07章 类 7.1.2 ?Sales_data类的定义如下: #ifndef SALES_DATA_H #define SALES_DATA_H #include <string> #include <iostream> class Sales_data { public: std::string isbn() const {return bookNo;} Sales_data& combine(const Sales_data&); dou

delphi 记录类型-结构指针

unit Unit1; interface uses   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,   Dialogs, StdCtrls; type   TForm1 = class(TForm)     Button1: TButton;     procedure Button1Click(Sender: TObject);   private     { Private decla

基类与派生类的指针和成员函数调用原理

基类与派生类的指针和成员函数调用原理 1.如果以一个基础类指针指向一个衍生类对象(派生类对象),那么经由该指针只能访问基础类定义的函数(静态联翩) 2.如果以一个衍生类指针指向一个基础类对象,必须先做强制转型动作(explicit cast),这种做法很危险,也不符合生活习惯,在程序设计上也会给程序员带来困扰.(一般不会这么去定义) 3.如果基础类和衍生类定义了相同名称的成员函数(非虚函数),那么通过对象指针调用成员函数时,到底调用哪个函数要根据指针的类型(基类指针or派生类指针)来确定,而不是

typedef struct LNode命名结构指针(线性表的链式存储)

一.typedef 关键字 1. 简介: typedef工具是一个高级数据特性,利用typedef可以为某一些类型自定义名称. 2. 工作原理: 例如我们定义链表的存储结构时,需要定义结点的存储数据元素的类型,如定义一个 int 类型的ElemType,我们可以在定义前面加上关键字typedef即可: typedef int ElemType; 随后我们便可以用ElemType来定义上述数据元素的类型了: ElemType data; 二.对下述结构指针定义的理解 1 typedef int E