对于类与结构体的字节对齐以及C++11中关键字alignas,alignof

一.什么是字节对齐?

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

二.为什么要字节对齐?

        主要原因是用空间来换取存储效率。访问的话就是类似 硬件每次读取地址的位置差。相当于pc = pc+1;每次移动的偏移量都是一样。这个偏移量就是字节对齐的值。

三.常用类型占用空间大小。

常见类型大小(32位系统)
类型大小
char1
int4
float4
double8
short2

四.字节对齐要区分的四个概念(重要)

  1. 基本数据类型的自身对齐值
  2. 程序指定的字节对齐值   #pragma pack(n)
  3. 自定义类型的自身对齐值:结构体或类的成员中自身对齐值最大的值
  4. 自定义类型的有效对齐值:自定义类型的自身对齐值与程序指定的对齐值中较小的值。

补充:最后的空间大小是字节对齐的整数倍

五.示例分析。

首先在vc6.0中,程序默认的是8字节对齐方式。

查看方法是在project -> settings -> c/c++  ->  categloy  ->code Generation 里面就可以看到。

例1:

typedef struct A 
{
	char a; //1+3
	int b;  //4
	char c; //1+3
}s_A;
typedef struct B   
{
	char a;  //1
	char c;  //1+2
	int b;   //4
}s_B;

void main()
{
	cout<<sizeof(A)<<endl; //12
	cout<<sizeof(B)<<endl; //8
}

例2:

#pragma pack(1)
typedef struct A 
{
	char a; //1
	int b;  //4
	char c; //1
}s_A;
typedef struct B   
{
	char a;  //1
	char c;  //1
	int b;   //4
}s_B;

void main()
{
	cout<<sizeof(A)<<endl;//6
	cout<<sizeof(B)<<endl;//6
}

例1例2对比就是 第二个例子为程序设置了字节对齐大小。其遵守规则的四个概念。

例3 结构体包含无名结构体问题

typedef struct A 
{
	char a; //  1+3
	struct 
	{
		char b; //1+3
		int c;  //4
		long d; //4  结构体内部是4字节对齐方式
	};
	short e;    //2+2
}s_A;
#pragma pack(2)
typedef struct A 
{
	char a; //  1+1
	struct 
	{
		char b; //1+1
		int c;  //4
		long d; //4  
	};
	short e;    //2
}s_A;

例3中两个对比。当程序设置对齐方式后,结构体内部的结构体本来的对齐大小应该是4,但是由于设置的为2,选择里面小的,所以外面的成员以2字节对齐。

例4 结构体包含有名结构体问题

typedef struct A 
{
	char a; //  1+1
	struct t
	{
		char b; 
		int c;  
		long d; 
	};
	short e; // 2
}s_A;
//结果为4,里面的结构t不在A中存。所以没有他的大小

例5 结构体中有数组问题

typedef struct A 
{
	char a; //  1+7
	struct 
	{
		char b; //1+7
		double c[7];  //8 *7
		long d; //4+4  结构体内部是4字节对齐方式
	};
	short e;    //2+6
}s_A;
//结果为88  字节对齐是以其类型大小对齐,不是所占空间大小。
#pragma pack(4)
typedef struct A 
{
	char a; //  1+3
	struct 
	{
		char b; //1+3
		double c[7];  //8 *7
		long d; //4  结构体内部是4字节对齐方式
	};
	short e;    //2+2
}s_A;
//结果为72

例6 加入柔性数组。

typedef struct A 
{
	char a; //1+1
	short e;//2
	char ar[0]; //为柔性数组,不占结构体中空间
}s_A;

柔性数组 :char ar[0];等价于char *p;
柔性数组的好处:1.释放空间的时候不易造成内存泄漏 一般很有可能只会释放掉结构体的空间,而忘记释放结构体中指针申请的空间从而导致的内存泄漏。
                             2.不占结构体空间。

它的用法就是在为结构体开辟空间时,连同柔性数组的空间大小一同开辟。此时释放空间只需释放结构体空间即可。
缺点:空间大小在创建时固定,不易变化。

例7 位域

typedef struct A 
{
	char a:1; //1+1
	short e:2;//2 按照结构体最大的类型对齐 
}s_A;
//结果为4
typedef struct B   
{
	char a :1; //
	char c :3; //1
	char b :5; //1
}s_B;
//结果为2

位域中对齐的原则:不跨字节存储 不跨类型存储

A结构体中 char int类型不同,所以不会存在同一个字节。

B结构体中 类型都相同,但是由于a和c 分别占了1位和3位,所以一个字节还剩余了4位,不够b的存储,所以b存储到新的一个字节。

例8 联合体union 字节对齐

union A 
{
	char a; 
	short e; //2
};
//结果为2

联合体union 结构中占空间最大的一个就是联合体的大小,联合体的空间是共用的。

例9 枚举类型enum

enum A 
{
	ADD, 
	SUB,
	MUL,
	DIV
};
// 大小为4

enum A 
{
	ADD, 
	SUB =100,
	MUL,
	DIV
};
//ADD此时的值为0,MUL为101
//大小为4

枚举类型结构体(同时只有一个存在)
不赋值 默认从0开始。后面的成员永远是前面一个+1;

例10 C++中的类

基础的东西也都是一样的。

class B
{
private:
	int x; //4+4
	double e; //8
};
class A: public B
{
private:
	char b; //1+3 
	int a;  //4
	double c; //8
};
//最后的大小为32

     2.继承中,派生类会把基类的成员变量都继承过来。(不论是怎么样的继承,私有保护公开) 

  1. 当基类与派生类拥有同名的对象时,大小也不会改变。

  

class B
{
	virtual void func(){cout<<"this is father."<<endl;}
private:
	int x;
	double c; 
};
class A: public B
{
private:
	char b;
	int a;
	double c;
};
//基类大小为 24
//派生类大小为40
//增加了虚函数后会有一个虚指针,指向了一个存有虚函数地址的表大小为4个字节,也要参与字节对齐。

还有一种情况是虚继承,此时还会有一个指针指向了一个虚基类表。可以参考虚函数与虚继承不同之处分析。

有静态变量时,不在类的空间中,因为静态变量是所有对象共享的。

六.C++11中关键字alignas,alignof

alignof用于获取取指定表达式指定的(类似sizeof,可以直接是类型名)的对齐(alignment)。alignas用于声明时指定对齐类似于现有的类型。和sizeof类似,两者的操作数都不被求值。

alignas改变一个变量的对齐值:  alignas(16) int a; // 也就是说a不在以4字节对齐。而是以16字节对齐。但是只影响他一个。

alignof获取一个类型的对齐值。sizeof是求大小。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值