内存中的字节对齐

来源:互联网 发布:数据分析师工资怎么样 编辑:程序博客网 时间:2024/06/07 01:12

先看个例子:

<span style="font-size:18px;">#include <stdio.h>struct {short sA;short sB;short sC;}A;struct {long lA;short sB;}B;struct {char chA;int iB;char chC;}C;struct {char chA;char chB;int iC;}D;struct {int iA;char chB;char chC;}E;int main(){printf("sizeof(A): %d\n", sizeof(A));printf("sizeof(B): %d\n", sizeof(B));printf("sizeof(C): %d\n", sizeof(C));printf("sizeof(D): %d\n", sizeof(D));printf("sizeof(E): %d\n", sizeof(E));return 0;}</span>

输出结果为:

6

8

12

8

8

分析:

1、结构体变量A,最长元素长度为2,每个成员连续存放刚好满足每个成员存储在2的边界上,而且A的内存大小为6刚好为2的整数倍。

2、结构体变量B,最长元素长度为4,成员存放仍然连续,满足每个成员在4的边界上,B的内存大小需要是8的时候满足是4的整数倍,6不符合这个要求。而且B的内存位置也需要在8的整数倍上,所以可以看到输出结果中结构体A完了之后有两个字节的空间没有使用,然后才是结构体B,(VS2005的输出结果,GCC中可能编译器做了些其他优化操作,它的内存分布是先B然后A),B内存空间最后又有两个字节是填补的。

3、结构体变量C,最长元素长度为4,第一个元素的起始必须是4的整数倍,由于第二个元素和第一个元素类型不同则起始地址也需要是4的整数倍,所以第一个元素之后有三个字节空间的浪费,第三个连续第二个存放满足起始地址是4的整数倍,现在是9个字节的大小,由于要求结构体大小是4的整数倍,所以结尾添加3个字节的填补空间,最后结构体的大小为12。

4、结构体变量D,最长元素长度为4,第一个和第二个元素类型同,二者连续存放,且第一个元素的起始位置是4的整数倍,由于要求第三个元素的位置也是4的整数倍,所以第二个元素之后填补了两个字节的空间。结构体总大小为8。

5、结构体变量E的情况可参考B和D。

不过从输出的结果上看,结构体的起始地址都是按4字节对齐的。

 

************************************************************************************************************************************


再看看C++对象的内存布局:


1. 空类:


class c000
{
}; // sizeof = 1
    它的大小为1字节,这是一个占位符,我们可以看到它的值是0xcc。在debug模式下,这表示是由编译器插入的调试代码所初始化的内存。在release模式下可能是个随机值。
    这个字节保证c000类的实例对象拥有不同的内存地址。&a!=&b。


2. 仅有普通成员变量的类
 
class c001
{
int a;
}; // sizeof = 4


指针4字节;
(unsigned) char占1字节,
(unsigned) short2字节,
(unsigned) int 4字节,
(unsigned) long4字节,
float4字节,
double8字节。
static成员变量存储在全局数据区,并不占用栈中的位置,因此不被sizeof计算在内。


class c002
{
char c1[2]; // 
char c2[2]; // 
int i[2]; // 
}; // sizeof = 12
c1 c2类型相同,可连续存储,加起来刚好4字节,与int的4字节相同,所以是12字节


class c003
{
char c1[2]; // 
int i[2]; // 
char c2[2]; //
}; // sizeof = 16
c1与i类型不同,所以c1也变成4字节了,c2也变成了4字节


class c004
{
  double d; // 
  int i; // 
}; // sizeof = 16


class c005
{
  char c[11]; // 
  double d; //
}; // sizeof = 24

double是8字节,要与double对齐的话,c 数组就变成了16字节


 要注意成员变量在类中的声明顺序也就是该变量在内存中的存放顺序。(内存对齐)
    这里采用了内存对齐。cpu的优化规则大致原则是这样的:对于n字节的元素(n=2,4,8…),它的首地址能被n整除,才能获得最好的性能。设计编译器的时候可以遵循这个原则:对于每一个变量,可以从当前位置向后找到第一个满足这个条件的地址作为首地址。


3. 有普通成员变量与普通成员函数的类
 
class c006
{
  int set(){return 1;};
  static int foo(){return 2;}
}; // sizeof = 1


class c007
{
short m; // sizeof = 2
float n[3]; // sizeof = 4
int set(){};
static int sfoo() { return 1; }
}; // sizeof = 16


    普通成员函数、静态成员函数、静态成员变量皆不会在类的对象中有所表示。
    成员函数和对象的关联由编译器在编译时处理,编译器会在编译时决议出正确的普通成员函数地址,并将对象的地址以this指针的方式,做为第一个参数传递给普通成员函数,以此来进行关联。
    静态成员函数类似于全局函数,不和具体的对象关联。静态成员变量也一样。静态成员函数和静态成员变量和普通的全局函数及全局变量不同之处在于它们多了一层名字限定。

(也即是说,函数对类占用多少个字节没有影响)


0 0
原创粉丝点击