字节对齐问题和 小端格式

(1) 基本数据类型的对齐
每个类型的起始地址必须是自身大小的整数倍:

char(1字节) → 任意地址。
short(2字节) → 地址是2的倍数(如0x1002, 0x1004)。
int(4字节) → 地址是4的倍数(如0x1004, 0x1008)。
double(8字节) → 地址是8的倍数(如0x1008, 0x1010)。
指针(64位系统):占8字节,对齐到8的倍数。

(2) 结构体成员的对齐
规则:每个成员起始地址满足以下较小值:
成员自身大小;
编译器默认对齐系数(通常为4或8)。

查看如下程序的输出结果

#include <stdio.h> 
typedef struct {
    unsigned int    iap; 
    unsigned char    cap; 
    unsigned short    sap;
    unsigned char     tmp;
} DeviceAddr;

int main(int argc, char *argv) {
    DeviceAddr devaddr;
    unsigned  char *p = "d9ea327e8392xy";
    printf("sizeof(devaddr)=%lu\n", sizeof(devaddr));
    devaddr.tmp = 0x78;
    sscanf(p, "%04x%02x%06x", &devaddr.sap, &devaddr.cap, &devaddr.iap); 
    // sscanf(p, "%04hx%02hhx%06x", &devaddr.sap, &devaddr.cap, &devaddr.iap); 
    printf("iap: 0x%x, cap: 0x%x, sap: 0x%x, tmp=0x%x\n", devaddr.iap, devaddr.cap, devaddr.sap, devaddr.tmp);
    return 0;
}

编译后执行结果如下:

$ ./a.out
sizeof(devaddr)=12
iap: 0x7e8392, cap: 0x32, sap: 0x0, tmp=0x0

问题:sap的结果应该是0xd9ea 才对,为什么是0 呢? 并且 tmp的值也变成 0 了?

分析:
1. 首先明确 devaddr 中各变量的内存分布,加入起始地址是0,则各变量的地址
iap :0-3
cap : 4
sap :6-7
tmp : 8
注:地址5 和 地址9-11 都是补齐用的位置

2. 使用sscanf, 将4个字符输入到 sap,即0xd9ea, 但是因为 参数是%x,这对应的是unsigned int类型,
所以会将 0x0000d9ea 存入到 sap中,但sap只有两个字节的空间,并且是 小端格式。所以
内存地址6-9的位置分别存放的是 0xea 0xd9 0x00 0x00, 此时tmp的数值 会被覆盖。
3. 然后设置cap时候,%x 接收的是0x00000032, 从而将 内存4-7的位置赋值为 0x32 0x00 0x00 0x00, 此时将sap的数值也覆盖了
4. 设置iap时,0x007e8392 分别对 内存0-3的位置赋值为 0x92 0x83 0x7e 0x00, 没有影响cap

改正方法:sscanf(p, "%04hx%02hhx%06x", &devaddr.sap, &devaddr.cap, &devaddr.iap);
sizeof(devaddr)=12
iap: 0x7e8392, cap: 0x32, sap: 0xd9ea, tmp=0x78

posted @ 2025-07-16 16:20  靖意风  Views(13)  Comments(0)    收藏  举报