C语言进阶整理 自定义类型:结构体 位段

C语言进阶整理 自定义类型:结构体 位段


正片开始

一、结构体

结构是一些值得集合,这些值称为成员变量。结构的每个成员可以是不同类型。

数组是一组相同类型的元素集合

1.1 结构体的基本声明

struct tag
{
    
    
    //成员变量;
};//分号别丢

例如

struct book
{
    
    
    char name[20];
    int price;
    char id[12];
}b4,b5,b6;//是全局变量,也是结构体类型变量,相当于inta中的a

int main()
{
    
    
    struct book b1={
    
    0};//也可以这么设变量
}

1.2 结构体的特殊声明

在声明结构的时候,可以不完全声明。

//匿名结构体类型 无结构体标签
struct
{
    
    
    int a;
    char b;
    float c;
}x;
struct
{
    
    
    int a;
    char b;
    float c;
}* p;
//这个问题合法吗?
p=&x;
//答案是不合法,因为编译器会当做两个类型,会出警告

1.3 结构体的自引用

在结构中包含一个类型为该结构本身的成员是否可以?

struct Node//Node 为节点,数据结构
{
    
    
    int data;//4
    struct Node next;//4+
};


答案是不可以,因为在struct Node类型中int为4个字节,而结构体struct Node nest中会反复出现在4+和反复调用自己,无限套娃,出现死递归。

解决办法:

//应该加上指针,找到下一个对应的地址
struct Node
{
    
    
    int data;
    struct Node* next;
};

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rZTNF8cN-1626965795371)(C:\Users\15277\AppData\Roaming\Typora\typora-user-images\image-20210722113201191.png)]

注意:

扫描二维码关注公众号,回复: 13173971 查看本文章

typedef-- - 类型重定义

typedef struct
{
    
    
   int data;
   Node* next;
}Node;
//这样写代码,可行否?

答案是不行,想用Node指针定义next这个成员必须先定义Node,存在先后顺序

解决方法:

typedef struct Node
{
    
    
   int data;
   struct Node* next;
}Node;

1.4 结构体内存对齐

现在深入讨论一下计算结构体的大小,这节特别重要!!!

struct s1
{
    
    
    double d;
    char c;
    int i;
};
printf("%d",sizeof(struct s1));

很多人觉得double 8字节,char1个字节,int4个字节,所以内存大小为13,其实大错特错,现在就来掌握下结构体的对齐规则。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2pTkmmy5-1626965795373)(C:\Users\15277\AppData\Roaming\Typora\typora-user-images\image-20210722112611064.png)]

下面看看编译器:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CiVmg8aG-1626965795374)(C:\Users\15277\AppData\Roaming\Typora\typora-user-images\image-20210722112655523.png)]

答案正确,确实是16.

嵌套情况尼?

struct s1
{
    
    
    double d;
    char c;
    int i;
};
printf("%d\n",sizeof(struct s1));

struct s2
{
    
    
    char c1;
    struct s1 s;
    double d;
};
printf("%d\n",sizeof(struct s2));

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UPJFnTWg-1626965795376)(C:\Users\15277\AppData\Roaming\Typora\typora-user-images\image-20210722121059491.png)]

看看编译器:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K8h72Vx9-1626965795377)(C:\Users\15277\AppData\Roaming\Typora\typora-user-images\image-20210722121146096.png)]

答案正确,确实是32.

为什么存在内存对齐?

大部分参考资料都是这样说的

  1. 平台原因(移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
    原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GzWeYefd-1626965795379)(C:\Users\15277\AppData\Roaming\Typora\typora-user-images\image-20210722213115458.png)]

总体来说:

结构体的内存对齐是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:

//例如:
struct S1
{
    
    
   char c1;
   int i;
   char c2;
};
struct S2
{
    
    
   char c1;
   char c2;
   int i;
};
//S1和S2类型的成员一模一样,但是S1和S2所占空间的大小有了一些区别

1.5 修改默认对齐数

一般vs默认对齐数是8,那我们可以改变对齐数吗?

#pragma 这个预处理指令,可以改变我们的默认对齐数。

#pragma pack(4)//设置默认对齐数4
、、、、、、、、
#pragma pack()//取消设置的默认对齐数,还原默认

结论

结构在对齐方式不合适的时候,我么可以自己更改默认对齐数。

offsetof

这个是一个宏,返回的是: 结构体成员 在内存中的偏移量。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O1a5jwyv-1626965795379)(C:\Users\15277\AppData\Roaming\Typora\typora-user-images\image-20210722215746214.png)]

1.6 结构体传参

结构体有两种传参的方式:1.值传参 2、地址传参

哪一种比较好尼?还是看例子吧

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gJ14pONd-1626965795380)(C:\Users\15277\AppData\Roaming\Typora\typora-user-images\image-20210722221622087.png)]

结论:
结构体传参的时候,要传结构体的地址。

二、位段

2.1什么是位段

位段的声明和结构是类似的,有两个不同:

1.位段的成员必须是 int、unsigned int 或signed int ,(char)整型家族
2.位段的成员名后边有一个冒号和一个数字。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-98FckcHk-1626965795381)(C:\Users\15277\AppData\Roaming\Typora\typora-user-images\image-20210722222050610.png)]

A就是一个位段类型。
那位段A的大小是多少?

struct A
{
    
    
	
	int _a : 2;//_a 成员占2个bit位
	int _b : 5;//_b 成员占5个bit位
	int _c : 10;//_c 成员占10个bit位
	int _d : 30;//_b 成员占30个bit位
};

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;
    };
    struct S s = {
          
           0 };
    s.a = 10;
    s.b = 12;
    s.c = 3;
    s.d = 4;
    //空间是如何开辟的?
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D4NNJMES-1626965795382)(C:\Users\15277\AppData\Roaming\Typora\typora-user-images\image-20210722224544758.png)]

2.3位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的。

  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
    器会出问题。

  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
    总结:
    跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

猜你喜欢

转载自blog.csdn.net/qq_54219272/article/details/119011048