结构体的深入了解(下)

1.修改默认对齐数

在之前我们了解到在vs中的默认对齐数为8,在gcc下没有默认对齐数的,那我们能否在vs上进行修改呢?我们来试一下:(#pragma 这个预处理指令,可以改变编译器的默认对齐数)


#include 
#pragma pack(1) //设置默认对⻬数为1
struct S
{
	char c1;
	int i;
	char c2;
};
int main()
{
	struct S s;
	size_t ret =sizeof(s);
	printf("%zd", ret);
	return 0;
}

在之前我们的这串代码结果为12,现在结果为6,虽然我们通过修改默认对齐数,而减少空间的浪费,但是我们编译器在寻找相对应的数据时也会浪费时间。这与我们了解结构体的对齐的作用相同。当我们要修改回默认对齐数时只需要再次输入#pragma pack(),即可。括号里为空。

2. 结构体传参

在之前的代码学习时,我们了解到可以将结构体整体传入,也可以传地址,但是这两种方法哪一种更好呢?当然是传地址了,因为我们传整个结构体的话,会多占用一份形参所申请的空间。

演示如下:

#include 
struct S
{
	char name[20];
	int age;
};
 void print1(struct S s[3])
{
	for (int i = 0; i < 3; i++)
	{
		printf("%s %d ", s[i].name, s[i].age);
	}
	
}
 void print2(struct S *pt)
 {
	 for (int i = 0; i < 3; i++)
	 {
		 printf("%s %d ", pt->name, pt->age);
		 pt++;
	 }
}
int main()
{
	struct S s[] = { {"zhang",18, } ,{"li",12},{"wang",24} };

	print1(s);
	printf("\n");
	print2(&s);
	return 0;
}

这里的print2就是采用传地址的方式,函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。所以结构体传参最好使用传地址的方式。

 3 .结构体实现位段

1.什么是位段

1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以
选择其他类型。
2. 位段的成员名后边有一个冒号和一个数字。

结构体位段的出现是为了节省空间。

#include
struct S
{
	char a:2 ;
	int b:20  ;
	int c:2  ;

};
int main()
{
	struct S s;
	printf("%zd ", sizeof(s));
	return 0;
}

这个结构体原来占用12个字节,现在占用多少个呢?

运行结果:

2.位段的内存分配


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

在不同编译器上位段的内存申请可能不同,下面我们来看下在vs上位段的内存申请是怎么样的?

struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;

 在内存中的表示:

结构体的深入了解(下)_第1张图片

为啥会是62 03 04 呢?我们来分析一下:

首先a我们给了它三个bit位,需要它存储的数字是10,10 的二进制为1010,存不下,发生截断只能存入010,b我们需要它存储的数字是12,它的二进制为1100也可以存下。以此内推,c中存的是00011,d中就是0100,我们一开始申请4个字节。

我们假设从右往左开始存放,如果后续空间不够就重新申请空间:一个字节一个字节的申请

结构体的深入了解(下)_第2张图片

 我们再一个字节一个字节的算,就是62 03 04,所以假设成立。这就是在vs中申请内存的方式。

(在位段中也存在对齐现象)

 3.位段的跨平台问题

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

4.位段的应用


下图是网络协议中,IP数据报的格式,我们可以看到其中很多的属性只需要几个bit位就能描述,这里使用位段,能够实现想要的效果,也节省了空间,这样网络传输的数据报大小也会较小一些,对网络的畅通是有帮助的。

位段使用的注意事项


位段的几个成员共有同一个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配一个地址,一个字节内部的bit位是没有地址的。
所以不能对位段的成员使用&操作符,这样就不能使用scanf直接给位段的成员输入值,只能是先输入放在一个变量中,然后赋值给位段的成员。

演示如下:

#include
struct S
{
	char a : 3;
	char b : 4;
};

int main()
{
	int n = 0;
	struct S s = { 0 };
	scanf("%d", &n);
	s.a = n;
	return 0;
}

这样就可以在不拿到位置的情况下,赋值了。

结构体的深入了解(下)_第3张图片 谢谢

你可能感兴趣的:(c语言,开发语言)