[C][自定义类型][一][结构体]详细讲解


0.基础

  • 结构体是一些值的集合,这些值称为成员变量
    • 结构的每个成员可以是不同类型的变量
  • 结构成员的类型:可以是标量、数组、指针,甚至是其他结构体
  • 结构体之间可以赋值运算 ——> 函数返回值,可以返回结构体,可用于打包返回值

1.结构的声明

struct tag
{
	member-list;
}variable-list;

typedef struct Stu
{
	char name[20];
	char age;
	char sex[5];
	char id[20];
}Stu; // 分号不能丢
  • 例如
struct Stu
{
	// 成员变量
	struct B b;
	char name[20];
	int age;
	char id[20];
}s1, s2; // s1和s2也是结构体变量

2匿名结构体声明

  • 在声明结构的时候,可以不完全的声明
//匿名结构体类型
struct
{
    int a;
    char b;
    float c;
}x;

struct
{
	int a;
    char b;
    float c;
}a[20], *p;
  • 上面的两个结构在声明的时候省略了结构体标签(tag)
  • 在上面代码的基础上,p = &x;不合法
    • 编译器会把上面的两个声明当成完全不同的两个类型

3.结构的自引用

  • 自引用时用结构体指针
struct Node
{
	int data;
	struct Node* next;
};
  • 以下代码不合法,Node里包含Node会导致无限套娃
struct Node
{
	int data;
	struct Node next;
};

4.结构体变量的定义和初始化

struct Point
{
	int x;
	int y;
}p1; // 声明类型的同时定义变量p1

struct Point p2; // 定义结构体变量p2
  • 初始化:定义变量的同时赋初值
struct Point p3 = {x, y};

struct Stu //类型声明
{
	char name[15];//名字
	int age;      //年龄
};
struct Stu s = {"zhangsan", 20};//初始化

struct Node
{
	int data;
	struct Point p;
	struct Node* next; 
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

5.结构体成员的访问

  • .(点操作符)操作结构体成员
  • ->(结构体指针)访问指向变量的成员
struct S s;
strcpy(s.name, "zhangsan"); //使用.访问name成员
s.age = 20; //使用.访问age成员

printf("name = %s   age = %d\n", (*ps).name, (*ps).age);
// 使用结构体指针访问指向对象的成员
printf("name = %s   age = %d\n", ps->name, ps->age);

6.结构体传参

  • 下面的print1print2哪个好些?
    • print2()
    • 函数传参的时候,参数是需要压栈的
    • 如果传递一个结构体对象,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降
  • 结论结构体传参的时候,要传结构体的地址
struct S
{
    int data[1000];
    int num;
};
struct S s = {{1,2,3,4}, 1000};

// 结构体传参
void print1(struct S s)
{
    printf("%d\n", s.num);
}
// 结构体地址传参
void print2(struct S* ps)
{
    printf("%d\n", ps->num);
}

int main()
{
    print1(s);  // 传结构体
    print2(&s); // 传地址
    return 0;
}

6.5函数调用的参数压栈

  • ,一种数据结构 -> 先进的后出,后进的先出
  • 压栈出栈一说
    ![[Pasted image 20240417231237.png]]

7.结构体内存对齐

  • 计算结构体的大小
  • 结构体对齐规则
    1. 结构体的第一个成员放在结构体变量在内存中存储位置的0偏移量开始
    2. 从第2个成员往后的所有成员,都放在一个对齐数(成员的大小和默认对齐数的较小值)的整数的整数倍的地址处
      • VS中对齐数的默认值为8
    3. 结构体的总大小是结构体的所有成员的对齐数中最大的那个对齐数的整数倍
    4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
  • 为什么存在内存对齐?
    • 平台原因(移植原因):
      • 不是所有的硬件平台都能访问任意地址上的任意数据的
      • 某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
    • 性能原因
      • 数据结构(尤其是栈)应该尽可能地在自然边界上对齐
      • 原因:为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问
  • 总的来说,结构体的内存对齐是拿空间来换取时间的做法
  • 设计结构体时,如何既满足对齐,又要节省空间?
    • 合理安排结构体顺序:让占用空间小的成员尽量集中在一起
struct S1 // 不合理
{
    char c1;
    int i;
    char c2;
};

struct S2 // 合理
{
    char c1;
    char c2;
    int i;
};

8.修改默认对齐数

  • 通过#pragma pack()修改默认对齐数
  • 结论:结构体在对齐方式不合适的时候,可以自己更改默认对齐数(但并不一定提升空间和时间效率)
#pragma pack(8)//设置默认对齐数为8
struct S1
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
  • 36
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DieSnowK

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值