C语言结构体详解(下)

发布于:2022-10-21 ⋅ 阅读:(465) ⋅ 点赞:(0)

结构体的一些基础知识在我之前的文章: C语言结构体详解(上)中有讲到,友友们可以去看看

1.结构体声明

1.1结构体类型

这里我们定义一个结构体:

struct Stu
{
	char name[20];
	int age;
};

struct Stu就是结构体类型

1.1.1匿名结构体类型

//匿名结构体类型
struct
{
	int a;
	char b;
	float c;
}s;
struct
{
	int a;
	char b;
	float c;
}*ps;

这里的struct在声明的时候省略掉了结构体标签,这里的s,*ps是结构体变量名,像这样的省略标签项的结构体类型就是匿名结构体
这里有一个疑问,如果令 ps=&s 可以吗?答案是不行的,它们虽然成员相同,不过并不是同一个结构体,存放在不同的内存空间,编译器会把上面的两个声明当成完全不同的两个类型,是非法的

1.1.2 typedef定义结构体类型

代码运用:

typedef struct Stu
{
	int a;
	char b;
	float c;
}Node;

int main()
{
	Node n={1,'w',5.24};
	printf("%d %c %lf", n.a, n.b, n.c);//1 w 5.240000
	return 0;
}

当结构体类型比较复杂时就可以用typedef将结构体类型重定义,如上面的代码中将struct Stu重定义为Node后再使用

1.2结构体自引用

在结构中可以包含一个类型为该结构本身的成员,如:

struct Stu
{
	char name[20];
	int age;
	struct Stu* date;//OK

	//struct Stu date;//err
	//Stu date;//err
};

注意:结构体自引用只能使用传址调用,传值调用(sizeof(struct Stu)无法得知结构体大小)或者是省略结构体关键字都是错误的写法

1.3结构体内存对齐

结构体内存对齐主要用于帮助我们计算结构体的大小。
结构体的对齐规则:
1.结构体的第一个成员直接对齐到相对于结构体变量起始位置为0的地址处
2.从第二个成员开始,要对齐到某个数字(对齐数)的整数倍的偏移处
对齐数:结构体成员自身大小和编译器默认对齐数的较小值
VS中默认对齐数为8
Linux环境默认不设对齐数(对齐数是结构体成员的自身大小)
3.结构体的总大小必须是最大对齐数的整数倍。
每个结构体成员都有一个对齐数,其中最大的对齐数就是最大对齐数
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

1.3.1结构体大小计算实例

例题一

struct S1
{
	char c1;
	int i;
	char c2;
};

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

题解:

例题二

struct S2
{
	char c1;
	char c2;
	int i;
};
 
int main()
{
	printf("%d\n", sizeof(struct S2));
	return 0;
}
//输出8

题解:
在这里插入图片描述例题三

struct S1
{
	char c1;
	int i;
	char c2;
};

struct S2
{
	char c3;
	struct S1 s;
	double d;
};

int main()
{
	printf("%d\n", sizeof(struct S4));
	return 0;
}
//输出32

题解:

1.3.1为什么存在内存对齐

  1. 平台原因(移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
    3.总体来说:结构体的内存对齐是拿空间来换取时间的做法。
    4.在设计结构体的时候,我们既要满足对齐,又要节省空间,就要做到,让占用空间小的成员尽量集中在一起。如上述的例题一和例题二。

1.4修改默认对齐数

可以使用#prangma预处理指令改变我们的默认对齐数。结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。

代码实例:

#include <stdio.h>
#pragma pack(1)//设置默认对齐数为8
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
struct S2
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%d\n", sizeof(struct S1));//输出6
	printf("%d\n", sizeof(struct S2));//输出12
	return 0;
}

2.位段

2.1什么是位段?

位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。
3.位段-这里的位是二进制位

代码实例:

struct Stu
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};

2.2位段的内存分配

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

代码实例:

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};

int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	printf("%d\n", sizeof(struct S));//输出3
	return 0;
}

图解:

2.3位段的跨平台问题

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

总结:跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。
好了今天的内容就到这里了,对友友们有帮助的话不妨来个三连加关注支持一下,后期后持续更新C语言干货!

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到