当前位置: 首页 > 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 重命名结构体类型时要注意类型定义和使用的先后顺序,避免出现编译错误。

http://www.xdnf.cn/news/4123.html

相关文章:

  • 首席人工智能官(Chief Artificial Intelligence Officer,CAIO)的详细解析
  • 从“链主”到“全链”:供应链数字化转型的底层逻辑
  • 智能sc一面
  • 【cocos creator 3.x】cocos creator2.x项目升级3.x项目改动点
  • 士兵乱斗(贪心)
  • 前端api(请求后端)简易template
  • Python高级爬虫之JS逆向+安卓逆向1.5节: 控制结构
  • docker harbor私有仓库登录报错
  • Ubuntu利用docker搭建Java相关环境问题记录
  • 如何有效防止服务器被攻击
  • 在激烈竞争下B端HMI设计怎样打造独特用户体验?
  • 数组理论基础
  • 从GPT到Gemini 大模型进化史
  • ADVB发送器设计
  • Matter如何终结智能家居生态割据,重构你的居住体验?
  • 随手笔记-python-opencv 读取图像的顺序 与pytorch处理图像的顺序
  • Mysql的安装
  • Java面试(2025)—— Spring
  • FPGA入门学习Day1——设计一个DDS信号发生器
  • opencv HSV的具体描述
  • 【Java学习笔记】关键字汇总
  • 赛灵思 XCVU440-2FLGA2892E XilinxFPGA Virtex UltraScale
  • ESP32- 开发笔记- 硬件设计-ESP32-C3 天线设计-利用嘉立创EDA来设计
  • 数码管LED显示屏矩阵驱动技术详解
  • Gitignore详解:版本控制中的文件忽略机制
  • 秒杀系统解决两个核心问题的思路方法总结:1.库存超卖问题;2.用户重复抢购问题。
  • Ubuntu 安装WPS Office
  • JavaScript 对象复制:浅拷贝与深拷贝
  • 观察者模式与发布订阅模式:解耦与通信的艺术
  • 【网络】IP层的重要知识