当前位置: 首页 > news >正文

基础知识 - 结构体

1、结构体类型与结构体变量

1.1 结构体的定义

        结构体是一种自定义的数据类型,它把多个不同类型的变量封装在一起,形成一个新的复合数据类型。可以定义该结构体类型的变量,与使用 int 定义变量的方法相同

结构体是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量

如:标量,指针,数组,甚至是其他结构体

1.2 结构体的创建

        使用 struct 关键字创建结构体

例如,在描述一个学生信息时,我们可能需要姓名(字符串)、年龄(整数)、成绩(浮点数)等不同类型的数据,这时就可以定义一个结构体来整合这些信息:

struct Student 
{char name[50];  //姓名int age;  //年龄float grade;  //成绩
};  //分号不能丢

1.3 结构体变量声明与初始化

        定义结构体后,就可以声明该类型的变量。有多种方式可以声明结构体变量,并且可以在声明时进行初始化:

C 和 C++ 中结构体的区别:

C 环境下:struct + 标号名 才表示结构体类型(可以使用 typedef 起别名)

#include<stdio.h>//typedef - 起别名
//格式:typedef 类型名 别名typedef struct Student  //定义一个学生结构体,并给结构体起别名为Stu
{int num;char sex;
}Stu;int main()
{typedef int i;  //给int起别名为ii m = 7;Stu a;  //通过别名定义结构体类型变量return 0;
}

C++ 环境下:结构体名就可以表示结构体的类型

#include<iostream>
using namespace std;struct Student  //定义一个学生结构体
{int num;char sex;
};int main()
{Student a;  //通过结构体名定义结构体类型的变量return 0;
}

本文代码使用 C 语言格式

// 单个结构体变量并初始化
struct Student student1 = {"Alice", 20, 85.5};// 结构体数组并初始化
struct Student class[3] = 
{{"Bob", 21, 78.0},{"Charlie", 20, 90.0},{"David", 22, 88.5}
};

1.4 结构体的成员访问

  • 结构体变量,可以使用点运算符(.)来访问其成员

        例如,要输出student1的年龄,可以这样写:

printf("Student age: %d\n", student1.age);
  • 如果结构体变量是指针类型,则需要使用箭头运算符(->)来访问成员

        例如:我们有一个指向Student结构体的指针ptrStudent,要输出ptrStudent的名字,可以这样写:

struct Student *ptrStudent = &student1;
printf("Student name: %f\n", ptrStudent->name);

2、结构体字节对齐

2.1 对齐规则

  • 找成员当中最大的类型来作为对齐数,因此结果一定是它的整数倍
  • 要按照成员变量定义的顺序进行,不能自由组合分配空间
  • 按照整数倍地址对齐

2.2 带有位域的对齐规则

位域:

  • 成员:之后的数字
  • 表示的是所占 bit 的大小
  • 在内存要求苛刻的情况下可以使用位域。在不同系统(不同编译器也不同)不同效果(不要在可移植代码中使用)
  • 分析相邻的两个成员是否是同种类型,如果是同种类型,可以考虑放置在同一个单位下
  • 如果相邻的成员超出一个单位,那么就放两个单位里面,放置的时候不允许跨单位存储

2.3 带有指针的对齐规则

只与当前运行环境有关(默认 x86 运行环境)

  • x86 - 32bit - 4字节
  • x64 - 64bit - 8字节
typedef struct s1
{char a;   //1int b;    //4short c;  //2
}s1;typedef struct s2
{char a : 1;  //1  1bitint b : 5;   //4  5bitchar* c;     //4 (默认x86运行环境)short d;     //2
}s2;typedef struct s3
{s1* a;   //4 (默认x86运行环境)s2 b;    //4  16  (s2的对齐数为4字节,s2的总字节数为16)char c;  //1long d;  //4
}s3;int main()
{printf("%d,%d,%d", sizeof(s1), sizeof(s2), sizeof(s3));//输出:                12         16           28return 0;
}

2.4 常用数据类型的字节数 

数据类型字节数
int4
char1
long8
short2
float4
double8

 习题

1、求 sizeof(s) (     )

struct s
{

int x: 3;
int y: 4;
int z: 5;
double a;

}

A: 16

B: 32

C: 20

D: 24

解析:2*8=16

成员当中最大的类型为 double,所以以 8 字节作为对齐数。前3个是同种类型且没有超出一个单位,可以考虑放置在同一个单位下,故 2 * 8

2、在 32 位 cpu 上选择缺省对齐的情况下,有如下结构体定义则 sizeof(struct A) 的值为 (     )

struct A {
unsigned a : 19;
unsigned b : 11;
unsigned c : 4;
unsigned d : 29;
char index;

A: 9

B: 12

C: 16

D: 20

解析:4*4=16

成员当中最大的类型为 int,所以以 4 字节作为对齐数。前 4 个都是同种类型,可以考虑放置在同一个单位下,由于相邻成员超出一个单位就要放两个单位里面,故前 2 个放在一个单位里,第 3 个放在一个单位里,第 4 个放在一个单位里,故 4 * 4

3、结构体的自引用

3.1 结构体中直接包含同类型结构体变量不可行

struct Node
{int data;struct Node next;
};

        这种定义是错误的。原因在于,如果一个结构体里包含同类型的结构体变量,会陷入无限嵌套的情况。假设要计算 sizeof(struct Node),为了确定 struct Node 的大小,就得先知道 next 成员的大小,而 next 又是 struct Node 类型,它里面又有 next 成员,如此循环往复,结构体大小就会无穷大,这显然是不合理的,编译器也无法为其分配确定大小的内存空间

3.2 结构体中包含同类型结构体指针是可行的

struct Node
{int data;struct Node* next;
};

        这是正确的结构体自引用方式。因为指针在内存中占用固定大小的空间(在 32 位系统中通常是 4 字节,在 64 位系统中通常是 8 字节),不管 struct Node 具体是什么样子,struct Node* 类型的指针只是用来存储另一个 struct Node 结构体实例的地址,所以不会出现无限嵌套导致大小无法确定的问题

        在这种情况下,计算 sizeof(struct Node) 就很明确了。假设 int 类型占 4 字节,指针在 32 位系统占 4 字节,那么 sizeof(struct Node) 的结果就是 4 + 4 = 8 字节;在 64 位系统中,如果指针占 8 字节,sizeof(struct Node) 就是 4 + 8 = 12 字节 (不考虑字节对齐的情况下)

3.3 关于 typedef 对匿名结构体类型重命名时的错误

typedef struct
{int data;Node* next;
}Node;

        这里是错误的。typedef 的作用是给前面的匿名结构体类型取一个新名字 Node,但在匿名结构体内部却提前使用了 Node 这个还未定义完成的类型来创建 next 成员变量。在编译器处理到 Node* next; 这一行时,Node 还没有被定义,所以编译器无法识别 Node 类型,从而导致编译错误

3.4 正确的 typedef 重命名方式

typedef struct Node
{int data;struct Node* next;
}Node;

        这种方式是正确的。首先,定义了一个名为 struct Node 的结构体,在结构体内部使用 struct Node* 来引用自身类型的指针,这是合法的。然后,使用 typedef 把 struct Node 类型重命名为 Node,这样之后就可以直接使用 Node 来声明变量了,例如:Node node1;

综上所述,结构体自引用时要使用指针,在使用 typedef 重命名结构体类型时要注意类型定义和使用的先后顺序,避免出现编译错误。

相关文章:

  • 二分查找-LeetCode
  • Python内置函数---anext()
  • osu ai 论文笔记 DQN
  • LeetCode 第59题:螺旋矩阵Ⅱ
  • 【17】数据结构之图的遍历篇章
  • B端小程序如何突破常规,成为企业获客新利器?
  • 代码随想录算法训练营第二十天
  • 软件安装包-yum
  • .NET工作流框架ELSA Core:让你的应用程序更高效、更灵活
  • VS2022+QT环境配置及基本操作
  • 深入理解设计模式之模板方法模式 1d87ab8b42e98069b6c2c5a3d2710f9a
  • 学习海康VisionMaster之矩形检测
  • Flink 内部通信底层原理
  • CREATE TABLE ... AS SELECT
  • 如何有效防止服务器被攻击
  • 【MySQL】索引运算与NULL值问题详解:索引字段应尽量 NOT NULL ,NULL值不能参与部分索引运算
  • 【ESP32|音频】一文读懂WAV音频文件格式【详解】
  • HTTP 2.0 协议特性详解
  • Nginx Http配置整理
  • MQTT客户端核心架构解析:clients.h源码深度解读
  • 基金经理调仓引发大金融板块拉升?公募新规落地究竟利好哪些板块
  • 沪喀同心|为新疆青少年提供科普大餐,“小小博物家(喀什版)”启动
  • 财政部党组召开2025年巡视工作会议暨第一轮巡视动员部署会
  • 国务院关税税则委员会关于调整对原产于美国的进口商品加征关税措施的公告
  • 安徽省委副秘书长、省委政研室主任余三元调任省社科院院长
  • 人民日报钟声:通过平等对话协商解决分歧的重要一步