成员变量对齐的原理

我花了一个上午,看了一些资料,总算把这个问题搞明白了。下面我以一些例子说明结构体成员变量的对齐问题。

对于

struct s1

{

char a;

long int d;

double c;

};

这个结构体的大小是16。编译器默认的一般是8字节对齐。a的大小是1,它就按1字节对齐(因为比指定的8 小),存诸在0偏移的地方;b大小为4,它就按4字节对齐(因为比指定的8小),存在偏移4——7的位置,c大小为8,存在8——15的位置。这样3个成员共占用了16个字节。由于该结构最大成员c大小为8,所以结构按8字节对齐,16按8园整还是16,因此sizeof s1 = 16.

而对于

struct s2

{

char a;

long int d;

double c;

char e;

};

这个结构体的大小是24。前3个成员和上面一样存诸,d在4——7位置,c在8——15位置,但e按1字节对齐,存在偏移位置16,这样4个成员占用了17个字节。由于该结构的最大的数据成员c的大小是8个字节,所以17对8园整得24。

当然你可以使用#pragma指令指定编译器按4字节对齐。即

#pragma pack(4)       // 这里也可以是#pragma pack(push,4)

struct s1

{

char a;

long int d;

double c;

};

struct s2

{

char a;

long int d;

double c;

char e;

};

这时s1的大小还是16,而s2的大小变为20。我们来分析一下,对s1来说,按4字节对齐和按8字节对齐个数据成员的存诸位置是一样的,只不过是最后一部园整时,16对4园整还是16。对s2就不一样了,a的大小为1(比指定的4字节对齐要小),按1字节对齐,存诸在0位置,d的大小是4(大于等于指定的4字节),按4字节对齐,存诸在4——7位置,c的大小是8(大于指定的4字节),按4字节对齐,这样存诸在 8——15,e的大小是1,存储在位置16,这样整个结构体的长度是17,17对4园整,得20。你也看到并不是指定编译器按4字节对齐就按4字节对齐的。比如下面的结构体:

#pragma pack(4)

struct TestStruct2

{

char m1[11];

short m2;

};

你知道它的大小吗?是14。因为m1按1字节对齐,存诸在0——11位置,m2按2字节对齐,存诸在12——13位置。结构体占用13个字节,因为结构体内最大的成员的数据类型是short,大小为2,比指定的对齐字节4小,所以13对2园整,得14。综的说来就是结构体成员的对齐是用成员本身的大小和 #pragma pack(push,n)中的n中较小的数对齐,例如如果成员大小为2,而你指定的对齐方式是4,则该成员按2对齐;结构本身的对其是用结构中最大成员的大小和#pragma pack(push,n)中的n较小的数对齐,即最后的园整,例如如果结构中最大成员大小8,而你指定对齐是16,则结构本身按8对齐。

下面给出不同位数编译器下的基本数据类型所占的字节数:

16位编译器

char :1个字节
char*(即指针变量): 2个字节
short int : 2个字节
int:  2个字节
unsigned int : 2个字节
float:  4个字节
double:   8个字节
long:   4个字节
long long:  8个字节
unsigned long:  4个字节

32位编译器

char :1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int:  4个字节
unsigned int : 4个字节
float:  4个字节
double:   8个字节
long:   4个字节
long long:  8个字节
unsigned long:  4个字节

64位编译器

char :1个字节
char*(即指针变量): 8个字节
short int : 2个字节
int:  4个字节
unsigned int : 4个字节
float:  4个字节
double:   8个字节
long:   8个字节
long long:  8个字节
unsigned long:  8个字节

时间: 2024-08-10 20:34:04

成员变量对齐的原理的相关文章

在类有成员变量的场景下, 按照虚表原理, 模拟虚函数实现

前言 当类没有成员变量的情况下,   类首地址有4个字节的空间, 这里可以放我们模拟出来的虚表入口地址. 当类有成员变量的情况下, 类首地址就是成员变量,  所以, 为了模拟虚表实现, 需要在成员变量前, 再定义一个int型变量, 用来存放模拟的虚表入口地址. 现在还得不到虚析构函数的地址, 暂时按照非虚析构函数进行模拟. 这个实验是在C++中模拟的. 模拟虚函数实现的用途 在非OOP语言(C语言)中, 模拟类的实现, 可以实现虚函数的效果. 效果 工程下载点 编译环境: vc6sp6 + wi

《coredump问题原理探究》Linux x86版6.2节C++风格数据结构内存布局之有成员变量的类

上面一节已经探究出this指针的辨别,由this指针就可以看到类的内容.在这里,就由this指针来看一下类的成员变量是如何排列. 先看一个例子 1 #include <stdio.h> 2 class xuzhina_dump_c06_s2 3 { 4 private: 5 short m_c; 6 char m_d; 7 int m_e; 8 9 public: 10 xuzhina_dump_c06_s2( int a, int b ) 11 { 12 m_c = (short)(a +

《coredump问题原理探究》Linux x86版6.3节有成员变量的类coredump例子

在探究完类成员变量分布后,来定位一个coredump例子来实践一把: (gdb) bt #0 0x0804863c in xuzhina_dump_c06_s2_ex::print() () #1 0x08048713 in main () 看一下xuzhina_dump_c06_s2_ex::print的汇编: (gdb) disassemble 0x0804863c Dump of assembler code for function _ZN22xuzhina_dump_c06_s2_ex

《coredump问题原理探究》Linux x86版6.1节C++风格数据结构内存布局之无成员变量的类

在探究完C风格数据结构内存布局之后,接着探究C++风格数据结构内存布局. 虽然最简单的类是没有任何成员变量和成员函数,但由于没什么意义,不值得探究.在这里,就先探究一下没有任何成员变量和虚函数,只有成员函数的类. 先看一下例子: 1 #include <stdio.h> 2 class xuzhina_dump_c06_s1 3 { 4 public: 5 void hello() 6 { 7 printf( "hello\n" ); 8 } 9 void print()

C语言中结构体内部成员的对齐

说明: ******不同的编译器和处理器,其结构体内部的成员有不同的对齐方式. ******使用sizeof()运算符计算结构体的长度. ###结构体中每个成员相对于结构首地址的偏移量都是成员大小的整数倍,如果有需要编译器会在成员之间加上填充字. ###结构体的总大小是结构体最宽基本类型成员大小的整数倍.如果需要编译器会在最后一个成员之后加上填充字. struct A { <span style="white-space:pre"> </span>unsigne

成员变量与初始化块

public class A { public int number = 5; { number = 10; } public static void main(String[] args) { A a = new A(); System.out.println(a.number); }} 这段代码的执行结果是a的值是10;下面将初始化块和成员变量交换一下顺序让成员变量在后面 public class A { { number = 10; } public int number = 5; pub

Objective-C的属性和成员变量用法及关系浅析

在使用Objective-C语言进行了一段时间的iOS开发之后,发现自己的语言基础相对薄弱,于是开始弥补自己的短处.我发现在用过一种语言之后,再回过头来看它的很多原理会发现有更加深刻的理解.下面就对一直困惑我的属性和成员变量的用法和关系问题进行浅析,由于水平有限可能会有错误,请看过文章的人多多指正. 1.属性 关于属性的用法在苹果的官方文档<The Objective-C Programming Language>中有详细的说明,在这里就不再赘述,链接如下: <The Objective

C++对象模型:成员变量&lt;一&gt;非静态成员

非静态成员变量,分别两种可能,要么类自定义,要么继承而来.根据<深度探索C++对象模型>的解读. class X { private: int x,y,z; }; 在这个类中,有三个私有成员变量(不管私有,保护,或公有),都按照某个次序排列(一般根据定义的先后顺序),唯一需要注意的是:某些变量需要对齐填充.在内存中的排列次序依次为:x,y,z:假如需要对这三个变量进行操作,实际会在成员函数中填充一个指针,参照<深入浅出MFC>. class X { public: void f()

OC 对象调用属性(成员变量)和方法

1.成员变量可以理解为所有在类的头上声明的,无论是@interface.@implementation下用大括号括起来或者是用@property声明的变量都可以称作这个类的 成员变量,只是在@implementation下声明的变量是这个类的私有变量,外部无法访问的,而在@interface下声明的变量可以设定为全局或者私有变量, 而用@property声明的就是全局变量,是外部既可以访问,内部也可以调用的 2.首先确定一下,对象.xxx的语法后面跟的不是方法,是一个可以被访问的成员变量,之所以