C/C++的内存对齐

 

1、内存对齐之pragma pack语法

语法:#pragma pack( [show] | [push | pop] [, identifier], n )
作用:指定结构,联合和类的包对齐方式(pack alignment),可以通知给编译器传递预编译指令而改变对指定数据的对齐方法

举个例子如下:

 

#include <iostream>
using namespace std;

int main() {

#pragma pack(8) // 指定对齐大小为8
    struct TEST_A
    {
    	// 实际对齐大小为min(sizeof(int),8)=min(4,8)=4,编译器会确保TEST_A首地址即a的地首址是4字节对齐的,此时a对齐
        int a;
        // 实际对齐大小为min(sizeof(char),8)=min(1,8)=1,由于b要求首地址1字节对齐,这显然对于任何地址都合适,所以a,b都是对齐的
        char b;
        // 这是一个数组,数组的对齐大小与其单元一致,因而align(c)=align(double)=min(sizeof(double),8)=min(8,8)=8,
        // 由于c要求首地址8字节对齐,因此前面的a+b=5,还要在c后面补上3个字节才能对齐
        // (注意:此处不仅要确保下一个TEST_A的a,b变量对齐,还要确保c也对齐,所以这里不是填充3字节,而是填充7字节)
        double c[10];
        char d; // 实际对齐大小为min(sizeof(char),8)=min(1,8)=1,任何地址均对齐
    };
#pragma  pack()

	cout << sizeof(TEST_A) << endl; // 整个结构体的大小为(4)+(1+3)+(10*8)+(1+7)=96
	return 0;
}

再举个例子,如下:

#pragma pack(4)
	struct TEST_B
	{
		char a;
		short b;
		int c;
		float d;
		int *e;
		char *f;
		double g;
	};
#pragma pack()
	cout << sizeof(TEST_B) << endl; // 整个结构体的大小为(1+1)+(2)+(4)+(4)+(8)+(8)+(8)=36

当按4字节对齐时是36,而按8字节对齐时需要40 

 

2、基本数据类型所占用内存大小

本章的实例都运行在64bit的ubuntu系统下。 

3、静态变量static

静态变量的存放位置与结构体实例的存储地址无关,是单独存放在静态数据区的,因此用siezof计算其大小时没有将静态成员所占的空间计算进来。

 

4、类

空类是会占用内存空间的,而且大小是1,原因是C++要求每个实例在内存中都有独一无二的地址。

(一)类内部的成员变量:

  • 普通的变量要占用内存,但是要注意对齐原则(这点和struct类型很相似)。
  • static修饰的静态变量不占用内容,原因是编译器将其放在全局变量区。

(二)类内部的成员函数:

  • 普通函数不占用内存。
  • 虚函数要占用8个字节,用来指定虚函数的虚拟函数表的入口地址。所以一个类的虚函数所占用的地址是不变的,和虚函数的个数是没有关系的

 举个例子,如下:

#pragma pack(4)
	class ClassA { };
#pragma pack()
	cout << sizeof(ClassA) << endl; // 1

再举个例子,如下:  

#pragma pack(4)
	class ClassB {
	private:
		char a;
		short b;
		int c;
	public:
		void fOut() {
			cout << "hello" << endl;
		}
	};

	cout << sizeof(ClassB) << endl; // 输出8,普通函数不占用内存空间 

 当将fOut()函数加上virutal关键字变为虚函数时,输出为16,再添加新的虚函数时,分配的内存空间也不会增加。

 

5、子类

子类所占内存大小是父类+自身成员变量的值。特别注意的是,子类与父类共享同一个虚函数指针,因此当子类新声明一个虚函数时,不必再保存虚函数表指针入口。 

#pragma pack(4)
	class BaseClass {
	private:
		char a;
		short b;
		int c;
	public:
		virtual void fOut() {
			cout << "virtual 1" << endl;
		}
	};

	class SubClass: public BaseClass {
	private:
		int n;
	public:
		virtual void fPut() {
			cout << "virtual 2";
		}
	};

	cout << sizeof(SubClass) << endl; // 输出20

  

扩展:

不想要字节对齐的时候,有没有办法取消字节对齐?答案是可以,就是在结构体声明当中,加上__attribute__ ((__packed__))关键字,它可以做到让我们的结构体,按照紧凑排列的方式,占用内存。来段实际代码:

#include <stdio.h>
#include <iostream>

using namespace std;

struct test1 {
char c;
int i;
};

struct __attribute__ ((__packed__)) test2 {
char c;
int i;
};

int main()
{
cout << "size of test1:" << sizeof(struct test1) << endl; // 输出结果为8
cout << "size of test2:" << sizeof(struct test2) << endl; // 输出结果为5
} 

test1结构体里面没有加关键字,它采用了4字节对齐的方式,即使是一个char变量,也占用了4字节内存,int占用4字节,共占用了8字节内存,这在64位机器当中将会更大。
而test2结构体,再加上关键字之后,结构体内的变量采用内存紧凑的方式排列,char类型占用1字节,int占用4字节,总共占用了5个字节的内存。
数据结构的对齐的问题。为了让我们的数据结构以最优的方式存储,处理,保证读写数据结构都一一对齐,我们往往采用3种方式:

1.程序作者,手动对齐,将数据按从小到大的顺序排列,尽量凑齐。

2.使用#pragma pack (n)来指定数据结构的对齐值。

3.使用 __attribute__ ((packed)) ,让编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,这样子两边都需要使用 __attribute__ ((packed))取消优化对齐,就不会出现对齐的错位现象。

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/mazhimazhi/p/11333820.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值