详解C语言中结构体的自引用和相互引用

结构体的自引用(self reference),就是在结构体内部,包含指向自身类型结构体的指针。
结构体的相互引用(mutual reference),就是说在多个结构体中,都包含指向其他结构体的指针。
1. 自引用 结构体
1.1 不使用typedef时
错误的方式:

struct tag_1{
  struct tag_1 A;  /* 结构体 */
  int value;
};

这种声明是错误的,因为这种声明实际上是一个无限循环,成员b是一个结构体,b的内部还会有成员是结构体,依次下去,无线循环。在分配内存的时候,由于无限嵌套,也无法确定这个结构体的长度,所以这种方式是非法的。
正确的方式: (使用指针):

struct tag_1{
  struct tag_1 *A; /* 指针 */
  int value;
};

由于指针的长度是确定的(在32位机器上指针长度为4),所以编译器能够确定该结构体的长度。
1.2 使用typedef 时
错误的方式:

typedef struct {
  int value;
  NODE *link; /* 虽然也使用指针,但这里的问题是:NODE尚未被定义 */
} NODE;

这里的目的是使用typedef为结构体创建一个别名NODEP。但是这里是错误的,因为类型名的作用域是从语句的结尾开始,而在结构体内部是不能使用的,因为还没定义。
正确的方式:有三种,差别不大,使用哪种都可以。

/* 方法一 */
typedef struct tag_1{
  int value;
  struct tag_1 *link;
} NODE; 

/* 方法二 */
struct tag_2;
typedef struct tag_2 NODE;
struct tag_2{
  int value;
  NODE *link;
}; 

/* 方法三 */
struct tag_3{
  int value;
  struct tag *link;
};
typedef struct tag_3 NODE;

2. 相互引用 结构体
错误的方式:

typedef struct tag_a{
  int value;
  B *bp; /* 类型B还没有被定义 */
} A; 

typedef struct tag_b{
  int value;
  A *ap;
} B; 

错误的原因和上面一样,这里类型B在定义之 前 就被使用。
正确的方式:(使用“不完全声明”)

/* 方法一  */
struct tag_a{
  struct tag_b *bp; /* 这里struct tag_b 还没有定义,但编译器可以接受 */
  int value;
};
struct tag_b{
  struct tag_a *ap;
  int value;
};
typedef struct tag_a A;
typedef struct tag_b B;  

/* 方法二  */
struct tag_a;  /* 使用结构体的不完整声明(incomplete declaration) */
struct tag_b;
typedef struct tag_a A;
typedef struct tag_b B;
struct tag_a{
  struct tag_b *bp; /* 这里struct tag_b 还没有定义,但编译器可以接受 */
  int value;
};
struct tag_b{
  struct tag_a *ap;
  int value;
};

3.实例:
应用结构体指针变量,打印结构体成员变量的信息。

#include <stdio.h>
struct Point
{
double x; /*x坐标*/
double y; /*y坐标*/
double z; /*z坐标*/
};
int main()
{
struct Point oPoint1={100,100,0};
struct Point oPoint2;
struct Point *pPoint; /*定义结构体指针变量*/
pPoint=& oPoint2;   /*结构体指针变量赋值*/
(*pPoint).x= oPoint1.x;
(*pPoint).y= oPoint1.y;
(*pPoint).z= oPoint1.z;
printf("oPoint2={%7.2f,%7.2f,%7.2f}",oPoint2.x, oPoint2.y, oPoint2.z);
return(0);
}

程序运行结果如下:

oPoint2={ 100.00,100.00,0.00}

其中表达式&oPoint2的作用是获得结构体变量oPoint2的地址。表达式pPoint=&oPoint2的作用是将oPoint2的地址存储在结构体指针变量pPoint中,因此pPoint存储了oPoint2的地址。*pPoint代表指针变量pPoint中的内容,因此*pPoint 和oPoint2等价。
通过结构体指针变量获得其结构体变量的成员变量的一般形式如下:
(*结构体指针变量). 成员变量
其中“结构体指针变量”为结构体指针变量,“成员变量”为结构体成员变量名称,“.”为取结构体成员变量的运算符。
另外C语言中引入了新的运算符“->”,通过结构体指针变量直接获得结构体变量的成员变量,一般形式如下:
结构体指针变量-> 成员变量
其中“结构体指针变量”为结构体指针变量,“成员变量”为结构体成员变量名称,“- >”为运算符。
因此,例中的部分代码

……
(*pPoint).x= oPoint1.x;
(*pPoint).y= oPoint1.y;
(*pPoint).z= oPoint1.z;
……

等价于

……
pPoint->x= oPoint1.x;
pPoint->y= oPoint1.y;
pPoint->z= oPoint1.z;
……
(0)

相关推荐

  • C语言中隐藏结构体的细节

    我们都知道,在C语言中,结构体中的字段都是可以访问的.或者说,在C++ 中,类和结构体的主要区别就是类中成员变量默认为private,而结构体中默认为public.结构体的这一个特性,导致结构体中封装的数据,实际上并没有封装,外界都可以访问结构体重的字段. C++中我们尚可用类来替代结构体,但是,C语言中是没有类的,只能用结构体,但很多时候,我们需要隐藏结构体的字段,不让外界直接访问,而是通过我们写的函数进行间接访问,这样就提高了程序的封装性. 实现方法,简单来说,就是,结构体定义时,要定义在.

  • C语言 结构体(Struct)详解及示例代码

    前面的教程中我们讲解了数组(Array),它是一组具有相同类型的数据的集合.但在实际的编程过程中,我们往往还需要一组类型不同的数据,例如对于学生信息登记表,姓名为字符串,学号为整数,年龄为整数,所在的学习小组为字符,成绩为小数,因为数据类型不同,显然不能用一个数组来存放. 在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据.结构体的定义形式为: struct 结构体名{     结构体所包含的变量或数组 }; 结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可

  • php读取二进制流(C语言结构体struct数据文件)的深入解析

    尽管php是用C语言开发的,不过令我不解的是php没有提供对结构体struct的直接支持.不过php提供了pack和unpack函数,用来进行二进制数据(binary data)和php内部数据的互转: 复制代码 代码如下: string pack ( string $format [, mixed $args [, mixed $...]] )   //Pack given arguments into binary string according to format.  array unp

  • 浅谈C语言中结构体的初始化

    <代码大全>建议在变量定义的时候进行初始化,但是很多人,特别是新人对结构体或者结构体数组定义是一般不会初始化,或者不知道怎么初始化.1.初始化 复制代码 代码如下: typedef struct _TEST_T {        int i;        char c[10];}TEST_T;TEST_T gst  = {1, "12345"};//可以初始化,设置i为1,s为一个字符串.TEST_T gst  = {1};//初始化个数少于实际个数时,只初始化前面的成员

  • C语言中的结构体的入门学习教程

    C语言中数组允许定义类型的变量,可容纳相同类型的多个数据项,但结构体在C语言编程中,它允许定义不同种类的数据项可供其他用户定义的数据类型. 结构是用来代表一个记录,假设要跟踪图书馆的书籍.可能要跟踪有关每本书以下属性: Title - 标题 Author - 作者 Subject - 科目 Book ID - 编号 定义结构体 定义一个结构体,必须使用结构体的struct语句.该struct语句定义了一个新的数据类型,程序不止一个成员.struct语句的格式是这样的: struct [struc

  • C语言中结构体struct编写的一些要点解析

    一.关于结构体的声明 1.匿名声明.如: struct { int i,j; }point; 说明: 这段代码的含义是,声明一个无名(anonymous)的结构体,并创建了一个结构体变量point.如果这段声明是放在全局域(在任意函数(比如main函数)外)内,那么point内的变量将被初始化为默认值,换句话说,以这种方式声明结构体变量时就已经为它分配了内存空间. 适用于该结构体只需要产生一个变量!本例中,该匿名结构体将有且仅有point这个结构体变量! 不同的匿名结构体变量,类型是不同的!如

  • C语言中结构体(struct)的几种初始化方法

    本文给大家总结的struct数据有3种初始化方法 1.顺序 2.C风格的乱序 3.C++风格的乱序 下面通过示例代码详细介绍这三种初始化方法. 1)顺序 这种方法很常见,在一般的介绍C的书中都有介绍.顺序初始化的特点是: 按照成员定义的顺序,从前到后逐个初始化:允许只初始化部分成员:在被初始化的成员之前,不能有未初始化的成员. 示例: struct User oneUser = {10, "Lucy", "/home/Lucy"}; 2)乱序(C风格) 顺序的缺陷是

  • 深入分析C语言中结构体指针的定义与引用详解

    指向结构体类型变量的使用首先让我们定义结构体:struct stu{char name[20];long number;float score[4];} ;再定义指向结构体类型变量的指针变量:struct stu *p1, *p2 ;定义指针变量p 1.p 2,分别指向结构体类型变量.引用形式为:指针变量→成员:[例7-2] 对指向结构体类型变量的正确使用.输入一个结构体类型变量的成员,并输出. 复制代码 代码如下: #include <stdlib.h> /*使用m a l l o c (

  • 详解C语言中结构体的自引用和相互引用

    结构体的自引用(self reference),就是在结构体内部,包含指向自身类型结构体的指针. 结构体的相互引用(mutual reference),就是说在多个结构体中,都包含指向其他结构体的指针. 1. 自引用 结构体 1.1 不使用typedef时 错误的方式: struct tag_1{ struct tag_1 A; /* 结构体 */ int value; }; 这种声明是错误的,因为这种声明实际上是一个无限循环,成员b是一个结构体,b的内部还会有成员是结构体,依次下去,无线循环.

  • 详解C语言中结构体的使用

    目录 结构体的声明 结构体成员的类型 结构体成员的访问 结构体的声明 结构体的定义:结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量. 举例: //定义结构体类型 struct tag//struct结构体关键字 tag结构体标签 struct tag结构体类型 { //成员变量 char name[20]; short age; char telphone[12]; char sex[5]; }s1,s2,s3;//s1,s2,s3是三个全局结构体变量 int m

  • 详解C语言中结构体(struct)的用法

    目录 粉丝问答 三种结构体类型变量说明 1. 先定义结构,再定义结构变量 2. 定义结构体类型的同时说明变量 3. 直接说明结构变量 结构体成员表示方法 结构体指针做参数 结构体初始化 定义变量的同时初始化 先定义在初始化 常用初始化 typedef与struct 前置声明 结构体对齐 粉丝问答 有个粉丝在群里问了这样一个问题,问题在图中已经标出,如下图. DQuestsion 头文件的结构体的定义为: typedef struct{ u8 bmRequestType; u8 bRequest;

  • 详解Go语言中结构体与JSON间的转换

    目录 前言 结构体转 JSON JSON 解析结构体 小结 前言 在日常开发中,我们往往会将 JSON 解析成对应的结构体,反之也会将结构体转成 JSON.接下来本文会通过 JSON 包的两个函数,来介绍 JSON 与结构体之间的转换. 结构体转 JSON Marshal(v any) ([]byte, error):将 v 转成 JSON 数据,以 []byte 的形式返回. import ( "encoding/json" "fmt" ) type User s

  • 详解C语言的结构体中成员变量偏移问题

    c语言中关于结构体的位置偏移原则简单,但经常忘记,做点笔记以是个记忆的好办法 原则有三个: a.结构体中的所有成员其首地址偏移量必须为器数据类型长度的整数被,其中第一个成员的首地址偏移量为0, 例如,若第二个成员类型为int,则其首地址偏移量必须为4的倍数,否则就要"首部填充":以此类推 b.结构体所占的总字节数即sizeof()函数返回的值必须是最大成员的长度的整数倍,否则要进行"末尾填充": c.若结构体A将结构体B作为其成员,则结构体B存储的首地址的偏移量必须

  • 详解Go语言中的数据类型及类型转换

    目录 1.基本数据类型 2.基础数据类型转换 3.基本数据类型转为字符串 4.strconv的使用 5.字符串转为基础类型 1.基本数据类型 数据类型有很多,先研究一下基础的,例如:布尔型.数字类型.字符串类型. 数字类型有uint8.uint16.uint32.uint64.int8.int16.int32.int64(uint和int区别在于uint为无符号整数,即只支持正数,不支持负数形式) 数字浮点型有fload32.float64.complex64.complex126(后面两个均为

  • 详解Go语言中配置文件使用与日志配置

    目录 项目结构调整 配置文件使用 日志配置 小结 接着上一篇的文章构建的项目:Go语学习笔记 - 环境安装.接口测试 只是简单的把GET和POST接口的使用测试了一下. 我还是想按照正常的项目结构调整一下,这篇笔记主要是三个部分:调整项目目录结构.增加配置文件使用.增加日志配置,很常规而且也是每个项目都需要用到的. 项目地址:github地址 项目结构调整 说先对项目目录结构调整一下,按照我自己的开发习惯,增加了几个目录. 项目结构如下图: 解释一下目录结构 app/constants:主要放置

  • 详解C语言中二级指针与链表的应用

    目录 前言 二级指针讲解 链表的应用 定义双链表的结构体 创建双链表 前言 这篇文章即将解决你看不懂或者不会写链表的基本操作的问题,对于初学者而言,有很多地方肯定是费解的.比如函数的参数列表的多样化,动态分配内存空间函数malloc等,其实这些知识和指针联系紧密,尤其是二级指针.那么开始好好的学习这篇文章吧! 二级指针讲解 简述:其实就是一个指针指向另一个指针的地址. 我们都知道指针指向地址,但是指针自身也是一个变量,当然也可以被二级指针所指向. 语法:形如 int x = 10; int *q

  • 详解C语言中动态内存管理及柔性数组的使用

    目录 一.malloc 二.free 三.calloc 四.realloc 1.realloc在扩容时的情况 2.realloc也能实现malloc功能 五.使用动态内存的常见错误 1.free空指针 2.对动态开辟的空间越界访问 3.对非动态开辟内容free 4.只free动态开辟空间的一部分 5.对同一块内存多次free 6.动态内存空间忘记释放(内存泄漏) 六.柔性数组 1.柔性数组的概念 2.柔性数组的特点 3.柔性数组的使用场景 4.柔性数组的优点 一.malloc 这个函数向堆区申请

随机推荐