【C语言】结构体

1、结构体中的对齐方式

#include <stdio.h>

struct person {
    char name[20];
    int age;
    char gender;
    float height;
};

int main() {
    struct person stu; //声明了struct person类型的变量
    printf("%ld\n", sizeof(stu)); //结果为32
    return 0;
}

之所以 stu 变量占32个字节,和结构体的对齐方式有关。

结构体的对齐方式:默认情况下,首先要确定空间对齐大小,先判断各个字段中占用空间最大的类型占的字节数,以此作为开辟或申请空间的标准。如上例中,struct person 中的类型有char、int、char、float,当前 floatint 都占4个字节,所以以4个字节为对齐标准,即申请空间的时候要申请4字节的整数倍。所以:

  1. name:申请5倍4个字节(即20个字节)
  2. age:申请4字节
  3. gender:也需要申请4字节,但是只使用了第一个字节存储数据;
  4. height:gender开辟的空间中还有三个字节是空的,但是并不能存储height,所以还要再开辟4字节。

因此,一共就是 5 * 4 + 4 + 4 + 4 = 32字节。

存在一个宏,可以强行改变结构体对齐大小 #pragma pack 

如下代码,依照之前的规则,以 4 字节为对齐标准,结构体中的大小就是 8 字节:

#include <stdio.h>

struct test {
    char c;
    int a;
};

int main() {
    struct test t;
    printf("%lu\n", sizeof(t)); //8
    return 0;
}

但是如果加了宏 pragma pack,强行改变对齐大小,如下:

#include <stdio.h>

//强行规定对齐大小为1字节
#pragma pack(1)
struct test {
    char c;
    int a;
};

int main() {
    struct test t;
    printf("%lu\n", sizeof(t)); //5
    return 0;
}

对齐标准为 1 字节,结构体的每个字段要分配的空间为 1 字节的整数倍,总共就是 1 + 4 = 5字节。

2、访问结构体中的字段

访问结构体中的字段有两种方式:

  • 直接引用,即使用”."直接引用运算符,使用变量值访问字段的时候使用“.”。如:
stu.name,stu.age;
  • 间接引用,使用“->” 间接应用运算符,通过地址访问字段的时候使用“->”。如:
struct person *p; 
p->age;

3、计算结构体中各字段的偏移量

v1版:

#include <stdio.h>

//需求:求某个字段的地址偏移量,偏移了多少个字节
//temp类型是结构体类型,而a是其他类型,所以统一转成char *类型。先定义一个结构体类型的变量temp
#define offset(T, a) ({\
    T temp;\
    (char *)&temp.a - (char *)&temp;\
})

struct Data {
    int a;
    double b;
    char c;
};

int main() {
    printf("%ld\n", offset(struct Data, a)); //传类型
    printf("%ld\n", offset(struct Data, b));
    printf("%ld\n", offset(struct Data, c));
    return 0;
}

v2版:利用空地址NULL做文章

#include <stdio.h>

//需求:求某个字段的地址偏移量,偏移了多少个字节
//先将空地址转为T*类型,获取结构体中的a字段,取a字段的地址,然后将地址转成long类型
#define offset(T, a) (long)(&(((T *)NULL)->a))

struct Data {
    int a;
    double b;
    char c;
};

int main() {
    printf("%ld\n", offset(struct Data, a)); //传类型
    printf("%ld\n", offset(struct Data, b));
    printf("%ld\n", offset(struct Data, c));

    return 0;
}

运行结果为:

0
8
16

理解"#define offset(T, a) (long)(&(((T *)NULL)->a))":

  • (T *)NULL:是一个指向T类型的指针,指针值为NULL(即0),其作用就是把从地址0开始的存储空间映射为一个T类型的对象
  • ((T *)NULL)->a:访问那类型中的成员a,相应地”&(((T *)NULL)->a)“ 就是返回这个成员的地址。由于对象的起始地址为0,所以成员的地址其实就是相对于对象首地址的成员的偏移地址,然后再通过类型转换为 long 类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值