当前位置: 首页 > news >正文

13、C 语言结构体尺寸知识点总结

 

CPU 字长、地址对齐、变量 m 值等基础概念入手,详细阐述结构体尺寸的计算方法,以及如何确保结构体的可移植性

C 语言结构体尺寸核心知识点总结

一、基础概念:CPU 字长与地址对齐

结构体尺寸的计算依赖于系统的CPU 字长地址对齐规则,这是理解结构体内存布局的基础。

1. CPU 字长

  • 定义:CPU 一次能处理的数据长度(32 位系统为 4 字节,64 位系统为 8 字节),决定了内存存取的基本单位。
  • 影响:系统以字长为单位存取数据,例如 32 位 CPU 每次读写 4 字节,64 位 CPU 每次读写 8 字节。

2. 地址对齐

  • 定义:数据的地址必须是其 “对齐单位” 的整数倍,确保 CPU 高效存取数据。
  • 未对齐的问题

若数据地址未对齐(如 32 位系统中 4 字节数据存放在地址 0x1001),CPU 需分多次读写,降低性能;部分系统甚至会直接报错。

  • 对齐优势

对齐的数据可被 CPU 一次性或最少次数读写,以空间换时间提升效率。

场景

32 位系统(4 字节单位)

64 位系统(8 字节单位)

对齐数据

1-2 次读写完成

1-2 次读写完成

未对齐数据

3 次以上读写

3 次以上读写

二、普通变量的 m 值(对齐单位)

变量的m 值是其地址需满足的最小整数倍(确保地址对齐),由变量类型的尺寸和系统字长共同决定。

1. m 值的计算规则

  • m 值 = min (变量尺寸,系统字长)

(即 m 值不超过系统字长,且为变量尺寸的整数倍)

2. 常见类型的 m 值(32 位与 64 位系统)

变量类型

尺寸(字节)

32 位系统 m 值

64 位系统 m 值

说明

char

1

1

1

1 字节任意地址均对齐

short

2

2

2

地址需为 2 的倍数

int

4

4

4

地址需为 4 的倍数

float

4

4

4

同 int

double

8

4(兼容 32 位)

8

64 位系统需 8 的倍数

指针

4(32 位)/8(64 位)

4

8

与系统字长一致

3. 示例验证

#include <stdio.h>

int main() {

    char c;    // m=1,地址可为任意值

    short s;   // m=2,地址需为偶数

    int i;     // m=4,地址需为4的倍数

    double f;  // 32位m=4,64位m=8

    printf("char地址:%p(%d的倍数)\n", &c, (unsigned long)&c % 1);  // 余数0

    printf("short地址:%p(%d的倍数)\n", &s, (unsigned long)&s % 2); // 余数0

    printf("int地址:%p(%d的倍数)\n", &i, (unsigned long)&i % 4);    // 余数0

    return 0;

}

三、结构体尺寸的计算

结构体的尺寸不仅取决于成员的总大小,还受地址对齐成员排列顺序的影响,需遵循严格的计算规则。

1. 核心原则

  • 结构体的 M 值:结构体成员中最大的 m 值(即M = max(m1, m2, ..., mn))。
  • 结构体尺寸:必须是 M 的整数倍,且不小于成员总尺寸的理论值。

2. 计算步骤

  1. 计算成员总理论尺寸:将所有成员的尺寸相加(不考虑对齐)。
  2. 确定结构体的 M 值:找到成员中最大的 m 值。
  3. 计算最小对齐尺寸:找到不小于理论总尺寸且为 M 整数倍的最小数值。
  4. 调整成员排列:成员在内存中需按各自 m 值对齐,可能产生空隙(填充字节),最终尺寸为调整后的总大小。

3. 示例解析

示例 1:基础结构体(32 位系统)

struct node1 {

    char c;    // 尺寸1,m=1

    short s;   // 尺寸2,m=2

    int i;     // 尺寸4,m=4

};

  • 理论总尺寸:1 + 2 + 4 = 7
  • 成员最大 m 值(M):4
  • 最小对齐尺寸:8(7 的下一个 4 的倍数)
  • 实际内存布局:
    • c占 1 字节(地址 0),填充 1 字节(地址 1)以对齐s(m=2,地址 2);
    • s占 2 字节(地址 2-3);
    • i占 4 字节(地址 4-7);
    • 总尺寸:8 字节。
示例 2:含 double 的结构体(32 位系统)

struct node2 {

    char c;      // 尺寸1,m=1

    double d;    // 尺寸8,m=4(32位系统)

};

  • 理论总尺寸:1 + 8 = 9
  • 成员最大 m 值(M):4
  • 最小对齐尺寸:12(9 的下一个 4 的倍数)
  • 实际内存布局:
    • c占 1 字节(地址 0),填充 3 字节(地址 1-3)以对齐d(m=4,地址 4);
    • d占 8 字节(地址 4-11);
    • 总尺寸:12 字节。
示例 3:成员顺序对尺寸的影响

// 顺序1:尺寸12字节

struct node3 {

    char c;    // 1字节 + 3填充

    int i;     // 4字节

    short s;   // 2字节 + 2填充(凑齐M=4的倍数)

};

// 顺序2:尺寸8字节(更优)

struct node4 {

    int i;     // 4字节

    short s;   // 2字节

    char c;    // 1字节 + 1填充(凑齐M=4的倍数)

};

  • 结论:成员按 “大尺寸在前,小尺寸在后” 排列可减少填充,减小结构体尺寸。

四、结构体的可移植性

结构体在不同系统(如 32 位与 64 位)中可能因尺寸和成员位置变化导致不可移植,需通过技术手段解决。

1. 可移植性问题根源

  • 尺寸变化:基本类型(如 int)在不同系统中尺寸可能不同(如 16 位系统 int 为 2 字节)。
  • 位置变化:不同系统的对齐规则(m 值)不同,导致成员相对位置变化。

2. 解决方案

(1)使用可移植整型

通过typedef定义与尺寸绑定的类型,确保在任何系统中尺寸固定:

// 有符号整型

typedef signed char int8_t;    // 1字节

typedef signed short int16_t;  // 2字节

typedef signed int int32_t;    // 4字节

typedef signed long long int64_t; // 8字节

// 无符号整型

typedef unsigned char uint8_t;  // 1字节

typedef unsigned short uint16_t; // 2字节

typedef unsigned int uint32_t;  // 4字节

typedef unsigned long long uint64_t; // 8字节

(2)固定成员对齐与位置

通过 GNU 扩展__attribute__控制对齐方式:

  • 固定成员 m 值__attribute__((aligned(n)))强制成员的 m 值为 n(n 为 2 的幂)。

struct node5 {

    int8_t a __attribute__((aligned(1)));  // m=1

    int32_t b __attribute__((aligned(4))); // m=4

};

  • 压实结构体__attribute__((packed))取消填充,成员紧密排列(以性能换空间)。

struct node6 {

    int8_t a;

    int32_t b;

} __attribute__((packed)); // 总尺寸=1+4=5字节(无填充)

五、总结与注意事项

  1. 地址对齐的核心:以空间换时间,确保 CPU 高效存取数据,结构体尺寸计算必须遵循此规则。
  2. 成员排列技巧:按 “大尺寸成员在前,小尺寸成员在后” 排列,减少填充字节,优化结构体尺寸。
  3. 可移植性关键:使用可移植整型(如 int32_t)和__attribute__控制对齐,避免不同系统下的尺寸和位置差异。
  4. 实战建议:通过sizeof验证结构体尺寸,必要时借助编译器工具(如gcc -Wpadded)查看填充情况。

掌握结构体尺寸计算与可移植性技巧,是编写跨平台 C 代码和优化内存使用的重要基础。

http://www.dtcms.com/a/331361.html

相关文章:

  • LeetCode 刷题【41. 缺失的第一个正数】
  • 【力扣322】零钱兑换
  • 非容器方式安装Prometheus和Grafana,以及nginx配置访问Grafana
  • GraphRAG查询(Query)流程实现原理分析
  • NetLimiter:精准掌控网络流量,优化网络体验
  • 《中国人工智能安全承诺框架》发布
  • arthas火焰图怎么看
  • 搭建 Docker 私有仓库
  • 前端css学习笔记5:列表表格背景样式设置
  • 【Golang】Golang内存泄漏问题排查(二)
  • 服务器路由相关配置Linux和Windows
  • Android POS应用在android运行常见问题及解决方案
  • 当消息队列遇上AI:飞算JavaAI实现智能流量调度与故障自愈实践
  • 在 Windows 系统中解决 Git 推送时出现的 Permission denied (publickey) 错误,请按照以下详细步骤操作:
  • LE AUDIO---Common Audio Service
  • C#WPF实战出真汁02--登录界面设计
  • STM32学习笔记11-通信协议-串口基本发送与接收
  • 从轨道根数计算惯性系到轨道系旋转矩阵
  • 2020/12 JLPT听力原文 问题二 1番
  • [激光原理与应用-268]:理论 - 几何光学 - 人眼结构与颜色感知
  • Nacos 配置热更新:Spring Boot Bean 自动获取最新配置
  • 【21-倾斜数据集的误差指标】
  • 金融风控实战:从数据到模型的信用评分系统构建全解析
  • 使用马尔可夫链如何解码、预测股市模式
  • 西门子PLC通过稳联技术EtherCAT转Profinet网关连接baumuller伺服器的配置案例
  • ThreadPoolExecutor 最佳实践
  • 8月AI面试工具测评:破解规模化招聘难题
  • 哈希表特性与unordered_map/unordered_set实现分析
  • 风电功率预测实战:从数据清洗到时空建模​​
  • 从单机到分布式:用飞算JavaAI构建可扩展的TCP多人聊天系统