初识结构体

结构体是一种用户自定义的数据类型,可以用来存储不同类型的数据项。

结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如:
标量、数组、指针,甚至是一些其他的结构体

结构体的声明和定义

将一些不同类型组合起来描述自定义的类型,例如:

struct stu
{   
	char name[20];
	int age;
	char id[20];     // 这三者均为结构体的成员变量
}s1;    // s1指结构体变量(第一种定义方式)

struct stu s2;  // s2指结构体变量 (第二种定义方式)

 

初始化 -- 三种方式

struct stu
{   
	char name[20];
	int age;
	char id[20];
}s1,s2,s3;

struct stu s1 = { "张三",20,2024001 };  // 第一种,按顺序声明
struct stu s2 = { .age = 21,.id = 2024002,.name = "李四" };  // 第二种,指定顺序初始化



// 定义内部结构体
struct Date {
    int day;
    int month;
    int year;
};

// 定义外部结构体,内部包含一个 Date 结构体作为成员
struct Employee {
    char name[20];
    int age;
    struct Date TheDate;
};

// 初始化外部结构体变量
struct Employee emp1 = { "ice", 30, {1, 1, 2024} };  // 第三种,结构体嵌套初始化

访问--两种方式

. 运算符用于访问结构体变量的成员,该结构体变量通过结构体名直接访问。而 -> 运算符用于访问结构体指针所指向的结构体变量的成员。  例子:

#include <stdio.h>

// 定义结构体
struct Point {
    int x;
    int y;
};

int main() {
    // 使用 . 访问结构体成员
    struct Point p1;
    p1.x = 10;
    p1.y = 20;
    printf("Using . : x = %d, y = %d\n", p1.x, p1.y);
    
    // 使用指针和 -> 访问结构体成员
    struct Point p2;
    struct Point *ptr = &p2;    // 指针ptr指向结构体变量p2,可以通过指针ptr来访问和操作结构体变 
                                // 量p2 的成员
    ptr->x = 30;
    ptr->y = 40;
    printf("Using ->: x = %d, y = %d\n", ptr->x, ptr->y);

    return 0;
}

自引用和匿名结构体

匿名结构体是在定义结构体变量的同时省略结构体名,只使用结构体内部的成员来创建结构体变量的一种方式。匿名结构体通常用于临时场景或者简单的数据结构中。

struct Node
{
 int data;
 struct Node *next;   // 不可以struct Node next ,如果这样结构体变量的⼤
                      // ⼩就会⽆穷的⼤,是不合理的。
};

结构体传参

结构体传参的时候,要传结构体的地址。
原因:
1.函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
2.如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下降。

结构体内存对齐

规则:

1. 结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处
2. 其他成员变量要对⻬到某个数字(对齐数)的整数倍的地址处。
对⻬数 = 编译器默认的⼀个对齐数 与 该成员变量⼤⼩的较⼩值。
- VS 中默认的值为 8
- Linux中 gcc 没有默认对齐数,对齐数就是成员自身的大小
3. 结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的
整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构
体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍

相关图示

那为什么有内存对齐?

为了保证数据在内存中以高效、可预测的方式存储和访问,从而提高程序的性能和可靠性,拿空间来换取时间的做法

在对齐方式不合适的时候,我们可以自己更改默认对齐数
#pragma pack(1)    //设置默认对齐数为1
#pragma pack()     //取消设置的对齐数,还原为默认

结构体位段

什么是位段?
位段的声明和结构是类似的,有两个不同:
1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以
选择其他类型。
2. 位段的成员名后边有⼀个冒号和⼀个数字。


位段内存分配

1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

位段跨平台问题(了解):​​​​​​​

1. int 位段被当成有符号数还是
⽆符号数是不确定的。
2. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会
出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃
剩余的位还是利⽤,这是不确定的。

位段应用(了解):​​​​​​​

嵌入式系统中的设备寄存器映射:在嵌入式系统开发中,通常需要与硬件设备进行交互,如读取或写入设备的控制寄存器。这些设备寄存器中的各个位通常用于表示不同的控制或状态信息,使用位段可以方便地对这些位进行操作,提高程序的可读性和可维护性。

网络协议编程:在网络编程中,常常需要对数据包的各个位进行解析或者构造。使用位段可以方便地定义数据包的结构,提取或设置其中的各个字段,以便进行网络通信操作。

节省内存空间:在内存有限的嵌入式系统或者对内存占用有严格要求的系统中,使用位段可以有效地节省内存空间。例如,一个只需要 3 位来表示的状态标志,使用传统的整型数据类型可能浪费了较多的位数,而使用位段则可以节省内存空间

注意事项:

位段的⼏个成员共有同⼀个字节,内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的,所以不能对位段的成员使⽤&操作符。
正确做法

struct A
{ 
 int _a : 2;
 int _b : 5;
 int _c : 10;
 int _d : 30;
};

int main()
{
 struct A sa = {0};
 scanf("%d", &sa._b);//这是错误的
 
 //正确的
 int b = 0;
 scanf("%d", &b);
 sa._b = b;
 return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值