c++中的对齐问题
c++中的对齐问题
需要对齐的原因
尽管内存是以字节为单位,但是大部分处理器并不是按字节块来存取内存的.它一般会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存,我们将上述这些存取单位称为内存存取粒度.
现在考虑4字节存取粒度的处理器取int类型变量(32位系统),该处理器只能从地址为4的倍数的内存开始读取数据。
假如没有内存对齐机制,数据可以任意存放,现在一个int变量存放在从地址1开始的联系四个字节地址中,该处理器去取数据时,要先从0地址开始读取第一个4字节块,剔除不想要的字节(0地址),然后从地址4开始读取下一个4字节块,同样剔除不要的数据(5,6,7地址),最后留下的两块数据合并放入寄存器.这需要做很多工作.

对齐的规则
有效对齐值:是 #pragma pack(n)和结构体中最长数据类型长度中较小的那个。有效对齐值也叫对齐单位。
注意:
#pragma pack(n)中的n可以取(1 , 2 , 4 , 8 , 16)中的任意一值。
2)规则:
-
结构体变量的首地址是有效对齐值(对齐单位)的整数倍。
-
结构体第一个成员的偏移量(offset)为0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节。
-
结构体的总大小为有效对齐值的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
-
结构体内类型相同的连续元素将在连续的空间内,和数组一样。
运用上面的规则,下面通过实际的例子进行计算。
例1:
#include <iostream>struct MyStruct {char c;int i;short s;
};int main()
{MyStruct obj;std::cout << "start addr of obj = " << (void*)&obj << std::endl;std::cout << "offset of c = " << offsetof(MyStruct,c) << std::endl;std::cout << "offset of i = " << offsetof(MyStruct,i) << std::endl;std::cout << "offset of s = " << offsetof(MyStruct,s) << std::endl;std::cout << "sizeof MyStruct = " << sizeof(MyStruct);
}
执行结果如下:
start of obj = 0x7fff2e8d1e94
offset of c = 0
offset of i = 4
offset of s = 8
sizeof MyStruct = 12
结构中最长的数据类型是int,长度也为4。因此结构体的有效对齐值是4。
对于c变量而言,没有悬念,将排在0偏移地址处。
对于变量i,类型为int,长度为4,int和有效对齐值的最小值为4,因此i需要排布在4的整数倍上,因此第一个符合要求的偏移量就是4。
对于变量s,类型为short,长度为2,short和有效对齐值二者中的最小值为2,第一个符合要求的地址为8。
到目前为止,使用的空间大小是10,而结构体大小需要满足有效对齐值的整数倍,因此需要2个填充,因此结构体最终大小是12。

例2:
#include <iostream>
#pragma pack(2)
struct MyStruct {char c;int i;short s;
};int main()
{MyStruct obj;std::cout << "start addr of obj = " << (void*)&obj << std::endl;std::cout << "offset of c = " << offsetof(MyStruct,c) << std::endl;std::cout << "offset of i = " << offsetof(MyStruct,i) << std::endl;std::cout << "offset of s = " << offsetof(MyStruct,s) << std::endl;std::cout << "sizeof MyStruct = " << sizeof(MyStruct);
}
执行结果如下:
start addr of obj = 0x7fff488e3418
offset of c = 0
offset of i = 2
offset of s = 6
sizeof MyStruct = 8
首先#pragma pack设置的对齐值是2,结构中最长的数据类型是int,长度也为4。因此结构体的有效对齐值是2。
对于c变量而言,没有悬念,将排在0偏移地址处。
对于变量i,类型为int,长度为4,int和有效对齐值的最小值为2,因此i需要排布在2的整数倍上,因此第一个符合要求的偏移量就是2。
对于变量s,类型为short,长度为2,short和有效对齐值二者中的最小值为2,第一个符合要求的地址为6。
到目前为止,使用的空间大小是8,已经满足结构体大小是有效对齐值的整数倍的要求。

#include <iostream>
#pragma pack(1)
struct MyStruct {char c;int i;short s;
};int main()
{MyStruct obj;std::cout << "start addr of obj = " << (void*)&obj << std::endl;std::cout << "offset of c = " << offsetof(MyStruct,c) << std::endl;std::cout << "offset of i = " << offsetof(MyStruct,i) << std::endl;std::cout << "offset of s = " << offsetof(MyStruct,s) << std::endl;std::cout << "sizeof MyStruct = " << sizeof(MyStruct);
}
执行结果如下:
start addr of obj = 0x7ffe96c067a9
offset of c = 0
offset of i = 1
offset of s = 5
sizeof MyStruct = 7
首先#pragma pack设置的对齐值是1,结构中最长的数据类型是int,长度也为4。因此结构体的有效对齐值是1。
对于c变量而言,没有悬念,将排在0偏移地址处。
对于变量i,类型为int,长度为4,int和有效对齐值的最小值为,因此i需要排布在2的整数倍上,因此第一个符合要求的偏移量就是1。
对于变量s,类型为short,长度为2,short和有效对齐值二者中的最小值为2,第一个符合要求的地址为5。
到目前为止,使用的空间大小是7,已经满足结构体大小是有效对齐值的整数倍的要求。

例4:
#include <iostream>
#include <emmintrin.h>struct MyStruct {char c;__m128i i;
};int main()
{MyStruct obj;std::cout << "start addr of obj = " << (void*)&obj << std::endl;std::cout << "offset of c = " << offsetof(MyStruct,c) << std::endl;std::cout << "offset of i = " << offsetof(MyStruct,i) << std::endl;std::cout << "sizeof MyStruct = " << sizeof(MyStruct);
}
执行结果如下:
start addr of obj = 0x7fff9d47cd90
offset of c = 0
offset of i = 16
sizeof MyStruct = 32
首先,结构中最长的数据类型是__m128i,长度为16。因此结构体的有效对齐值是16。
对于c变量而言,没有悬念,将排在0偏移地址处。
对于变量i,类型为__m128i,长度为16,__m128i和有效对齐值的最小值为16,因此i需要排布在2的整数倍上,因此第一个符合要求的偏移量就是16。

例5:
#include <iostream>
#include <emmintrin.h>#pragma pack(8)
struct MyStruct {char c;__m128i i;
};int main()
{MyStruct obj;std::cout << "start addr of obj = " << (void*)&obj << std::endl;std::cout << "offset of c = " << offsetof(MyStruct,c) << std::endl;std::cout << "offset of i = " << offsetof(MyStruct,i) << std::endl;std::cout << "sizeof MyStruct = " << sizeof(MyStruct);
}
执行结果如下:
start addr of obj = 0x7ffddbec2c40
offset of c = 0
offset of i = 8
sizeof MyStruct = 24
首先#pragma pack设置的对齐值是8,结构中最长的数据类型是__m128i,长度为16。因此结构体的有效对齐值是8。
对于c变量而言,没有悬念,将排在0偏移地址处。
对于变量i,类型为__m128i,长度为16,__m128i和有效对齐值的最小值为8,因此i需要排布在2的整数倍上,因此第一个符合要求的偏移量就是8。

总结
- 为了高效的访问内存数据,通常需要对内存数据进行对齐。
#pragma pack(n)用于设置的对齐有效值,如果设置比结构体的最长成员还大的对齐值将是无效的。
相关文章:
c++中的对齐问题
c中的对齐问题 需要对齐的原因 尽管内存是以字节为单位,但是大部分处理器并不是按字节块来存取内存的.它一般会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存,我们将上述这些存取单位称为内存存取粒度. 现在考虑4字节存取粒度的处理器取in…...
力扣(LeetCode)算法_C++—— 存在重复元素
给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。 示例 1: 输入:nums [1,2,3,1] 输出:true 示例 2: 输入:nums …...
OpenCV实现Photoshop曲线调整
《QT 插件化图像算法研究平台》有仿Photoshop曲线调整图像的功能,包括RGB曲线调整和HSV曲线调整。 Photoshop曲线调整原理:RGB、HSV各通道曲线,可以理解为一个值映射(值转换)函数。X轴是输入,Y轴是输出。x0…...
【探索Linux】—— 强大的命令行工具 P.8(进程优先级、环境变量)
阅读导航 前言一、进程优先级1. 优先级概念2. Linux查看系统进程3. PRI(Priority)和NI(Nice) 二、环境变量1. 概念2. 查看环境变量方法3. 环境变量的组织方式4.通过代码获取环境变量5. 环境变量的特点 总结温馨提示 前言 前面我们…...
蓝牙协议栈BLE
前言 这阵子用到蓝牙比较多,想写一个专栏专门讲解蓝牙协议及其应用,本篇是第一篇文章,讲解低功耗蓝牙和蓝牙协议栈。 参考网上各大神文章,及瑞萨的文章,参考GPT,并且加入了一些本人的理解。 图片部分源自…...
企业架构LNMP学习笔记17
反向代理: 反向代理服务器和真实访问的服务器是在一起的,有关联的。 根据实际业务需求,分发代理页面到不同的解释器。常见于代理后端服务器。 安装apache服务器: yum install -y httpd 修改配置文件: vim /et/http…...
php 获取每月开始结束时间,指定月份的开始结束时间戳
php 获取指定月份的开始结束时间戳。 /** * * 获取指定年月的开始和结束时间戳 * param int $year 年份 * param int $month 月份 * return array(开始时间,结束时间) */ function getMonthBeginAndEnd($year 0, $month 0) {$year $year ? $year : date(Y);$month $month…...
Docker技术入门| Part03:Dockerfile详解(Dockerfile概念、Dockerfile 指令、使用Dockerfile构建镜像)
文章目录 1. Dockerfile概念2. Dockerfile 指令FROM 指定基础镜像RUN执行命令CMD 容器启动命令COPY 复制文件ADD 更高级的复制文件ENV 设置环境变量ARG 构建参数VOLUME 定义匿名卷EXPOSE 暴露端口WORKDIR 指定工作目录USER 指定当前用户LABEL 为镜像添加元数据SHELL 指令 3. 使…...
分享一个有意思的线程相关的程序运行题
翻开之前的代码,发现了一个有意思的代码,猜以下代码的运行结果: package thread;/*** author heyunlin* version 1.0*/ public class ThreadMethodExample {public static void main(String[] args) {Thread thread new Thread(new Runnabl…...
集合的进阶学习
集合体系结构 Collection 单列集合 包含List Set List 包含ArrayList LinkedList Set包含HashSet TreeSet HashSet包含LinkedHashSet List系列集合:添加的元素是有序的、可重复、有索引 Set系列集合:添加的元素是无序的、不重复、无索引 Collectio…...
Java真过饱和了吗?现在学Java迟了?
Java行业内幕揭秘 我是某有名机构的线下课Java老师,负责Java热门框架教学,如Spring、Spring MVC、Spring Boot。但最近被解雇了,让我来吐槽一下。Java现在的学习人数真的太多太多了。 Java的学习饱和度 Java学习的人太多,给你一…...
glibc2.35-通过tls_dtor_list劫持exit执行流程
前言 glibc2.35删除了malloc_hook、free_hook以及realloc_hook,通过劫持这三个hook函数执行system已经不可行了。 传统堆漏洞利用是利用任意地址写改上上述几个hook从而执行system,在移除之后则需要找到同样只需要修改某个地址值并且能够造成程序流劫持…...
linux-OpenSSL升级
1.安装编译所需的安装包 yum install -y gcc make perl zlib-devel 2.从 OpenSSL 官网下载(https://www.openssl.org/source/openssl-1.1.1v.tar.gz) 注:如果原先版本为1.x.x,升级时还是需要选择1.x.x 3. 编译安装 # 解压tar -xvf openssl-1.1.1v.tar…...
Nginx全家桶配置详解
源码包安装NGINX A,搭建Web Server,任意HTML页面,其8080端口提供Web访问服务,截图成功访问http(s)://[Server1]:8080并且回显Web页面。保留Server1,但是不允许直接访问Server 1,再部署1套NGINX …...
CMake生成Visual Studio工程
CMake – 生成Visual Studio工程 C/C项目经常使用CMake构建工具。CMake 项目文件(例如 CMakeLists.txt)可以直接由 Visual Studio 使用。本文要说明的是如何将CMake项目转换到Visual Studio解决方案(.sln)或项目(.vcxproj) 开发环境 为了生成Visual S…...
数学建模--K-means聚类的Python实现
目录 1.算法流程简介 2.1.K-mean算法核心代码 2.2.K-mean算法效果展示 3.1.肘部法算法核心代码 3.2.肘部法算法效果展示 1.算法流程简介 #k-means聚类方法 """ k-means聚类算法流程: 1.K-mean均值聚类的方法就是先随机选择k个对象作为初始聚类中心. 2.这…...
防坠安全带上亚马逊美国站要求的合规标准是什么?
防坠安全带 防坠安全带是一种防护装备,适合工人在高空作业时或在可能发生跌落的无防护边缘行走时穿着。防坠安全带设计用于包裹身体躯干,并将坠落力至少分布到大腿上部、骨盆、胸部和肩部。防坠安全带是固定物体与非固定物体之间的连接物,通…...
PDF转Word的方法分享与注意事项。
PDF和Word是两种常用的文档格式,它们各有优点,适用于不同的场景。然而,有时候我们需要将PDF转换为Word,以便更好地进行编辑和排版。本文将介绍几种常用的PDF转Word的方法,并分享一些注意事项。 一、PDF转Word的方法 使…...
gitlab配置webhook,commit message的时候校验提交的信息
在 GitLab 中配置 Webhook 来调用 Java 接口以校验 commit 信息,是很多公司的一些要求,因为提交信息的规范化是必要的 在 GitLab 项目中进入设置页面。 在左侧导航栏中选择 “Webhooks”(Web钩子)。 在 Webhooks 页面中点击 “…...
借助CIFAR10模型结构理解卷积神经网络及Sequential的使用
CIFAR10模型搭建 CIFAR10模型结构 0. input : 332x32,3通道32x32的图片 --> 特征图(Feature maps) : 3232x32即经过32个35x5的卷积层,输出尺寸没有变化(有x个特征图即有x个卷积核。卷积核的通道数与输入的通道数相等,即35x5&am…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
【WebSocket】SpringBoot项目中使用WebSocket
1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖,添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...
LUA+Reids实现库存秒杀预扣减 记录流水 以及自己的思考
目录 lua脚本 记录流水 记录流水的作用 流水什么时候删除 我们在做库存扣减的时候,显示基于Lua脚本和Redis实现的预扣减 这样可以在秒杀扣减的时候保证操作的原子性和高效性 lua脚本 // ... 已有代码 ...Overridepublic InventoryResponse decrease(Inventor…...
嵌入式面试常问问题
以下内容面向嵌入式/系统方向的初学者与面试备考者,全面梳理了以下几大板块,并在每个板块末尾列出常见的面试问答思路,帮助你既能夯实基础,又能应对面试挑战。 一、TCP/IP 协议 1.1 TCP/IP 五层模型概述 链路层(Link Layer) 包括网卡驱动、以太网、Wi‑Fi、PPP 等。负责…...
统计学(第8版)——统计抽样学习笔记(考试用)
一、统计抽样的核心内容与问题 研究内容 从总体中科学抽取样本的方法利用样本数据推断总体特征(均值、比率、总量)控制抽样误差与非抽样误差 解决的核心问题 在成本约束下,用少量样本准确推断总体特征量化估计结果的可靠性(置…...
