结构体,位段问题

一、结构体

 1.简单说说结构体是什么?
 结构体就是把不同的数据类型整合到一起,组成的一个数据类型!!
 2.结构体的创建和初始化

struct Stu
{
   
	char name[20];
	int age;
	char sex[10];
	char id[20];
};
int main()
{
   
	struct Stu s1 = {
    "zhangsan",20,"男","202131705118" };
	struct Stu s2 = {
   .age = 20,.id = "110",.name = "魏江南",.sex = "女"};
	printf("%s %d %s %s\n", s1.name, s1.age, s1.sex, s1.id);
	printf("%s %d %s %s\n", s2.name, s2.age, s2.sex, s2.id);
	return 0;
}

 3.匿名结构体类型

//匿名结构体类型
//没有写结构体名称
struct
{
   
 int a;
 char b;
 float c;
}x;
struct
{
   
 int a;
 char b;
 float c;
}a[20], *p;
p = &x;

 这个匿名结构体可以理解为两次定义,x,a[20],*p没有关系,因此会报错!
 1.编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。
 2.匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。

 4.结构体的自引用
 结构体的子引用也就是定义链表节点的功能!

struct SLTNode
{
   
	int val;
	struct SLTNode* next;
}

 再写一下就是typedef一下,重新起一个名字!!!

二、结构体内存分配问题

 1.对齐规则。
 * 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
 *其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处
&&// 对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。

 - VS 中默认的值为8
 - Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

 *结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。
  * 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
1
1
1

1
 解析:(1)第一个char直接放,第二个int对齐数为4,所以前面为4个字节,后面放了4个字节,共8个字节,还有一个char没放,放完之后9个字节,结构体的总大小为最大对齐数的整数倍,最大对齐数为4,所以是12个字节。
 (2)第二问,第一个是char,第二个是char,2个字节,第三个是int,前面要4个字节才对齐,所以总共是8个字节。

struct S3
{
   
	double d; //8
	char c; //1
	int i;  //4
};

int main()
{
   
	printf("%d\n", sizeof(struct S3)); //16
	return 0;
}

 上面double 8个字节,char1个字节,要对齐所以共12个字节,int 4个字节,所以共16个字节。

struct S3
{
   
	double d; //8
	char c; //1
	int i;  //4
};
//
//int main()
//{
   
//	printf("%d\n", sizeof(struct S3)); //16
//	return 0;
//}

struct S4
{
   
	char c1;  //1
	struct S3 s3; //16
	double d;
};

//8+16+8=32
int main()
{
   
	printf("%d\n", sizeof(struct S4));//32
	return 0;
}

 第一个char为1个字节,struct S3 s3为16个字节,里面的最大对齐数为8个字节,所以总共是8+16 = 24个字节,最后一个double 是8个字节,正好对齐,来放double ,总共24+8 = 32个字节!!!

三、存在内存对齐的原因

 1.平台原因(移植原因):
 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
 2.性能原因
 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。
这儿在说一下这个内存对齐的优势,就是浪费一点点空间,内存对齐取数据的效率高一点,是以空间换取时间的做法!

 那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到:
让占⽤空间⼩的成员尽量集中在⼀起
//例如:
struct S1
{
char c1;
int i;
char c2;
};
/
struct S2
{
char c1;
char c2;
int i;
};
S1 和 S2 类型的成员⼀模⼀样,但是 S1 和 S2 所占空间的大小有了⼀些区别

 3.修改默认对齐数
#pragma 这个预处理指令,可以改变编译器的默认对齐数。
#pragma pack()

#pragma pack()

1

四、结构体传参问题

 结构体传参,怎么传,传地址还是复制传整个结构体,答案是传地址,地址占用空间小,可以一部到位,也可以通过地址直接改变结构体的值!

struct S
{
   
	int data[100];
	int num;
};

void print1(struct S s)
{
   
	printf("%d\n", s.num);
}

void print2(struct S *s)
{
   
	printf("%d\n", s->num);
}

int main()
{
   
	struct S s = {
    .data = {
   0},.num = 1 };
	print1(s);
	print2(&s);
	return 0;
}

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

五、结构体实现位段

 1.什么是位段?
**位段的成员必须是int,unsigned int, signed int
**位段的成员名后面有一个数字和一个冒号!

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

int main()
{
   
	printf("%zd\n", sizeof(struct A));
	return 0;
}

 2.位段的内存存储问题!!!
 **位段的成员可以是unsigned int ,signed int ,char类型
  **位段的空间是按需要以4个字节或1个字节来开辟的!
  **位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。

1
1
 在vs2022中,由上面例子可以看出内存是由一个字节从右到左开辟,如果剩余空间开辟的内存不够,则另开辟一个字节,重新存储数据!!!
 3.位段的跨平台问题!
 1. int 位段被当成有符号数还是⽆符号数是不确定的。
 2. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会
出问题。
 3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
 4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃剩余的位还是利⽤,这是不确定的。

位段是以节省空间为目的而创造出来的数据类型,他在申请空间时,是按比特位的形式来存储的,但是它存在跨平台问题!!!
 4.位段的应用
1

 5.位段使用的注意事项!!!
 位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。
 所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。
这个问题就是说,它的位段成员分配空间是按比特位来分配的,不是按字节数分配的,会存在两个位段成员有同一个地址问题,因此不能&操作!!!



 完结!!!!

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-02-15 08:14:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-15 08:14:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-15 08:14:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-15 08:14:02       20 阅读

热门阅读

  1. Windows安装DeepSpeed

    2024-02-15 08:14:02       37 阅读
  2. 什么是系统工程(字幕)27

    2024-02-15 08:14:02       24 阅读
  3. 2024.2.14作业

    2024-02-15 08:14:02       39 阅读
  4. 【docker 的常用命令——详细讲解】

    2024-02-15 08:14:02       28 阅读
  5. 甲辰年正月初五情人节

    2024-02-15 08:14:02       31 阅读
  6. AutoSAR(基础入门篇)9.1-协议数据单元PDU

    2024-02-15 08:14:02       35 阅读
  7. 如何学习机器学习和深度学习: 软件工程师指南

    2024-02-15 08:14:02       36 阅读
  8. 假期作业 2月14日

    2024-02-15 08:14:02       31 阅读
  9. 盐值1111

    2024-02-15 08:14:02       31 阅读
  10. elasticSearch使用场景深入详解

    2024-02-15 08:14:02       30 阅读
  11. SpringAOP的实现原理

    2024-02-15 08:14:02       31 阅读