C字节对齐与C++类对象内存布局

一、什么是对齐,以及为什么要对齐:

1. 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

2. 对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况, 但是最常见的是如果不按照适合其平台的要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为 32位)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低 字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。

二、对齐的实现

通常,我们写程序的时候,不需要考虑对齐问题。编译器会替我们选择适合目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对指定数据的对齐方法。
但是,正因为我们一般不需要关心这个问题,所以因为编辑器对数据存放做了对齐,而我们不了解的话,常常会对一些问题感到迷惑。最常见的就是struct数据结构的sizeof结果,出乎意料。为此,我们需要对对齐算法所了解。
对齐的算法:
由于各个平台和编译器的不同,现以本人使用的gcc version 3.2.2编译器(32位x86平台)为例子,来讨论编译器对struct数据结构中的各成员如何进行对齐的。
设结构体如下定义:
struct A {
    int a;
    char b;
    short c;
};
结构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一个。所以A用到的空间应该是7字节。但是因为编译器要对数据成员在空间上进行对齐。
所以使用sizeof(strcut A)值为8。
现在把该结构体调整成员变量的顺序。
struct B {
    char b;
    int a;
    short c;
};
这时候同样是总共7个字节的变量,但是sizeof(struct B)的值却是12。
下面我们使用预编译指令#pragma pack (value)来告诉编译器,使用我们指定的对齐值来取代缺省的。
#progma pack (2) /*指定按2字节对齐*/
struct C {
    char b;
    int a;
    short c;
};
#progma pack () /*取消指定对齐,恢复缺省对齐*/
sizeof(struct C)值是8。

修改对齐值为1:
#progma pack (1) /*指定按1字节对齐*/
struct D {
    char b;
    int a;
    short c;
};
#progma pack () /*取消指定对齐,恢复缺省对齐*/
sizeof(struct D)值为7。

对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。
这里面有四个概念值:
1)数据类型自身的对齐值:就是上面交代的基本数据类型的自身对齐值。

2)指定对齐值:#pragma pack (value)时的指定对齐值value。

3)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。

4)数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。

       有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是 数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整 数倍,结合下面例子理解)。这样就不难理解上面的几个例子的值了。
例子分析:
分析例子B;
struct B {
    char b;
    int a;
    short c;
};
假设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。第一个成员变量b的自身对齐值是1,比指定或者默认指 定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a,其自身对齐值为4,所以有效对齐值也为 4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐 值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存 放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求, 0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B 共有12个字节,sizeof(struct B)=12;

同理,分析上面例子C:
#pragma pack (2) /*指定按2字节对齐*/
struct C {
    char b;
    int a;
    short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
第一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1= 0;第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续 字节中,符合0x0002%2=0。第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放
在0x0006、0x0007中,符合0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量。又C的自身对齐值为4,所以 C的有效对齐值为2。又8%2=0,C只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8.

有 了以上的解释,相信你对C语言的字节对齐概念应该有了清楚的认识了吧。在网络程序中,掌握这个概念可是很重要的喔,在不同平台之间(比如在Windows 和Linux之间)传递2进制流(比如结构体),那么在这两个平台间必须要定义相同的对齐方式,不然莫名其妙的出了一些错,可是很难排查的哦^_^。

#include<stdio.h>int main()
{struct   YourStruct   {	char   dda;   		double   dda1; 		int   type;			};  #pragma   pack(push)   //保存对齐状态     #pragma   pack(4)     //设定为4字节对齐  struct   MyStruct     {	char   dda;   		double   dda1; 		int   type;			};  printf("%d\n",sizeof(struct MyStruct));#pragma  pack(pop)   //恢复对齐状态printf("%d\n",sizeof(struct YourStruct));return 0;
}


C++对象内存布局:

http://www.cnblogs.com/lindeshi/archive/2012/10/20/2732590.html

http://www.cnblogs.com/lindeshi/archive/2012/10/20/2732592.html


查看虚函数表:

http://www.cnblogs.com/mhjerry/archive/2012/12/16/2820895.html(很不错的一篇文章)


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/508730.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

word小结

域代码/域结果显示设置 word选项---->>高级------>>显示域代码而非域值将样式传给其它文件使用 首先启动Word打开包含这些样式的一个文件&#xff0c;然后选择“工具”---->“模板和加载项”。在弹出的对话框中单击“管理器”按钮。在弹出的“管理器”对话框中&…

excel中VBa应用总结

单元格操作Range("C4") 1 很直观&#xff0c;不用多解释了吧[C4] 1 跟Range差不多&#xff0c;但更简洁Cells(4,3) 1 Cells有2个参数Rowindex和ColumnIndex&#xff0c;分别是行号和列号。注意是先行后列Sheets(1).Range("A1").Value 1 使用 Sheets(in…

回调函数的应用

VPP 协议组件提供了许多的函数接口供开发者调用&#xff0c;同时也提供了很多的回调函数接口需要我们去实现&#xff0c;而由协议栈内部来调用。说白了&#xff0c;回调函数就是一个函数指针&#xff0c;协议栈通过一个函数指针&#xff0c;指向我们注册给回调函数接口的函数&a…

VBScript自动打卡

下载插件并注册 将下载后的AutoItX3.dll放到WINDOWS文件夹下&#xff0c;开始--->运行--->regsvr32 AutoItX3.dll 编辑代码&#xff1a;打卡.vbs Set oAutoIt WScript.CreateObject("AutoItX3.Control")Set WShell CreateObject("wscript.shell"…

C/C++语言函数学习(1):atexit、exit、return

/*stdlib.h*void exit(int status);*功 能&#xff1a;终止程序*int atexit(void &#xff08;*func)(void));*功 能: 注册终止函数&#xff0c;在main函数结束以后调用*/ //#include <stdio.h> #include <stdlib.h> void exit_fn1(void) {printf("Exit fun…

C/C++语言函数学习(2)qsort

/***函数名称: qsort*函数原型: void qsort(void *base,size_t num,size_t width,int (*fcmp)(const void *,const void *)*函数功能: 使用快速排序法对数组base进行排序*函数返回:*参数说明:*base 待排序数组*num 数组元素个数*width 每个…

C/C++语言函数学习(3)STL中map容器

/******************************************************************map的基本操作函数&#xff1a;C Maps是一种关联式容器&#xff0c;包含“关键字/值”对begin() 返回指向map头部的迭代器clear(&#xff09; 删除所有元素count() 返回指定元素…

C/C++语言函数学习(4)字符串处理函数

#include <string.h>函数名称: strchr函数原型: char* strchr(char* str,char ch);函数功能: 找出str指向的字符串中第一次出现字符ch的位置函数返回: 返回指向该位置的指针,如找不到,则返回空指针参数说明: str-待搜索的字符串&#xff0c;ch-查找的…

C/C++函数学习(6)容器分类

http://net.pku.edu.cn/~yhf/UsingSTL.htm 三十分钟掌握STL STL容器分三大类&#xff1a;顺序容器(sequence container)、关联容器(associative container)和容器适配器(container adapter)。另外我们熟悉的C语言式数组合string&#xff0c;它们也是一种容器&#xff0c;称为…

Python字符串内建函数

方法 描述 string.capitalize() 把字符串的第一个字符大写 string.center(width) 返回一个原字符串居中,并使用空格填充至长度 width 的新字符串 string.count(str, beg0,endlen(string)) 返回 str 在 string 里面出现的次数&#xff0c;如果 beg 或者 end 指…

巧用快捷键轻松设置Excel单元格格式

巧用快捷键轻松设置Excel单元格格式 在应用程序中使用快捷键&#xff0c;可以使我们避免在键盘与鼠标之间来回切换&#xff0c;从而节省大量的时间&#xff0c;显著地提高工作效率。多掌握一些常用的快捷键&#xff0c;可以使您告别菜鸟时代&#xff0c;迅速成长为电脑应用的高…

perror()与strerror()的应用及区别 man手册查询

perror() 和 strerror() 以一种直观的方式打印出错误信息&#xff0c;对于调试程序和编写优秀的程序非常有用。下面是perror() 与 strerror() 的使用范例及区别&#xff1a;perror()原型&#xff1a;#include <stdio.h>void perror(const char *s);其中&#xff0c;perro…

Linux下解压缩包命令

各种压缩文件的解包与打包命令 .tar 解包&#xff1a;tar xvf FileName.tar打包&#xff1a;tar cvf FileName.tar DirName——————————————— .tar .xz 解包&#xff1a;xz -d openvpn-2.3.5.tar.xz --stdout | tar -xv 打包&#xff1a;tar cvf - openvpn-2.3.…

PyQt学习总结

1&#xff09;QDoubleSpinBox() setRange(min,max) 设置范围Min~maxsetSingleStep(step) 设置步长为stepsetValue(value) 设置当前值为valuesetPrefix("$") 设置前缀为$setSuffix("%"&#xff09; 设置后缀为%selectAl…

Qt样例学习1(数字时钟)

/*main.cpp*/ #include <QApplication> #include <QTextCodec> #include "digitalclock.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);//显示本地化&#xff0c;解决中文乱码.QTextCodec::setCodecForTr(QTextCodec::codecForLoca…

linux网络编程--数据结构与函数原型

套接字有三种类型&#xff1a;流式套接字&#xff08;SOCK_STREAM&#xff09;&#xff0c;数据报套接字&#xff08;SOCK_DGRAM&#xff09;及原始套接字。 socket() | bind() | listen() | socket() a…

OpenGL画简单图形

#include <GL/glut.h>#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") /*三角形GL_TRIANGLES(三个点成一个三角形)GL_TRIANGLE_STRIP(相邻三点成一个三角形)GL_TRIANGLE_FAN(同第二个&#xff0c;后2个和第一…

ubuntu下编译OpenGL

安装基本编译环境&#xff1a; sudo apt-get install build-essential 安装Opengl工具箱&#xff1a; sudo apt-get install freeglut3-dev /* main.cpp */ #include <GL/glut.h> #include <stdlib.h> #include <stdio.h> #include "app.h" using …

关于linux的进程和线程

关于linux的进程和线程 http://kenby.iteye.com/blog/1014039 Linux下的多线程编程 http://fanqiang.chinaunix.net/a4/b8/20010811/0905001105.html 线程的最大特点是资源的共享性&#xff0c;但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程…

linux setsockopt函数

功能描述&#xff1a; 获取或者设置与某个套接字关联的选 项。选项可能存在于多层协议中&#xff0c;它们总会出现在最上面的套接字层。当操作套接字选项时&#xff0c;选项位于的层和选项的名称必须给出。为了操作套接字层的选项&#xff0c;应该 将层的值指定为SOL_S…