为什么需要字节对齐?

摘要:
#pragmapack的基本用法是:#pragmapack。n是字节对齐数。其值为1、2、4、8和16。默认值为8。如果该值小于结构成员的sizeof值,则结构成员的偏移量应基于该值,即结构成员的偏差应取两者中的最小值,公式如下:offsetof=min再举一个例子:#pragmapack//将当前包设置推到堆栈上以保存#pragmapack//您必须使用structS1{charc;int i;}在定义结构之前;结构S3{charc1;S1s;charc2;};#Pragmapack//要恢复以前的包设置,请注意“空结构”的大小不是0,而是1。

为什么需要字节对齐?

计算机组成原理教导我们这样有助于加快计算机的取数速度,否则就得多花指令周期了。为此,编译器默认会对结构体进行处理(实际上其它地方的数据变量也是如此),让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被4整除的地址上,以此类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。

字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:

1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);

3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。

结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获得,这个宏也在stddef.h中定义,如下:

struct S2

{

int i;

char c;

};

#define offsetof(s,m) (size_t)&(((s *)0)->m)

例如,想要获得S2c的偏移量,方法为

size_t pos = offsetof(S2, c);// pos等于4

还有一个影响sizeof的重要参量,那便是编译器的 pack指令。它是用来调整结构体对齐方式的,不同编译器名称和用法略有不同,VC6中通过#pragma pack实现,也可以直接修改/Zp编译开关。

#pragma pack的基本用法为:#pragma pack( n )n为字节对齐数,其取值为124816,默认是8,如果这个值比结构体成员的sizeof值小,那么该成员的偏移量应该以此值为准,即是说,结构体成员的偏移量应该取二者的最小值,

公式如下:

offsetof( item ) = min( n, sizeof( item ) )

再看示例:

#pragma pack(push) // 将当前pack设置压栈保存

#pragma pack(2) // 必须在结构体定义之前使用

struct S1

{

char c;

int i;

};

struct S3

{

char c1;

S1 s;

char c2;

};

#pragma pack(pop) // 恢复先前的pack设置

还有一点要注意,“空结构体”(不含数据成员)的大小不为0,而是1。试想一个“不占空间”的变量如何被取地址、两个不同的“空结构体”变量又如何得以区分呢于是,“空结构体”变量也得被存储,这样编译器也就只能为其分配一个字节的空间用于占位了。

struct S5 { };

原文:

http://baike.baidu.com/view/1356720.htm

免责声明:文章转载自《为什么需要字节对齐?》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Python的几个爬虫代码整理(网易云、微信、淘宝、今日头条)[转载] 使用 Docker 部署 openstf 平台下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

iOS中Block的用法,举例,解析与底层原理

1. 前言 Block:带有自动变量(局部变量)的匿名函数。它是C语言的扩充功能。之所以是拓展,是因为C语言不允许存在这样匿名函数。 1.1 匿名函数 匿名函数是指不带函数名称函数。C语言中,函数是怎样的呢?类似这样: int func(int count); 调用的时候: int result = func(10); func就是它的函数名。也可以通...

TCP/IP网络编程系列之三(初级)

TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值。端口号并非赋予计算机值,而是为了区分程序中创建的套接字而分配给套接字的序号。 网络地址 网络地址分为IPV4和IPV6,分别你别为4个字节地址簇和6个字节地址簇。IPV4标准的4个字...

Code Tips: gcc对结构体的默认字节“对齐”方式

1. 发现问题     最近在编写代码过程中发现,对一个结构体进行 sizeof 操作时,有时候大小是填充过的,有时候又没有填充。     那么,如果在代码中没有显示的指定要求编译器进行对齐时,gcc的默认处理是怎样的呢? 2. 先说结论     代码中如果没有显示指定字节对齐时,gcc默认并没有进行cpu宽度字节对齐;     gcc会将结构体的大小填...

函数的返回值为结构体类型

可见,函数的返回值为结构体类型,其返回值既不是“值传递”也不是通过“寄存器”回传。编译器在编译此类函数时,为其附加了一个指针参数(指向的地址在caller的堆栈上),且作为函数的第一个参数(函数本身的参数依次后移),函数语义上的返回值通过该附加的指针参数回传,而函数真正的返回值就是该指针。 ————————————————版权声明:本文为CSDN博主「st...

解析STM32的库函数

意法半导体在推出STM32微控制器之初,也同时提供了一套完整细致的固件开发包,里面包含了在STM32开发过程中所涉及到的所有底层操作。通过在程序开发中引入这样的固件开发包,可以使开发人员从复杂冗余的底层寄存器操作中解放出来,将精力专注应用程序的开发上,这便是ST推出这样一个开发包的初衷。 但这对于许多从51/AVR这类单片机的开发转到STM32平台的开发...

C#与C++数据类型比较及结构体转换[整理]

//c++:HANDLE(void   *)                          ----    c#:System.IntPtr//c++:Byte(unsigned   char)                     ----    c#:System.Byte//c++:SHORT(short)                   ...