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))取消优化对齐,就不会出现对齐的错位现象。
C/C++的内存对齐的更多相关文章
- C++内存对齐总结
大家都知道,C++空类的内存大小为1字节,为了保证其对象拥有彼此独立的内存地址.非空类的大小与类中非静态成员变量和虚函数表的多少有关. 而值得注意的是,类中非静态成员变量的大小与编译器内存对齐的设置有 ...
- C/C++: C++位域和内存对齐问题
1. 位域: 1. 在C中,位域可以写成这样(注:位域的数据类型一律用无符号的,纪律性). struct bitmap { unsigned a : ; unsigned b : ; unsigned ...
- C/C++ 知识点1:内存对齐
预备知识:基本类型占用字节 在32位操作系统和64位操作系统上,基本数据类型分别占多少字节呢? 32位操作系统: char : 1 int :4 short : 2 unsigned ...
- Windows+GCC下内存对齐的常见问题
结构/类对齐的声明方式 gcc和windows对于modifier/attribute的支持其实是差不多的.比如在gcc的例子中,内存对齐要写成: class X { //... } __attrib ...
- c++内存对齐
内存对齐原则: 1.数据成员对齐规则:struct, union的数据成员,第一个数据成员放在offset为0的地方,之后的数据成员的存储起始位置都是放在该数据成员大小的整数倍位置.如在32bit的机 ...
- C语言中内存对齐
今天一考研同学问我一个问题,一个结构体有一个int类型成员和一个char类型成员,问我这个结构体类型占多少个字节,我直接编个程序给他看结果.这个结构体占八个字节,咦,当时我蛮纳闷的,一个int类型四个 ...
- 内存对齐 和 sizeof小结
数据对齐(内存对齐)指该数据所在的地址必须是该数据长度的整数倍.X86CPU能直接访问对齐的数据,当它试图访问未对齐的数据时,会在内部进行一系列的调整,降低运行速度.数据对齐一般出现在结构体和类中,在 ...
- 解析C语言结构体对齐(内存对齐问题)
C语言结构体对齐也是老生常谈的话题了.基本上是面试题的必考题.内容虽然很基础,但一不小心就会弄错.写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的 ...
- C语言再学习之内存对齐
昨天看Q3的代码,看到有个_INTSAIZEOF的宏,着实晕了一阵.一番google后,终于明白,这个宏的作用是求出变量占用内存空间的大小,先看看_INTSAIZEOF的定义吧: #define _I ...
- C结构体中数据的内存对齐问题
转自:http://www.cnblogs.com/qwcbeyond/archive/2012/05/08/2490897.html 32位机一般默认4字节对齐(32位机机器字长4字节),64位机一 ...
随机推荐
- OO_BLOG3_规格化设计(JML学习)
目录 JML语言学习笔记 理论基础 应用工具链情况 JMLUnit/JMLUnitNG UNIT3 作业分析 作业 3-1 实现两个容器类Path和PathContainer 作业 3-2 实现容器类 ...
- javascript:void(0)的含义
void关键字介绍 首先,void关键字是javascript当中非常重要的关键字,该操作符指定要计算或运行一个表达式,但是不返回值. 语法格式: void func() void(func()) 实 ...
- FreePascal - CodeTyphon 和 Lazarus, 如何像Delphi一样有代码之间的连线?
CodeTyphon 6.9 默认CodeTyphon没有开启代码之间连线功能,一直不知道如何调出来,在使用CodeTyphon 和 Lazarus 很渴望也能像Delphi那样有这种功能,实际上它们 ...
- android中实现service动态更新UI界面
案例:通过service向远程服务器发送请求,根据服务器返回的结果动态更新主程序UI界面,主程序可实时关闭或重启服务. 注册BroadcastReceiver 在主程序activity中注册一个Bro ...
- Docker 0x02: Docker生态
目录 Docker生态 Docker官网 0x00 网址 Docker组件 0x01. docker-client 与 docker-daemon 0x02. docker镜像 0x03. docke ...
- SpringBoot + Nginx 配置HTTPS的一次经历
最近公司开发了一款小程序的应用,但是小程序为了保证数据安全,强制要求使用HTTPS,然后就不得不去配置了一下. 之前在php开发的项目上配置过一次,使用的是wdcp的控制台程序,配置起来很简单,不需要 ...
- zabbix--邮件告警报错“Support for SMTP authentication was not compiled in”
zabbix 邮件告警报错“Support for SMTP authentication was not compiled in” 邮件报警失败:Support for SMTP authentic ...
- 黄金矿工(LeetCode Medium难度)1129题 题解(DFS)
题目描述: 给定一个二维网络,给定任意起点与终点.每一步可以往4个方向走.要找出黄金最多的一条线路. 很明显的是要“一条路走到黑,一直下去直到某个条件停止”. 运用dfs(深度优先搜索)求解. 因为起 ...
- 在linux系统中如何通过shell脚本批量设置redis键值对
业务逻辑:批量设置redis中手机号的验证码为888888: 准备shell脚本如下:将18888888100~18888888110的手机号验证码设置为888888: #!/bin/bash ;i& ...
- git---如何解决The authenticity of host can't be established.
新生成密钥的时候,git clone或者push的时候,可能会报这样的错误: ``` The authenticity of host 'gitee.com (xxx.xxx.xxx.xxx)' ca ...