解析C语言中结构体struct的对齐问题

首先看一下结构体对齐的三个概念值:

数据类型的默认对齐值(自身对齐):
1.基本数据类型:为指定平台上基本类型的长度。如在32位机器中,char对齐值为1,short为2,int,float为4,double为8;
结构体:其数据成员中默认对齐值最大的那个值。
2.指定对齐值:#pragma pack (value)时的指定对齐值value。
3.数据类型的有效对齐值:默认对齐值和指定对齐值中小的那个值。
有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的“偏移量%N=0”。而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放(对于非对齐成员需要在其前面填充一些字节,保证其在对齐位置上),结构体本身也要根据自身的有效对齐值圆整(就是结构体总长度需要是结构体有效对齐值的整数倍)。

通过上面的分析,对结构体进行字节对齐,我们需要知道四个值:

  • 指定对齐值:代码中指定的对齐值,记为packLen;
  • 默认对齐值:结构体中每个数据成员及结构体本身都有默认对齐值,记为defaultLen;
  • 成员偏移量:即相对于结构体起始位置的长度,记为offset;
  • 成员长度:结构体中每个数据成员的长度(注结构体成员为补齐之后的长度),记为memberLen。

及两个规则:

1.对齐规则:
offset % vaildLen = 0,其中vaildLen为有效对齐值vaildLen = min(packLen, defaultLen);

2.填充规则:
如成员变量不遵守对齐规则,则需要对其补齐;在其前面填充一些字节保证该成员对齐。需填充的字节数记为padLen:

padLen = getPadLen(offset , defaultLen);
int getPadLen(int offsetLen, int defaultLen)
{
  int vaildLen = min(packLen,defaultLen);
  if(0 == vaildLen || 0 == offsetLen % vaildLen)
  {
    return 0;
  }
  return vaildLen - (offsetLen % vaildLen);
}

结构体对齐算法思想:深度优先填充

先对齐内层结构体;
对每个数据成员计算其defaultLen、memberLen和offset;

再遍历每个数据成员时计算;
对于基本数据类型成员defaultLen=memberLen;对于结构体成员defaultLen等于它的所有成员的最大的memberLen;
遍历时对成员的memberLen进行累加,得到当前成员的offsetLen;
运用对齐及填充规则:在当前结构体成员前填充padLen个字节;

举例说明:

struct{

 short a;

 short b;

 short c; }A; sizeof(A) = 6;

(vc6与gcc相同)

struct{

 long a;

 short c; }A; sizeof(A) = 8;

(vc6与gcc相同), 它的内存分配为: a1 a2 a3 a4 , c1 c2 x x(a1为a的第一个字节,x为补齐字节,下同)

struct{

int a;

char b;

short c; }A;

sizeof(A) = 8;

A的内存分配为:

 a1 a2 a3 a4, b1 x c1 c2
struct{

char a;

int b;

short c; }A1;

sizeof(A1) = 12;

(vc6与gcc相同)

A1的内存分配为:

a1 x x x, b1 b2 b3 b4, c1 c2 x x

下面是更复杂的情况,结构体作为成员

struct{

int a;

doubl b;

short c; }A; // sizeof(A) = 24 (vc6与gcc相同)

struct{

  char a,b;

int c;

double d;

short e;

struct A h;

}B;

sizeof(B) = 48 //(vc6与gcc相同)

A的内存分布:

a1 a2 a3 a4 x x x x, b1 b2 b3 b4 b5 b6 b7 b7, c1 c2 x x x x x x

B的内存分布:

a1 b1 x x, c1 c2 c3 c4 , d1 d2 d3 d4 d5 d6 d7 d8, e1 e2 x x x x 
(0)

相关推荐

  • C语言结构体(struct)常见使用方法(细节问题)

    基本定义:结构体,通俗讲就像是打包封装,把一些有共同特征(比如同属于某一类事物的属性,往往是某种业务相关属性的聚合)的变量封装在内部,通过一定方法访问修改内部变量. 结构体定义: 第一种:只有结构体定义 struct stuff{ char job[20]; int age; float height; }; 第二种:附加该结构体类型的"结构体变量"的初始化的结构体定义 //直接带变量名Huqinwei struct stuff{ char job[20]; int age; floa

  • php读取二进制流(C语言结构体struct数据文件)的深入解析

    尽管php是用C语言开发的,不过令我不解的是php没有提供对结构体struct的直接支持.不过php提供了pack和unpack函数,用来进行二进制数据(binary data)和php内部数据的互转: 复制代码 代码如下: string pack ( string $format [, mixed $args [, mixed $...]] )   //Pack given arguments into binary string according to format.  array unp

  • C语言中结构体struct编写的一些要点解析

    一.关于结构体的声明 1.匿名声明.如: struct { int i,j; }point; 说明: 这段代码的含义是,声明一个无名(anonymous)的结构体,并创建了一个结构体变量point.如果这段声明是放在全局域(在任意函数(比如main函数)外)内,那么point内的变量将被初始化为默认值,换句话说,以这种方式声明结构体变量时就已经为它分配了内存空间. 适用于该结构体只需要产生一个变量!本例中,该匿名结构体将有且仅有point这个结构体变量! 不同的匿名结构体变量,类型是不同的!如

  • C语言 结构体(Struct)详解及示例代码

    前面的教程中我们讲解了数组(Array),它是一组具有相同类型的数据的集合.但在实际的编程过程中,我们往往还需要一组类型不同的数据,例如对于学生信息登记表,姓名为字符串,学号为整数,年龄为整数,所在的学习小组为字符,成绩为小数,因为数据类型不同,显然不能用一个数组来存放. 在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据.结构体的定义形式为: struct 结构体名{     结构体所包含的变量或数组 }; 结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可

  • C语言中结构体(struct)的几种初始化方法

    本文给大家总结的struct数据有3种初始化方法 1.顺序 2.C风格的乱序 3.C++风格的乱序 下面通过示例代码详细介绍这三种初始化方法. 1)顺序 这种方法很常见,在一般的介绍C的书中都有介绍.顺序初始化的特点是: 按照成员定义的顺序,从前到后逐个初始化:允许只初始化部分成员:在被初始化的成员之前,不能有未初始化的成员. 示例: struct User oneUser = {10, "Lucy", "/home/Lucy"}; 2)乱序(C风格) 顺序的缺陷是

  • 解析C语言中结构体struct的对齐问题

    首先看一下结构体对齐的三个概念值: 数据类型的默认对齐值(自身对齐): 1.基本数据类型:为指定平台上基本类型的长度.如在32位机器中,char对齐值为1,short为2,int,float为4,double为8: 结构体:其数据成员中默认对齐值最大的那个值. 2.指定对齐值:#pragma pack (value)时的指定对齐值value. 3.数据类型的有效对齐值:默认对齐值和指定对齐值中小的那个值. 有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式.有效对齐值N是最

  • C语言中结构体与内存对齐实例解析

    1.结构体类型 C语言中的2种类型:原生类型和自定义类型,结构体类型是一种自定义类型. 2.结构体使用时先定义结构体类型再用类型定义变量 -> 结构体定义时需要先定义结构体类型,然后再用类型来定义变量. -> 也可以在定义结构体类型的同时定义结构体变量. // 定义类型 struct people { char name[20]; int age; }; // 定义类型的同时定义变量. struct student { char name[20]; int age; }s1; // 将类型st

  • 详解C语言中结构体(struct)的用法

    目录 粉丝问答 三种结构体类型变量说明 1. 先定义结构,再定义结构变量 2. 定义结构体类型的同时说明变量 3. 直接说明结构变量 结构体成员表示方法 结构体指针做参数 结构体初始化 定义变量的同时初始化 先定义在初始化 常用初始化 typedef与struct 前置声明 结构体对齐 粉丝问答 有个粉丝在群里问了这样一个问题,问题在图中已经标出,如下图. DQuestsion 头文件的结构体的定义为: typedef struct{ u8 bmRequestType; u8 bRequest;

  • C语言中结构体的内存对齐规则讲解

    目录 1.结构体的内存对齐规则 2.例子 3.为什么存在内存对齐 4.如何修改默认对齐数 1.结构体的内存对齐规则 1.第一个成员在与结构体变量偏移量为0的地址处. 2.其他成员变量都放在对齐数(成员的大小和默认对齐数的较小值)的整数倍的地址处. 对齐数=编译器默认的一个对齐数与该成员大小的较小值.(VS中默认的对齐数是8) 3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数 )的整数倍. 4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最

  • C语言中结构体实例解析

    目录 一.结构体定义 二.实例演示 结构体作为函数参数 结构体指针 三.typedef struct 和 struct的区别 1.声明不同 2.访问成员变量不同 3.重新定义不同 总结 一.结构体定义 C语言结构体由一系列相同或者不同类型的数据构成的集合,结构体类型就是以struct关键字定义的数据类型. 结构体的格式如下: struct 结构名称 { 结构体所包含的数据成员,包括变量数组等 } 结构变量 ;//结构变量可以指定一个或多个 举例: struct Student { char na

  • C语言中结构体、联合体的成员内存对齐情况

    前言 最近项目进行中,遇到一个小问题,在数据协议传输过程中,我为了方便解析,就定义了一个结构体,在数据的指针传入函数的时候,我用定义好的结构体进行强制转化,没想到一直解析失败,调试很久,终于反应过来,在用结构体指针对数据强制转换时,定义结构体我没有注意到数据对齐,因为在底层实现中,我传入的数据buffer是排列整齐的,而强制转化的结构体格式中,我定义的时候没有使用__attribute__((__packed__))或者__packed强制数据对齐,导致结构体成员真实排列会按照成员中最大的变量的

  • 详解Go语言中结构体与JSON间的转换

    目录 前言 结构体转 JSON JSON 解析结构体 小结 前言 在日常开发中,我们往往会将 JSON 解析成对应的结构体,反之也会将结构体转成 JSON.接下来本文会通过 JSON 包的两个函数,来介绍 JSON 与结构体之间的转换. 结构体转 JSON Marshal(v any) ([]byte, error):将 v 转成 JSON 数据,以 []byte 的形式返回. import ( "encoding/json" "fmt" ) type User s

  • Go语言中结构体方法副本传参与指针传参的区别介绍

    GO语言结构体方法跟结构体指针方法的区别 首先,我定了三个接口.一个结构和三个方法: type DeptModeA interface { Name() string SetName(name string) } type DeptModeB interface { Relocate(building string, floor uint8) } type Dept struct { name string building string floor uint8 Key string } fun

随机推荐