内存对齐--面试常问问题和笔试常考问题

1.内存对齐的意义

C++ 内存对齐的主要意义可以简练概括为以下几点:

  1. 提高访问效率:内存对齐可以使数据在内存中以更加紧凑的方式存储,从而提高了数据的访问效率。处理器通常能够更快地访问内存中对齐的数据,而不需要额外的字节偏移计算。

  2. 硬件要求:许多硬件平台要求数据按照一定的对齐规则存储,否则可能会导致性能下降或者错误。不符合硬件要求的数据存储方式可能会引发总线错误或性能降低。

  3. 结构体和类的正确性:在C++中,结构体和类中的成员变量通常按照编译器的默认对齐方式进行排列,以确保数据的正确访问和存储。手动调整对齐方式可以保证数据的正确性。

  4. 跨平台开发:内存对齐可以确保数据在不同平台上的一致性。这对于跨平台开发非常重要,因为不同的硬件架构可能有不同的对齐要求。

  5. 节省内存:内存对齐可以减少内存碎片,从而节省内存空间。当数据按照对齐要求存储时,不会出现因为填充字节而浪费内存的情况。

总之,C++ 内存对齐的主要意义在于提高访问效率、符合硬件要求、确保数据的正确性、支持跨平台开发以及节省内存空间。通过遵循对齐规则,可以充分利用硬件的性能优势,并确保程序在不同平台上的可移植性和正确性。

2.对齐原则


原则1 :数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在
offiset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身
长度中,比较小的那个进行。(先偏移到那个对齐标准数的指定倍数,在进行加上本身)
原则2:结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进
行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的
那个进行。
原则3 :结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元
素大小的整数倍地址开始存储。

3.默认对齐值

默认对齐值:
Linux默认#pragma pack(4)
window默认#pragma pack(8)
注:可以通过预编译命令#pragma pack(n) , n=1,2,4,8,16来改变这一系数,其中的n就是指定
的“对齐系数”。

例题1:

#include <iostream>
#include <vector>
#include <algorithm>
#pragma pack(1)
using namespace std;struct AA
{int a;   //长度4>1 按照1对齐,偏移量为0,存放位置区间[0,3]char b;  //长度1==1 按1 对齐,偏移量为4,存放位置区间[4]short c; //长度2>1,按1对齐,偏移量提升到2的 倍数6,存放位置区间[5,6]char d;  //长度1==1,按1 对齐,偏移量为6,存放位置区间[7]//整体存放【0-7】 位置中,共八个字节。
};int main()
{cout << sizeof(AA) << endl;system("pause");return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#pragma pack(2)
using namespace std;struct AA
{int a;   //长度4>1 按照1对齐,偏移量为0,存放位置区间[0,3]char b;  //长度1==1 按1 对齐,偏移量为4,存放位置区间[4]short c; //长度2>1,按1对齐,偏移量,存放位置区间[5,6]char d;  //长度1==1,按1 对齐,偏移量为6,存放位置区间[7]//整体存放【0-7】 位置中,共八个字节。
};struct BB
{// BB 是注释掉以上 内容 按照2个字节对齐的情况int a;   //长度4>2 按照2对齐,偏移量为0,存放位置区间[0,3]char b;  //长度2>1 按1 对齐,偏移量为4,存放位置区间[4]short c; //长度2=1,按2对齐,整体偏移到2的倍数6,存放位置区间[6,7]char d;  //长度2>1,按1对齐,偏移量为7,存放位置区间[8],共9个字节。//整体存放【0-8】 位置中,共9个字节。
};int main()
{// cout << sizeof(AA) << endl;cout << sizeof(BB) << endl;system("pause");return 0;
}

  

 

#include <iostream>
#include <vector>
#include <algorithm>
#pragma pack(4)
using namespace std;struct AA
{int a;   //长度4>1 按照1对齐,偏移量为0,存放位置区间[0,3]char b;  //长度1==1 按1 对齐,偏移量为4,存放位置区间[4]short c; //长度2>1,按1对齐,偏移量,存放位置区间[5,6]char d;  //长度1==1,按1 对齐,偏移量为6,存放位置区间[7]//整体存放【0-7】 位置中,共八个字节。
};struct BB
{// BB 是注释掉以上 内容 按照2个字节对齐的情况int a;   //长度4>2 按照2对齐,偏移量为0,存放位置区间[0,3]char b;  //长度2>1 按1 对齐,偏移量为4,存放位置区间[4]short c; //长度2=1,按2对齐,整体偏移到2的倍数6,存放位置区间[6,7]char d;  //长度2>1,按1对齐,偏移量为7,存放位置区间[8],共9个字节。//整体存放【0-8】 位置中,共9个字节。
};struct CC
{int a;   //长度4=4 按照4对齐,偏移量为0,存放位置区间[0,3]char b;  //长度4>1 按照1对齐,偏移量为4,存放区间[4]short c; //长度 2<4 按照两个字节对齐,对齐到2的倍数6 存放位置{6,7}char d;  // 1<4 ,按照1 对齐。偏移量为7.存放位置的区间为[8],总大小为9
};int main()
{// cout << sizeof(AA) << endl;// cout << sizeof(BB) << endl;cout << sizeof(CC) << endl;system("pause");return 0;
}

 

8字节对齐

#include <iostream>
#include <vector>
#include <algorithm>
#pragma pack(8)
using namespace std;struct AA
{int a;   //长度4>1 按照1对齐,偏移量为0,存放位置区间[0,3]char b;  //长度1==1 按1 对齐,偏移量为4,存放位置区间[4]short c; //长度2>1,按1对齐,偏移量,存放位置区间[5,6]char d;  //长度1==1,按1 对齐,偏移量为6,存放位置区间[7]//整体存放【0-7】 位置中,共八个字节。
};struct BB
{// BB 是注释掉以上 内容 按照2个字节对齐的情况int a;   //长度4>2 按照2对齐,偏移量为0,存放位置区间[0,3]char b;  //长度2>1 按1 对齐,偏移量为4,存放位置区间[4]short c; //长度2=1,按2对齐,整体偏移到2的倍数6,存放位置区间[6,7]char d;  //长度2>1,按1对齐,偏移量为7,存放位置区间[8],共9个字节。//整体存放【0-8】 位置中,共9个字节。
};struct CC
{int a;   //长度4=4 按照4对齐,偏移量为0,存放位置区间[0,3]char b;  //长度4>1 按照1对齐,偏移量为4,存放区间[4]short c; //长度 2<4 按照两个字节对齐,对齐到2的倍数6 存放位置{6,7}char d;  // 1<4 ,按照1 对齐。偏移量为7.存放位置的区间为[8],总大小为9
};struct DD
{int a;   // 4<8  按4对齐, 偏移:0 位置 {0,3}char b;  // 1<8 按1对齐 偏移:4 位置:{4}short c; // 2<8 按照2 对齐 偏移 6  位置 {6,7}char d;  // 1<8  按照1 对齐 偏移为7 位置[8] 总大小为9
};int main()
{// cout << sizeof(AA) << endl;// cout << sizeof(BB) << endl;// cout << sizeof(CC) << endl;cout << sizeof(DD) << endl;system("pause");return 0;
}

#按照8位,有 包含 double的情况。

#include <iostream>
#include <vector>
#include <algorithm>
#pragma pack(8)
#include <stddef.h>
using namespace std;struct AA
{int a;   //长度4>1 按照1对齐,偏移量为0,存放位置区间[0,3]char b;  //长度1==1 按1 对齐,偏移量为4,存放位置区间[4]short c; //长度2>1,按1对齐,偏移量,存放位置区间[5,6]char d;  //长度1==1,按1 对齐,偏移量为6,存放位置区间[7]//整体存放【0-7】 位置中,共八个字节。
};struct BB
{// BB 是注释掉以上 内容 按照2个字节对齐的情况int a;   //长度4>2 按照2对齐,偏移量为0,存放位置区间[0,3]char b;  //长度2>1 按1 对齐,偏移量为4,存放位置区间[4]short c; //长度2=1,按2对齐,整体偏移到2的倍数6,存放位置区间[6,7]char d;  //长度2>1,按1对齐,偏移量为7,存放位置区间[8],共9个字节。//整体存放【0-8】 位置中,共9个字节。
};struct CC
{int a;   //长度4=4 按照4对齐,偏移量为0,存放位置区间[0,3]char b;  //长度4>1 按照1对齐,偏移量为4,存放区间[4]short c; //长度 2<4 按照两个字节对齐,对齐到2的倍数6 存放位置{6,7}char d;  // 1<4 ,按照1 对齐。偏移量为7.存放位置的区间为[8],总大小为9
};struct DD
{int a;   // 4<8  按4对齐, 偏移:0 位置 {0,3}char b;  // 1<8 按1对齐 偏移:4 位置:{4}short c; // 2<8 按照2 对齐 偏移 6  位置 {6,7}char d;  // 1<8  按照1 对齐 偏移为7 位置[8] 总大小为9
};struct EE
{int a;    // 4<8  按4对齐, 偏移:0 位置 {0,3}double b; // 8==8 按8对齐 偏移量偏移到8的倍数 偏移8 位置:{8,16}short c;  // 2<8 按照2 对齐 偏移 16  位置 {16,17}char d;   // 1<8  按照1 对齐 偏移为17 位置[18] 总大小为9
};int main()
{// cout << sizeof(AA) << endl;// cout << sizeof(BB) << endl;// cout << sizeof(CC) << endl;// cout << sizeof(DD) << endl;cout << sizeof(EE) << endl;cout << offsetof(EE, b) << endl; //查看偏移了多少内存。system("pause");return 0;
}

 例四:结构体包含结构体的运算。

#include <iostream>
#include <vector>
#include <algorithm>
#pragma pack(8)
#include <stddef.h>
using namespace std;struct AA
{int a;   //长度4>1 按照1对齐,偏移量为0,存放位置区间[0,3]char b;  //长度1==1 按1 对齐,偏移量为4,存放位置区间[4]short c; //长度2>1,按1对齐,偏移量,存放位置区间[5,6]char d;  //长度1==1,按1 对齐,偏移量为6,存放位置区间[7]//整体存放【0-7】 位置中,共八个字节。
};struct BB
{// BB 是注释掉以上 内容 按照2个字节对齐的情况int a;   //长度4>2 按照2对齐,偏移量为0,存放位置区间[0,3]char b;  //长度2>1 按1 对齐,偏移量为4,存放位置区间[4]short c; //长度2=1,按2对齐,整体偏移到2的倍数6,存放位置区间[6,7]char d;  //长度2>1,按1对齐,偏移量为7,存放位置区间[8],共9个字节。//整体存放【0-8】 位置中,共9个字节。
};struct CC
{int a;   //长度4=4 按照4对齐,偏移量为0,存放位置区间[0,3]char b;  //长度4>1 按照1对齐,偏移量为4,存放区间[4]short c; //长度 2<4 按照两个字节对齐,对齐到2的倍数6 存放位置{6,7}char d;  // 1<4 ,按照1 对齐。偏移量为7.存放位置的区间为[8],总大小为9
};struct DD
{int a;   // 4<8  按4对齐, 偏移:0 位置 {0,3}char b;  // 1<8 按1对齐 偏移:4 位置:{4}short c; // 2<8 按照2 对齐 偏移 6  位置 {6,7}char d;  // 1<8  按照1 对齐 偏移为7 位置[8] 总大小为9
};struct EE
{int a;    // 4<8  按4对齐, 偏移:0 位置 {0,3}double b; // 8==8 按8对齐 偏移量偏移到8的倍数 偏移8 位置:{8,16}short c;  // 2<8 按照2 对齐 偏移 16  位置 {16,17}char d;   // 1<8  按照1 对齐 偏移为17 位置[18] 总大小为9
};//=============================================================
struct GG
{//结构体内部最大元素为int.由于偏移量8刚好是4 的倍数,所以从8 开始存放struct 对应了规则三。int a1;   // 4<8   4  8   [8,11]char b1;  // 1<8  1  12  [12]short c1; // 2<8   2  14  [14,15]char d1;  // 1 <8   1  16  [16]
};
struct FF
{int a;   // 4<8 按4   偏移0 存放的位置【0,3】char b;  // 1<8 按1   偏移4           [4]short c; // 2<8  2      6            [6,7]GG g;//子strcut整体对齐系数=min((max(int,short,char),8))=4. 将内存补齐到4 的整数倍 20.char d; // 1<8  1   21  21//整体对齐系数 4 所有有21 补到24.
};
//===========================================
int main()
{// cout << sizeof(AA) << endl;// cout << sizeof(BB) << endl;// cout << sizeof(CC) << endl;// cout << sizeof(DD) << endl;// cout << sizeof(EE) << endl;// cout << offsetof(EE, b) << endl; //查看偏移了多少内存。cout << sizeof(FF) << endl;// cout << offsetof(FF, GG) << endl;system("pause");return 0;
}

 

 

 4.练习:

        

#include <iostream>
#include <vector>
#include <algorithm>
#pragma pack(8)
using namespace std;struct A
{int a; // [0,4]double b;// [ 8, 16 ]float c;// [ 17, 20 ]
};
// #24struct B
{char e[2]; // 1<8 按照2对齐  偏移 0  位置【0,1】short h;   // 2<8 2             2       [2,4]A a; // 24// 一共28 偏移一起达到32.
};int main()
{cout << sizeof(B) << endl;system("pause");return 0;
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/142032.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

oracle

title: “Oracle” createTime: 2021-12-13T16:35:4108:00 updateTime: 2021-12-13T16:35:4108:00 draft: false author: “name” tags: [“oracle”] categories: [“db”] description: “测试的” 时间字段分析 timestamp 精确到秒后面6位 createTime: 2021-12-13T16:35:…

vue3基础

141.用vite创建vue3项目 142.项目目录 vue3中&#xff0c;直接导入组件就能用 不再要求唯一根元素 //createApp(App)是创建实例&#xff0c;.mount(#app)是在将实例往id为app的盒子上挂载 createApp(App).mount(#app)//挂载就是让实例接管一片区域 assets是存放图片或样式的…

uniapp——实现二维码生成+保存二维码图片——基础积累

最近在做二维码推广功能&#xff0c;自从2020年下半年到今天&#xff0c;大概有三年没有用过uniapp了&#xff0c;而且我之前用uniapp开发的程序还比较少&#xff0c;因此很多功能都浪费了很多时间去查资料&#xff0c;现在把功能记录一下。 这里写目录标题 效果图1.根据接口返…

Vue Mock.js介绍和使用与首页导航栏左侧菜单搭建

前言&#xff1a; 因为使用Vue开发&#xff0c;组件写的太多&#xff0c;组件与组件之间的传递数据复杂&#xff0c;所以要用到Mock和Bus事件 一&#xff0c;关于Mock 1.1.什么是Mock.js Mock.js是一个模拟数据的生成器&#xff0c;用来帮助前端调试开发、进行前后端的原型分离…

ctfshow 命令执行 (29-39)

学习参考的 https://www.cnblogs.com/NPFS/p/13279815.html 说的很全面 web29 命令执行&#xff0c;需要严格的过滤 源码 error_reporting(0); if(isset($_GET[c])){$c $_GET[c];if(!preg_match("/flag/i", $c)){eval($c);}}else{highlight_file(__FILE__); } …

深入浅出Java的多线程编程——第一篇

目录 1. 认识线程&#xff08;Thread&#xff09; 1.1 概念 1.1.1 线程是什么 1.1.2 为啥需要线程 1.1.3 进程和线程的区别 1.1.4 Java的线程和操作系统线程的关系 1.2 第一个多线程程序 1.3 创建线程的方式&#xff08;5种&#xff09; 1.3.1 继承Thread类 1.3.2 实现…

Spring面试题21:说一说Spring的@Required注解和@Qualifier注解

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:说一说Spring的@Required注解 @Required ,用于标记在注入的属性上。它表示被注解的属性在配置 Bean 的时候是必需的,如果没有正确配置,则会抛出…

go字符串拼接方式及性能比拼

在golang中字符串的拼接方式有多种&#xff0c;本文将会介绍比较常用的几种方式&#xff0c;并且对各种方式进行压测&#xff0c;以此来得到在不同场景下更适合使用的方案。 文章目录 1、go字符串的几种拼接方式1.1 fmt.Sprintf1.2 运算符拼接1.3 strings.Join1.4 strings.Bui…

【从0学习Solidity】45. 时间锁

【从0学习Solidity】45. 时间锁 博主简介&#xff1a;不写代码没饭吃&#xff0c;一名全栈领域的创作者&#xff0c;专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构&#xff0c;分享一些项目实战经验以及前沿技术的见解。关注我们的主页&#xff0c;探索全栈开…

关于vantUI的导航组件tab标签页在ios和安卓中运用遇到的坑

vantTab的默认值 应用场景问题描述原始代码更正代码 应用场景 根据路由传值设置默认tab页&#xff0c;获取不同的数据并进行展示 问题描述 ios可正常按照路由传值默认tab页&#xff0c;安卓始终默认tabList的第一个value值&#xff0c;疑安卓系统中不接受dataMap.tabActive为…

YOLOv5如何训练自己的数据集(生活垃圾数据集为例)

文章目录 前言1、数据标注说明2、定义自己模型文件3、训练模型参考文献 前言 本文主要介绍如何利用YOLOv5训练自己的数据集 1、数据标注说明 以生活垃圾数据集为例子 生活垃圾数据集&#xff08;YOLO版&#xff09;点击这里直接下载本文生活垃圾数据集 生活垃圾数据集组成&…

Python开发与应用实验2 | Python基础语法应用

*本文是博主对学校专业课Python各种实验的再整理与详解&#xff0c;除了代码部分和解析部分&#xff0c;一些题目还增加了拓展部分&#xff08;⭐&#xff09;。拓展部分不是实验报告中原有的内容&#xff0c;而是博主本人自己的补充&#xff0c;以方便大家额外学习、参考。 &a…

【JavaEE】多线程案例-线程池

文章目录 1. 什么是线程池2. 为什么要使用线程池&#xff08;线程池有什么优点&#xff09;3. 如何使用Java标准库提供的线程池3.1 创建一个线程池对象3.2 什么是工厂模式3.3 为什么要使用工厂模式3.4 Executors 创建不同具有不同特性的线程池3.5 ThreadPool 类的构造方法3.6 线…

C++之list成员函数应用总结(二百三十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Neo4j 与 Cypher 基础

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 简介 Neo4j 是用 Java 实现的开源 NoSQL 图数据库。从2003年开始开发&#xff0c;2007年正式发布第一版&#xff0c;其源码托管于 GitHub。 与常见的关系型数据库不同&#xff0c;Neo4j 基于图图结构来表示…

学习:原码-反码-补码

文章目录 前提知识原码详解反码补码 二进制负数的运算 前提知识 正数不需要进行原码反码补码一说&#xff0c;正数就是正数&#xff0c;我们原码反码补码是为了针对负数 &#xff08;按道理来说其实根本不存在什么码&#xff0c;只有二进制机器码&#xff0c;不过是为了方便计…

华为云云耀云服务器L实例评测|华为云上安装etcd

文章目录 华为云云耀云服务器L实例评测&#xff5c;华为云上安装etcd一、什么是etcd官方硬件建议 二、华为云主机准备三、etcd安装1. 安装预构建的二进制文件2. 从源代码构建 四、etcd服务注册与发现1. 配置etcd2. 使用systemctl 管理启动etcd服务3. 注册服务4. 发现服务 五、其…

ISE_ChipScope Pro的使用

1.ChipScope Pro Core Inserter 使用流程 在之前以及编译好的流水灯实验上进行学习 ChipScope的使用。 一、新建一个ChipScope 核 点击Next,然后在下一个框中选择 Finish&#xff0c;你就会在项目菜单中看到有XX.cdc核文件。 二、对核文件进行设置 右键“Synthesize – XST” …

最快的包管理器--pnpm创建vue项目完整步骤

1.用npm全局安装pnpm npm install -g pnpm 2.在要创建vue项目的包下进入cmd&#xff0c;输入&#xff1a; pnpm create vue 3.输入项目名字&#xff0c;选择Router,Pinia,ESLint,Prettier之后点确定 4.cd到创建好的项目 &#xff0c;安装依赖 cd .\刚创建好的项目名称\ p…

怎样快速打开github.com

1访问这个网站很慢是因为有DNS污染&#xff0c;被一些别有用心的人搞了鬼了&#xff0c; 2还有一个重要原因是不同的DNS服务器解析的速度不一样。 1 建议设置dns地址为114.114.114.114.我觉得假设一个县城如果有一个DNS服务器的话&#xff0c;这个服务器很可能不会存储…