写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。

在之前的 C语言结构体大小及对齐问题 文章上有分析过字节对齐的问题,但后面发现分析得不够透彻,于是以此篇继续阐述分析

一、字节对齐回顾

假设定义了如下两个结构体,算一下它们的数据类型长度

// 32位,X86处理器,GCC编译器
struct A{int    a;char   b;short  c;
};
struct B{char   b;int    a;short  c;
};

已知 32位机器上各数据类型的长度为:char为 1字节、short为 2字节、int为 4字节、long为 4字节、float为 4字节、double为 8字节。那么上面两个结构体大小如何呢?

结果是:sizeof(strcut A)值为 8;sizeof(struct B)的值却是 12。

  结构体 A中包含一个 4字节的 int数据,一个 1字节 char数据和一个 2字节 short数据;B也一样。按理说 A和 B大小应该都是 7字节。之所以出现上述结果,就是因为编译器要对数据成员在空间上进行对齐。具体的计算分析可以参看之前的 C语言结构体大小及对齐问题

二、处理器间数据通信

处理器间通过消息(对于C/C++而言就是结构体)进行通信时,需要注意字节对齐以及字节序的问题。

大多数编译器提供内存对其的选项供用户使用。这样用户可以根据处理器的情况选择不同的字节对齐方式。例如C/C++编译器提供的#pragma pack(n) n=1,2,4等,让编译器在生成目标文件时,使内存数据按照指定的方式排布在1,2,4等字节整除的内存地址处。

然而在不同编译平台或处理器上,字节对齐会造成消息结构长度的变化。编译器为了使字节对齐可能会对消息结构体进行填充,不同编译平台可能填充为不同的形式,大大增加处理器间数据通信的风险。

下面以 32位处理器为例,提出一种内存对齐方法以解决上述问题。

  • 对于本地使用的数据结构,为提高内存访问效率,采用四字节对齐方式;同时为了减少内存的开销,合理安排结构体成员的位置,减少四字节对齐导致的成员之间的空隙,降低内存开销。

  • 对于处理器之间的数据结构,需要保证消息长度不会因不同编译平台或处理器而导致消息结构体长度发生变化,使用一字节对齐方式对消息结构进行紧缩;为保证处理器之间的消息数据结构的内存访问效率,采用字节填充的方式自己对消息中成员进行四字节对齐。

数据结构的成员位置要兼顾成员之间的关系、数据访问效率和空间利用率。

顺序安排原则是:四字节的放在最前面,两字节的紧接最后一个四字节成员,一字节紧接最后一个两字节成员,填充字节放在最后。

例如:

typedef struct tag_T_MSG{long  ParaA;long  ParaB;short ParaC;char  ParaD;char  Pad;       //填充字节
}T_MSG;

三、对齐方式更改

每个特定平台上的编译器都有自己的默认 “对齐系数”(也叫对齐模数)。程序员可以通过预编译命令 #pragma pack(n),n=1,2,4,8,16 来改变这一系数,其中的 n就是你要指定的 “对齐系数”。

对齐方式的更改主要是更改 C编译器的缺省字节对齐方式。

在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:

  • 使用伪指令#pragma pack(n):C编译器将按照 n个字节对齐;
  • 使用伪指令#pragma pack(): 取消自定义字节对齐方式。

接下来我们通过实际例子来说明这些规则。

试验环境:Dev C++ 5.11。

平台:Windows 8。

我们将用典型的 struct对齐来说明。首先我们定义一个 struct:

#pragma pack(n) /* n = 1, 2, 4, 8, 16 */
struct test_t{int a;char b;short c;char d;
};
#pragma pack()

1、字节对齐(#pragma pack(1))
输出结果:sizeof(struct test_t) = 8

分析过程:

#pragma pack(1)struct test_t {/* 长度4 > 1 按1 对齐;起始offset=0 0%1=0;存放位置区间[0,3] */   int a;/* 长度1 = 1 按1 对齐;起始offset=4 4%1=0;存放位置区间[4] */char b;/* 长度2 > 1 按1 对齐;起始offset=5 5%1=0;存放位置区间[5,6] */short c;/* 长度1 = 1 按1 对齐;起始offset=7 7%1=0;存放位置区间[7] */char d;
};#pragma pack()

成员总大小=8

整体对齐系数= min((max(int,short,char), 1) = 1

整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整= 8 /* 8%1=0 */ [注1]

[注1] 什么是圆整

举例说明:假设整体大小=9 按4 圆整= 12

圆整的过程:从9 开始每次加一,看是否能被4 整除,这里9,10,11 均不能被4 整除,到12 时可以,则圆整结束。

2、字节对齐(#pragma pack(2))
输出结果:sizeof(struct test_t) = 10

分析过程:

#pragma pack(2)struct test_t {/* 长度4 > 2 按2 对齐;起始offset=0 0%2=0;存放位置区间[0,3] */int a;/* 长度1 < 2 按1 对齐;起始offset=4 4%1=0;存放位置区间[4] */char b;/* 长度2 = 2 按2 对齐;起始offset=6 6%2=0;存放位置区间[6,7] */short c;/* 长度1 < 2 按1 对齐;起始offset=8 8%1=0;存放位置区间[8] */char d;
};#pragma pack()

成员总大小=9

整体对齐系数= min((max(int,short,char), 2) = 2

整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整= 10 /* 10%2=0 */

3、字节对齐(#pragma pack(4))
输出结果:sizeof(struct test_t) = 12

分析过程:

#pragma pack(4)struct test_t {/* 长度4 = 4 按4 对齐;起始offset=0 0%4=0;存放位置区间[0,3] */int a;/* 长度1 < 4 按1 对齐;起始offset=4 4%1=0;存放位置区间[4] */char b;/* 长度2 < 4 按2 对齐;起始offset=6 6%2=0;存放位置区间[6,7] */short c;/* 长度1 < 4 按1 对齐;起始offset=8 8%1=0;存放位置区间[8] */char d;
};#pragma pack()

成员总大小=9

整体对齐系数= min((max(int,short,char), 4) = 4

整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整= 12 /* 12%4=0 */

4、字节对齐(#pragma pack(8))
输出结果:sizeof(struct test_t) = 12

分析过程:

#pragma pack(8)struct test_t {/* 长度4 < 8 按4 对齐;起始offset=0 0%4=0;存放位置区间[0,3] */int a;/* 长度1 < 8 按1 对齐;起始offset=4 4%1=0;存放位置区间[4] */char b;/* 长度2 < 8 按2 对齐;起始offset=6 6%2=0;存放位置区间[6,7] */short c; /* 长度1 < 8 按1 对齐;起始offset=8 8%1=0;存放位置区间[8] */char d;
};#pragma pack()

成员总大小=9

整体对齐系数= min((max(int,short,char), 8) = 4

整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整= 12 /* 12%4=0 */

5、字节对齐(#pragma pack(16))
输出结果:sizeof(struct test_t) = 12

分析过程:

#pragma pack(16)struct test_t {/* 长度4 < 16 按4 对齐;起始offset=0 0%4=0;存放位置区间[0,3] */int a;/* 长度1 < 16 按1 对齐;起始offset=4 4%1=0;存放位置区间[4] */char b;/* 长度2 < 16 按2 对齐;起始offset=6 6%2=0;存放位置区间[6,7] */short c;/* 长度1 < 16 按1 对齐;起始offset=8 8%1=0;存放位置区间[8] */char d;
};#pragma pack()

成员总大小=9

整体对齐系数= min((max(int,short,char), 16) = 4

整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整= 12 /* 12%4=0 */

四、总结分析

  • 数据成员对齐规则(结构体或类的自身对齐值):其成员中自身对齐值最大的那个值。
  • 整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的 pack值}。
  • 推测:当 #pragma pack 的 n值等于或超过所有数据成员长度的时候,这个 n值的大小将不产生任何效果。

C语言结构体大小及对齐问题 · 续相关推荐

  1. C语言结构体大小;结构体嵌套结构体大小的计算方法分析

    C语言结构体大小:结构体嵌套结构体大小的计算方法分析! 在了解结构体大小如何计算之前,我们首先得了解结构体的对齐规则: 第一个成员在与结构体变量偏移量为0的地址处: 其他成员变量要对齐到某个数字(对齐 ...

  2. C语言结构体大小计算

    这篇文章来探讨一下C语言中,结构体占的内存大小如何计算. printf("str = %d", sizeof(struct str));//用这个方法来查看一个结构体的大小 我尝试 ...

  3. (C语言)计算结构体大小——结构体内存对齐

    本篇文章计划用简单的方式向大家介绍如何计算结构体大小这一问题 首先我们必须明确一件事,想要计算结构体大小,就不得不了解结构体内存对齐的规则 一.结构体的对齐规则 1.第一个成员在在与结构体变量为0的地 ...

  4. C语言结构体详解(结构体定义,使用,结构体大小等)

    c语言结构体详解 1.c语言结构体 1.1 结构体基础知识 1.2 结构体声明 1.3 结构体特殊声明 1.4 结构体的自引用 1.5 结构体的大小的计算 1.5.1了解结构体大小计算规则 1.5.2 ...

  5. 【转】彻底搞清计算结构体大小和数据对齐原则

    数据对齐: 许多计算机系统对基本数据类型合法地址做出了一些限制,要求某种类型对象的地址必须是 某个值K(通常是2,4或8)的倍数.这种对齐限制简化了形成处理器和存储器系统之间的接口的硬件 设计.例如, ...

  6. 【C语言进阶深度学习记录】二十 结构体大小计算与结构体内存布局的详细方法

    结构体大小的计算往往是面试笔试常考的知识.对于简单的结构体,可以一眼看出来,对于复杂的结构体,该如何计算结构体占用内存的大小呢? 本文学习所使用的编译器是gcc 4.4.5 使用其他编译器或者使用Wi ...

  7. Windows下struct和union字节对齐设置以及大小的确定(一 简介和结构体大小的确定)...

    在windows下设置字节对齐大小的方式,目前我了解有三种: 1. 在编译程序时候的编译选项  /Zp[n],如 cl /Zp4 表示对齐大小是4字节: 2. 预处理命令   #pragma pack ...

  8. C语言sizeof结构体大小计算

    一.一般类型 约定为32位系统,即char 1字节.short 2字节.int 4字节,指针是地址4个字节 定义:偏移量 偏移量指的是结构体变量中成员的地址和结构体变量地址的差.结构体大小等于最后一个 ...

  9. c语言 struct结构体大小计算方法

    struct结构体大小的计算方法 1)第一个成员处在偏移地址0的地方 2)每个成员按其类型大小和pack参数中较小(编译器默认#pragma pack(4) 4字节对齐)的一个进行对齐 偏移地址必须能 ...

最新文章

  1. 老年痴呆 数字化_设计老年人愉快数字体验的5条原则
  2. 35岁不是程序员的坎儿,看不清楚这件事才是!
  3. [UGUI]圆形Image
  4. java关联查询实战_MapReduce实战(五)实现关联查询
  5. 微信监控机器学习、深度学习训练过程,可视化
  6. image 微信小程序flex_第三天学习微信小程序开发总结
  7. python循环控制语句将数值转化成字符串_python基础入门详解(文件输入/输出内建类型字典操作使用方法)...
  8. 人工智能机器学习笔记 10月15日
  9. 自定义镜像迁移实例到新的区域(实例启动异常排解)
  10. ai人工智能让女神_人工智能可能只会让你兴奋不已
  11. Java web项目如何在服务器上跑起来(有源码)
  12. linux防ddos攻击脚本,Linux系统防止DDOS攻击脚本
  13. python获取网页内容 不打开_网页抓取python不返回任何内容
  14. There were errors checking the update sites: SSLHandshakeException: sun.secu解决方案
  15. Javaweb开发了解前端知识七、Servlet(一)
  16. EGE基础入门篇(九):双缓冲与手动渲染
  17. LeetCode 09:回文数(Java实现)
  18. Java编程验证鬼谷猜想,山河令:阿絮是什么时候知道老温是鬼谷谷主的?老温心里没点数吗?...
  19. python建站与java建站有何不同_Python与JAVA有何区别?
  20. 重装系统后只剩一个C盘的解决方法

热门文章

  1. EWSTM8系列教程02_新建基础软件工程
  2. java for 奇数_java – 如何仅使用for循环生成奇数
  3. Android开发之十四: camera(一):camera模组CMM介绍
  4. 图片转换word格式用什么软件好
  5. Egret实战开发笔记,飞行射击游戏(六)
  6. MDAEMON邮件服务器修改
  7. c语言实现带加减乘除以及单元函数的计算器
  8. 什么是深度学习,如何进行深度学习?
  9. 用于多分支的条件语句(if-else语句)
  10. Nao机器人Any ball detect