对于类与结构体的字节对齐以及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是求大小。

<think>嗯,用户的问题是关于C/C++结构体字节对齐的作用范围,想知道对齐规则是针对单个元素还是整个结构体。我需要结合用户提供的引用资料来回答。首先,用户引用了四个资料,分别提到了结构体定义、alignof函数、结构体之间的对齐以及嵌套结构体对齐规则。 首先,结构体对齐确实涉及到两个层面:单个成员的对齐和整个结构体对齐。根据引用[3],结构体内部每个成员需要按照自身型的对齐要求放置,比如int通常是4字节对齐。然后,整个结构体的大小必须是其最大成员对齐值的整数倍,这样在结构体数组中每个元素才能正确对齐。例如,如果结构体的最大成员是double(8字节对齐),那么结构体总大小必须是8的倍数,即使内部成员加起来不是。 另外,嵌套结构体的情况,引用[4]提到,如果结构体B包含结构体A,那么A的起始地址必须是A中最大成员对齐值的整数倍。例如,如果A的最大成员是4字节对齐,那么B中A的成员要从4的倍数地址开始。 需要解释清楚这两个层面的规则,并且举例说明。比如,给出一个结构体的例子,计算其大小,说明各个成员的对齐和整个结构体的填充。同时,可能用户还会关心如何查看对齐值,可以用alignof函数,如引用[2]中的例子。 需要确保回答结构清晰,分点说明单个成员和整体结构体的规则,再结合例子和引用资料。最后生成相关问题,可能涉及优化内存布局、调整对齐方式的方法,或者实际应用中的问题。</think>### 结构体字节对齐规则(单个成员 vs 整体结构体) 在C/C++中,**结构体字节对齐规则同时作用于单个成员和整体结构体**,但作用范围和目的不同。以下是具体规则的分层说明: --- #### 一、单个成员的对齐规则 1. **成员起始地址要求** 每个成员的起始地址必须是其型大小或编译器默认对齐值的整数倍。例如: - `char`型(1字节)可以从任意地址开始; - `int`型(4字节)必须从4的倍数地址开始; - `double`型(8字节)必须从8的倍数地址开始[^1][^3]。 ```c++ struct Example1 { char a; // 地址0(1字节对齐) int b; // 地址4(跳过1-3字节,满足4字节对齐) double c; // 地址8(满足8字节对齐) }; ``` 2. **填充字节插入** 如果成员间因对齐要求产生空隙,编译器会自动插入填充字节(Padding)[^3]。 --- #### 二、整体结构体对齐规则 1. **结构体总大小要求** 结构体的总大小必须是其所有成员中最大对齐值的整数倍。例如: - 若最大成员为`double`(8字节对齐),则结构体总大小必须是8的倍数[^3]。 ```c++ struct Example2 { char a; // 地址0 // 填充3字节(地址1-3) int b; // 地址4 double c; // 地址8 }; // 总大小=16字节(满足8的倍数) ``` 2. **结构体数组对齐要求** 在结构体数组中,每个元素的起始地址必须满足结构体自身的对齐需求,因此结构体总大小必须符合上述规则。 --- #### 三、嵌套结构体的特殊规则 若结构体包含其他结构体成员,**被嵌套结构体的起始地址需满足其内部最大成员的对齐要求**。例如: ```c++ struct Inner { char a; // 地址0 double b; // 地址8(跳过1-7字节) }; // 总大小=16字节(最大对齐值8) struct Outer { int x; // 地址0 Inner y; // 地址8(满足Inner的最大对齐值8) }; // 总大小=24字节(最大对齐值8) ``` --- #### 四、验证对齐的实践方法 1. **使用`alignof`获取对齐值** 可通过C++的`alignof`函数查看结构体或成员的对齐要求[^2]: ```c++ std::cout << alignof(Example1) << std::endl; // 输出8(由double决定) ``` 2. **计算内存布局** 手动分析成员地址和填充字节,例如: ```c++ struct Test { char a; // 地址0 // 填充3字节 int b; // 地址4 char c; // 地址8 // 填充7字节(若结构体最大对齐值为8) }; // 总大小=16字节(假设编译器优化) ``` --- ### 作用范围总结 1. **单个成员**:对齐规则确保数据访问效率,避免硬件异常(如未对齐访问导致崩溃)。 2. **整体结构体**:对齐规则保证结构体数组的连续内存布局合法性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值