结构体对齐及联合体+大小端总结

一,结构体

概念:是一种新的自定义类型,由一组成员的不同数据组成的,每个成员都可以具有不同的数据类型,通常来表示类型不同但是又有 相关的若干数据。

例如:一个表示学生的结构体中有:char类型的学生名字,int类型的年龄等等。

 

struct Student
{
char name[20];
char gender[3];
int age;
double hegiht;
};
printf("%d\n", sizeof(struct Student));
//在使用是必须加上struct


struct Student//表示结构体类型名
{
char name[20];
char gender[3];
int age;
double hegiht;
}S1;
printf("%d\n", sizeof(S1));
//S1表示结构体变量名


typedef struct Student
{
char name[20];
char gender[3];
int age;
double hegiht;
}Student,*PStudent;
//Student是struct Student的别名
//PStudent是struct Student*的别名

 

结构体对齐:

对齐规则:

1. 第一个成员在与结构体变量偏移量为0的地址处。

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

(对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的值为8)

3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

struct S1
{
double d;//8
char c;//1+3,需要对齐所以是4
int i;//4
};
printf("%d\n", sizeof(struct S1));//8+4+4=16
struct S2
{
char c1;//1+7,
struct S1 s1;//16
double d;//8
};
printf("%d\n", sizeof(struct S2));8+16+8=32

内存对齐的原因:

1,平台原因:因为不是所有平台都可以访问任意地址上的数据。

2,性能:如果内存对齐,在访问时效率会更高。如果没有对齐,会出现为了访问未对齐的内存,处理器再一次访问内存。

 

 

二,联合体

定义:联合体也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员公用一块空间

union Un
{
int i;
char c;
};
union Un un;

 

联合体大小的计算:

1,联合的大小至少是最大成员的大小

2,当最大成员的大小不是最大对齐树的整倍时,就要对齐到最大对齐树的整数倍。

union Un1
{
char c[5];//5
int i;
};

union Un2
{
short c[7];//14
int i;
};

printf("%d\n", sizeof(union Un1));//8
printf("%d\n", sizeof(union Un2));//16,

 

联合体与结构体区别:

1,结构和联合都是由多个不同的数据类型成员组成,但在任一时刻,联合中只存放一个成员,而结构的所有成员都存在。

2,对于联合的不同成员赋值,将会对其成员重写,原来成员的值不存在了,而对于结构的不同成员赋值是互不影响的。

 

三,位段:

与结构体的对齐类似,但是成员必须是整形,而且段位成员后面必须要有一个冒号和数字

struct A
{
int _a:2;//_a是2个比特位
int _b:5;//_b占5个比特位
int _c:10;//_c占10个比特位
int _d:30;//_d占30个比特位
};
//_a+_b+_c对齐共用4个字节
//_d用4个字节,struct A的大小是8个字节

 

注:1,如果位段中的类型是有符号的,位段的高位表示的就是符号位

2,前后类型相同,比特位能共用则共用,否则重新开辟空间。


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

struct S s = { 0 };
s.a = 10;// 0000 1010 -> 010    ->2
s.b = 12;// 0000 1100 -> 1100   ->-4 (因为只取了4个比特位,首位是符号位所以s.b是-4)
s.c = 3;//  0000 0011 -> 0 0011 ->3
s.d = 4;//  0000 0100 -> 0100   ->4
printf("s=%d\n", sizeof(struct S));//3
printf("%d %d %d %d \n", s.a, s.b, s.c, s.d);//2 -4 3 4

 

 

大小端问题:

小端:是指数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中

大端:是指数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中

如何判断大小端:

1,利用union来检测:

因为联合体union的存放顺序是所有成员都从低地址开始存放这一特性进行检测

2,通过将多字节数据强制类型转换成单字节数据,再通过判断起始存储位置是数据高字节还是低字节进行检测

#include <stdio.h>

int   IsBigEndian_1()
{
    union Un
    {
        char c;
        int i;
    }un;
    un.i = 1;
    return un.c;
}

int IsBigEndian_2()
{
    int a = 1;
    return *(char*)&a;
//一个整形的指针每次偏移的是4个字节,而一个字符类型的指针每次偏移的是一个字节
}


int main()
{
    int ret=IsBigEndian_1();    
    if (1 == ret1)
    {
        printf("当前模式为小端存储\n");
    }
    else
    {
        printf("当前模式为大端存储\n");
    }
    return 0;
    
}

为什么区分大小端;大小端是根据cpu架构而决定的,跟操作系统无关。在网络编程是需要考虑双方机器是否大小端一致,在代码移植时需要考虑到硬件的情况,否则无法得到正确的数据。而且一部分PC机是小端,网络序是大端,在写代码的时候必须考虑到大小端转换的问题,否则网卡或者网络设备会解析错误。

 

 

  • 16
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值