04-结构体

一. 结构体的定义声明和初始化

1.1 结构体定义

  • 定义: 结构体是值的集合, 与数组不同的是, 这个值可以是不同类型. 这些不同类型的值也被称为成员变量.
struct Test{
  int a = 10;
  char b = 'x';
  float f = 2.18;
};

1.2 结构体的声明方式

  • 结构体主要有以下3种声明方式

1.3 结构体的初始化和成员访问

  • 结构体访问成员的方式用: 结构体.成员变量
#include <stdio.h>

struct Day
{
	int day;
	char cty[20];
};

struct Node
{
	int data;
	char name[20];
	struct Day d;
};

int main()
{
	struct Node n = { 1, "djl", {3, "深圳"} };

	printf("{ %d, \"%s\", { %d, \"%s\" } }", n.data, n.name, n.d.day, n.d.cty);
	return 0;
}

二. 结构体自引用与typedef连用

2.1 结构体自引用

  • 结构体自引用典型应用就是链表.

2.2 结构体与typedef连用

  • 其作用就是将struct Node重新命名为Node(可以是任何名), 从而简化后续该类型结构体的创建.
#include <stdio.h>

typedef struct Node  //这里Node可以省略但是并不建议
{
	int data;
	struct Node* next;
}Node;

int main()
{
	Node n;  // 没有typedef的话, 需要: struct Node n;

	return 0;
}

三. 结构体内存对齐

3.1 为什么需要内存对齐

  • 平台移植问题: 不是所有平台都可以任意地址上的任意数据. 某些平台只能在特定地址取特定数据.
  • 性能问题: 处理器对未对齐的内存需要访问两次才能完整取出数据, 反之一次即可.(空间换时间)

3.2 内存对齐规则

  • 第一个成员在结构体变量偏移量为0的地址处
  • 其他成员变量要对齐某个数字(对齐数)的整数倍地址处[对齐数=编译器默认对齐数与该成员大小的较小值]
  • 结构体总大小为成员最大对齐数的整数倍(如果有嵌套结构体, 且该结构体中成员对齐数大于外部结构体, 那么它也是外部结构体最大对齐数)

3.3 拓展知识

  • 以下两个关于结构体的知识: 一. 默认对齐数设置方法 二. offsetof(结构体类型, 成员变量); 获取成员变量相对结构体的地址偏移量
#include <stdio.h>
#include <stddef.h>   //#pragma()需要的头文件

#pragma(4)  //设置默认对齐数(vs中默认为4)
struct S
{
	int a;
	char name[9];
	float f;
};
#pragma()   //结束标志.

int main()
{
	printf("%d", offsetof(struct S, f));  // offsetof(结构体类型, 成员变量);
	
	return 0;
}

3.3 分析案例

  • 分析以下两个结构体所占空间大小
#include <stdio.h>

struct S1
{
	char a;
	int b;
	char c;
};

struct S2
{
	char a;
	char c;
	int b;
};

int main()
{
	printf("%d\n", sizeof(struct S1));  // 12
	printf("%d", sizeof(struct S2));  // 8
	
	return 0;
}

总结: 由于存在内存对齐机制, 由上面这个案例可以看出, 成员变量合理的排布可以优化空间的使用

四. 结构体传参和位段

4.1 结构体传参

  • 结构体可以指针传递, 也可以值传递. 但是对于结构体一般推荐指针传递的方式(节省空间)
#include <stdio.h>

struct S
{
	int a;
};

void InitS(struct S* tmp) {};     //指针传递 (需要注意的是, 这里用指针接收, 里面访问成员需要使用: 结构体指针->成员变量)
void printfS(struct S tmp) {};   //值传递(这里可能只是打印结构体内容, 值传递可以防止内容被修改, 不过使用const指针也可以做到这点)

int main()
{
	struct S s;

	InitS(&s);
	printfS(s);

	return 0;
}

4.2 位段

  • 当需数据量十分庞大时, 普通结构体的内存对齐规则就无法满足需求, 需要使用对空间利用率更高的位段. 例如网络协议需要的包头

4.3 案例

  • 下面这段代码输出值为多少? (02290000)


总结: 位段每开辟一次空间都要从小端开始存放

posted @ 2023-06-24 16:02  烙铁666  阅读(58)  评论(0)    收藏  举报