C 语言中结构体字节对其的两种方式

本文介绍了C语言中实现字节对齐的两种方法:通过预处理命令#pragmapack(n)设置特定的对齐方式及使用__attribute__((packed))取消默认优化,确保结构体成员按实际大小对齐。

C语言中字节对其有两种方式:

1、通过预处理命令

#pragma pack(n),指定按照n字节对齐;
#pragma pack(),取消自定义的对齐值;

例如:

#pragma pack(2)
struct A 

  char c;   //第一个成员,自身长1,#pragma pack(2),取较小值,按照1字节对齐,放在[0]偏移的位置;
  int  i;   //第二个成员,自身长4,#pragma pack(2),取较小值,按照2字节对齐,放在[2,5]偏移的位置;
}; 
struct B 

  char c1;  //第一个成员,自身长1,#pragma pack(2),取较小值,按照1字节对齐,放在[0]偏移的位置;
  A    s;   //第二个成员,自身长6,#pragma pack(2),取较小值,按照2字节对齐,放在[2,7]偏移的位置;
  char c2;  //第三个成员,自身长1,#pragma pack(2),取较小值,按照1字节对齐,存放在[8]偏移的位置;
}; 
#pragma pack()

2、__attribute__((packed)):取消结构体在编译过程中的优化对齐,按照实际占用字节数进行对齐,等价于指令#pragma pack(1),即,按照1字节对齐;

备注:对于内存对齐,CPU的优化规则大致原则是这样的:对于n字节的元素(n=2,4,8,...),它的首地址能被n整除时,才能获得最好的性能;

在 C 语言中,**结构体(struct)** 和 **结构体指针(pointer to struct)** 是两种不同的数据类型,它们在内存使用、访问方式、性能和用途上有显著区别。下面详细解释它们的差异,并通过代码示例说明。 --- ## ✅ 1. 基本定义对比 | 类型 | 定义方式 | 含义 | |------|----------|------| | 结构体变量 | `struct Person p;` | 在栈上分配一个完整的结构体对象,包含所有成员 | | 结构体指针 | `struct Person *p;` | 指向某个结构体对象的地址,本身只是一个指针(通常 8 字节) | --- ## ✅ 2. 内存布局不同 ```c typedef struct { char name[32]; int age; float salary; } Person; ``` ### 情况一:结构体变量(值类型) ```c Person person1; // 直接在栈上分配内存 ``` - 占用内存 = `sizeof(Person)` ≈ 40 字节(取决于对齐) - 所有字段都存储在这个变量内部 ### 情况二:结构体指针 ```c Person *ptr = &person1; // 指向已有的结构体 Person *ptr2 = malloc(sizeof(Person)); // 动态分配在堆上 ``` - `ptr` 本身只占 8 字节(64位系统),但它指向一块 40 字节的空间 - 实际数据位于堆或栈上,由指针间接访问 --- ## ✅ 3. 访问成员的方式不同 ```c // 方法1:通过结构体变量访问 person1.age = 25; strcpy(person1.name, "Alice"); // 方法2:通过结构体指针访问 ptr->age = 30; // 等价于 (*ptr).age = 30; strcpy(ptr->name, "Bob"); ``` > ⚠️ 注意: > - `.` 用于结构体变量 > - `->` 用于结构体指针(推荐写法) > - `(*ptr).member` 等价但更繁琐 --- ## ✅ 4. 函数传参时的行为完全不同(关键区别!) ### ❌ 传递结构体(复制整个值) ```c void modify_person_bad(Person p) { p.age += 1; // 修改的是副本,原结构体不变 } Person p = {.age = 20}; modify_person_bad(p); printf("%d\n", p.age); // 输出 20 → 没变! ``` ✅ 缺点:效率低(大结构体会被完整拷贝)、无法修改原值 --- ### ✅ 传递结构体指针(推荐做法) ```c void modify_person_good(Person *p) { p->age += 1; // 修改原始结构体 } modify_person_good(&p); printf("%d\n", p.age); // 输出 21 → 成功修改! ``` ✅ 优点: - 不复制数据,速度快 - 可以修改原始结构体内容 - 节省内存(尤其对大型结构体) --- ## ✅ 5. 动态创建 vs 静态创建 | 方式 | 是否需要手动管理内存 | 使用场景 | |------|------------------------|---------| | 结构体变量 | 否(自动释放) | 局部临时使用 | | 结构体指针 + `malloc` | 是(需 `free`) | 返回给外部、大小未知、大对象 | ```c Person* create_person(const char* name, int age) { Person *p = malloc(sizeof(Person)); if (!p) return NULL; strncpy(p->name, name, 31); p->name[31] = '\0'; p->age = age; return p; // 返回堆上结构体指针 } // 使用后必须 free Person *p = create_person("Charlie", 35); ... free(p); // 必须调用 ``` --- ## ✅ 6. 性能与适用场景总结 | 对比项 | 结构体变量 | 结构体指针 | |--------|------------|------------| | 存储位置 | 栈(局部)或全局 | 指针在栈/全局,数据可在堆 | | 内存开销 | 大(等于结构体大小) | 小(指针仅 8 字节) | | 函数传参 | 会复制全部数据(慢) | 只传地址(快) | | 修改能力 | 不能直接修改原值 | 可修改指向的内容 | | 生命周期 | 自动管理 | 手动管理(malloc/free) | | 推荐程度 | 小结构体可用 | 大多数情况首选 | --- ## ✅ 示例对比完整代码 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char name[32]; int age; } Person; // 错误示范:传值导致无法修改 void set_age_by_value(Person p, int a) { p.age = a; // 改的是副本 } // 正确示范:传指针可以修改原结构体 void set_age_by_pointer(Person *p, int a) { p->age = a; } int main() { Person alice; // 结构体变量 Person *bob = malloc(sizeof(Person)); // 结构体指针指向堆内存 strcpy(alice.name, "Alice"); alice.age = 20; strcpy(bob->name, "Bob"); bob->age = 25; printf("Before: Alice=%d, Bob=%d\n", alice.age, bob->age); set_age_by_value(alice, 99); // 无效 set_age_by_pointer(bob, 99); // 有效 printf("After: Alice=%d, Bob=%d\n", alice.age, bob->age); free(bob); return 0; } ``` 输出: ``` Before: Alice=20, Bob=25 After: Alice=20, Bob=99 ``` --- ## ✅ 总结一句话: > 🔁 **结构体变量是“实体”,结构体指针是“遥控器”** > 你想操作谁?如果想高效地读写、传递或动态创建结构体,请使用 **结构体指针**;否则小对象可直接用变量。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值