为什么填充必须是 2 的幂?

2023-12-30

我正在做一些示例程序来探索 C,并且想知道为什么结构填充只能以 2 的幂来完成。

#include <stdio.h>

#pragma pack(push, 3)

union aaaa
{

   struct bbb
   {
      int a;
      double b;
      char c;
   }xx;

   float f;
};

#pragma pack(pop)

int main()
{

printf("\n Size: %d", sizeof(union aaaa));

return 0;
}

编译时

warning: alignment must be a small power of two, not 3 [-Wpragmas]
warning: #pragma pack (pop) encountered without matching #pragma pack (push) [-Wpragmas]

看来#pragma 没有效果。输出只有24。即 4 字节对齐。


简短的回答是,处理器中的基本对象的大小为 2 的小幂(例如,1、2、4、8 和 16 字节),并且内存按大小为 2 的小幂(例如,8)的组进行组织。字节),因此结构必须对齐才能很好地适应这些大小。

长的答案是,其原因是基于物理学和初等数学的。计算机自然地使用值为 0 和 1 的位。这是因为设计在两个值之间切换的物理事物很容易:高电压和低电压、存在电荷或不存在电荷等等。区分三个值比较困难,因为您必须对值之间的转换更加敏感。因此,随着计算机技术几十年来的发展,我们已经使用位(二进制数字)来代替三位数字等替代品。

为了得到更大的数字,我们组合多个位。因此两个位组合起来可以有四个值。三位可以有八个值,依此类推。在较旧的计算机中,有时位一次分为六个或十个。然而,八个变得很常见,并且现在基本上是标准的。使用八位作为一个字节并不像我描述的其他一些分组那样具有强大的物理原因,但这是世界的方式。

计算机的另一个特点是内存。一旦我们有了这些字节,我们希望将它们存储在处理器可以轻松访问的设备中,这样我们就可以快速地将大量字节传入和传出处理器。当我们有很多字节时,我们需要一种方法让处理器告诉内存处理器想要读取或写入哪些字节。因此处理器需要一种对字节进行寻址的方法。

处理器使用位来表示值,因此它将使用位来表示地址值。因此,存储器将被构建为接受位来指示处理器读取时向处理器提供哪些字节或处理器写入时存储哪些字节。存储设备如何处理这些位?一件简单的事情是使用一位来控制存储器路径的一个开关。内存将由许多存储字节的小部分组成。

考虑存储设备中可以存储字节的事物,并考虑其中两个彼此相邻的事物,例如 A 和 B。我们可以使用开关来选择是否希望 A 字节处于活动状态或 B 字节处于活动状态。积极点。现在考虑其中的四件事,即A、B、C和D。我们可以使用一个开关来选择是使用A-B组还是使用C-D组。然后另一个开关选择 A 或 B(如果使用 A-B 组)或 C 或 D(如果使用 C-D)组。

此过程继续进行:内存地址中的每一位选择要使用的一组存储单元。 1 位在 2 个存储单元之间选择,2 位在 4 个存储单元之间选择,3 位在 8 个存储单元之间选择,4 位在 16 个存储单元之间选择,依此类推。 8 位在 256 个存储单元之间选择,24 位在 16,777,216 个存储单元之间选择,32 位在 4,294,967,296 个存储单元之间选择。

还有一个复杂的情况。在处理器和内存之间移动单个字节的速度很慢。相反,现代计算机将内存组织成更大的块,例如八个字节。您一次只能在内存和处理器之间移动八个字节。当处理器请求内存提供一些数据时,处理器仅发送地址的高位 - 低三位选择八个字节内的各个字节,并且它们不发送到内存。

这样速度更快,因为处理器获取 8 个字节所需的时间是内存完成所有切换以提供一个字节所花费的时间,而且它更便宜,因为您不需要区分各个字节所需的大量额外开关。内存中的字节。

然而,现在这意味着处理器无法从内存中获取单个字节。当您执行访问单个字节的指令时,处理器必须从内存中读取八个字节,然后在处理器内部移动这些字节以获得所需的一个字节。同样,要获取两个或四个字节,处理器会读取八个字节并仅提取所需的字节。

为了简化这个过程,处理器设计者指定数据应该以某种方式对齐。通常,它们要求将两字节数据(例如 16 位整数)与两个字节的倍数对齐,将四字节数据(例如 32 位整数和 32 位浮点值)与四的倍数对齐字节,以及要与八字节的倍数对齐的八字节数据。

这种必要的对齐有两个效果。首先,由于四字节数据只能从内存读取的八字节块中的两个位置开始(开头或中间),因此处理器设计者只需放置电线即可从两个位置提取四个字节。如果允许任何对齐,他们不需要添加所有额外的连线来从可能作为起始位置的八个单独字节中的任何一个中提取四个字节。 (有些处理器将完全禁止加载未对齐的数据,而有些处理器将允许它,但使用缓慢的方法来提取数据,这些方法使用较少的线路,但使用迭代算法在多个处理器周期内移动数据,因此未对齐的加载速度很慢。)

第二个效果是,由于四字节数据只能在八字节块中的两个位置开始,因此它也在该块内结束。考虑一下如果您尝试加载从八字节块的第六字节开始的四个字节的数据,会发生什么情况。前两个字节位于该块中,但接下来的两个字节位于内存中的下一个块中。处理器必须从内存中读取两个块,从每个块中取出不同的字节,然后将这些字节放在一起。这比只读取一大块要慢得多。

因此,内存是按 2 的幂组织的,因为这是位的自然结果,并且处理器需要对齐,因为这使得内存访问更加高效。对齐自然是 2 的幂,这就是为什么当结构大小是用于对齐的 2 的幂的倍数时效果更好。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么填充必须是 2 的幂? 的相关文章

  • C# 打印问题(RichTextBox)

    我想打印我的 RichTextBox eintragRichTextBox 的内容 我现在有这个代码 private void druckenPictureBox Click object sender EventArgs e PrintD
  • 将 new 与 decltype 一起使用

    T t T is an implementation detail t new T want to avoid naming T to allow for flexibility t new decltype t error cannot
  • 在路由mvc 4中添加公司名称

    我一直在尝试为 Facebook 等用户提供在 URL 中添加公司名称的选项 http localhost 50753 MyCompany Login 我尝试过不同的网址 但没有成功 routes MapRoute name Default
  • C++ 长 switch 语句还是用地图查找?

    在我的 C 应用程序中 我有一些值充当代表其他值的代码 为了翻译代码 我一直在争论使用 switch 语句还是 stl 映射 开关看起来像这样 int code int value switch code case 1 value 10 b
  • 检测wlan是否关闭

    任何人都可以给我一个提示 如何在 Windows Phone 上以编程方式检测 C 8 1 应用程序 不是 8 0 是否启用 禁用 WLAN 我不想更改这些设置 只是需要知道 该解决方案是一个 Windows 8 1 通用应用程序 Wind
  • 在现代 C++ 中,临时生命周期延长何时有用?

    在 C 中 您可以将函数的返回值 返回值 而不是引用 绑定到 const 引用 并且代码仍然有效 因为该临时对象的生命周期将延长到作用域末尾 例如 std string get string return abc void f const
  • 将完整模板参数值映射到原始类型

    我想将数字映射到类型 在这个例子中 我将创建一个函数 将 sizeof 结果映射到有符号的原始类型 我想知道是否有更好的方法来完成我在现代 C 中所做的事情 即采用模板化值并将其转换为类型 现在 这可以将大小转换为已知类型 但我似乎无法在标
  • std::call_once 可重入且线程安全吗?

    std call once http en cppreference com w cpp thread call once是线程安全的 但它也是可重入的吗 我使用 VS2012 调试和发布 进行的测试表明 调用std call once从单
  • 如何使用 SOAP 且不使用 WSE 在 .NET 中签署 Amazon Web 服务请求

    亚马逊产品广告 API 以前称为 Amazon Associates Web Service 或 Amazon AWS 实施了一项新规则 即自 2009 年 8 月 15 日起 向其发送的所有 Web 服务请求都必须经过签名 他们在其网站上
  • 是否存在指向不同类型的指针具有不同大小的平台?

    C 标准允许指向不同类型的指针具有不同的大小 例如sizeof char sizeof int 是允许的 但是 它确实要求如果将指针转换为void 然后转换回其原始类型 它必须与其原始值进行比较 因此 从逻辑上来说 sizeof void
  • 检测到堆栈崩溃

    我正在执行我的 a out 文件 执行后 程序运行一段时间 然后退出并显示消息 stack smashing detected a out terminated Backtrace lib tls i686 cmov libc so 6 f
  • 在开关中使用“goto”?

    我看到了一个建议的编码标准 内容如下Never use goto unless in a switch statement fall through 我不跟 这个 例外 案例到底是什么样的 这证明了goto 此构造在 C 中是非法的 swi
  • 访问 ascx 文件中的母版页控件

    我有一个母版页文件 其中包含 2 个面板控件中的 2 个菜单 我还使用控件来检查用户是否登录并获取用户类型 根据我想要显示 隐藏面板的类型 控件本身不在母版页中引用 而是通过 CMS 系统动态引用 我想在用户控件中使用findcontrol
  • 增强精神、递归和堆栈溢出

    为什么下面的代码在运行时崩溃 它会给出堆栈溢出错误 include
  • 如何从 Rx Subscribe 回调异步函数?

    我想回调 Rx 订阅中的异步函数 例如 像那样 public class Consumer private readonly Service service new Service public ReplaySubject
  • .NET 4 的条件编译[重复]

    这个问题在这里已经有答案了 可能的重复 条件编译和框架目标 https stackoverflow com questions 2923210 c sharp conditional compilation and framework ta
  • CUDA 8 编译错误 -std=gnu++11

    我正在尝试转换一些代码以使用 CUDA 并且我认为我遇到了兼容性问题 我们使用CMake 这些是我使用的 gcc 和 CUDA 版本 gcc version gcc Ubuntu 5 4 0 6ubuntu1 16 04 5 5 4 0 2
  • 在 C#.NET 中安全删除文件

    在我正在做的一个项目中 我想为用户提供 安全 删除文件的选项 例如 用随机位或 0 覆盖它 在 C NET 中是否有一种简单的方法可以做到这一点 效果如何 你可以调用系统内部删除 http technet microsoft com en
  • 如何将 SQL“LIKE”与 LINQ to Entities 结合使用?

    我有一个文本框 允许用户指定搜索字符串 包括通配符 例如 Joh Johnson mit ack on 在使用 LINQ to Entities 之前 我有一个存储过程 该存储过程将该字符串作为参数并执行以下操作 SELECT FROM T
  • 使用未分配的局部变量

    我遇到了一个错误 尽管声明了变量 failturetext 和 userName 错误仍然出现 谁能帮帮我吗 Use of Unassigned local variable FailureText Use of Unassigned lo

随机推荐

  • 架构数组路径的值无效

    我正在尝试构建评论模型 其中包含 Reply 和 CommentThread CommentThread包含Reply 而Reply可以递归自身 models comment js var mongoose require mongoose
  • SWIG python初始化一个指向NULL的指针

    在处理 SWIG 模块时 是否可以从 python 端将 ptr 初始化为 NULL 例如 假设我已将结构体 track t 包装在 swig 模块 m m so 中 我可以从 python 创建一个指向该结构体的指针 如下所示 impor
  • 如何编辑 Visual Studio 的鼠标快捷键?

    过去 只要我点击鼠标上的后退按钮 Visual Studio 就会执行 Naviagte Backward 命令 但它最近停止了这样做 如何编辑 Visual Studio 的鼠标快捷方式设置以重新启用此功能 编辑 开始赏金 仍然没有得到对
  • JavaScript 对象作为哈希值?复杂度是否大于 O(1)?

    对于我最近编写的某些算法 我认为哈希会非常好 我认为我可以只使用对象中的成员变量作为键值对 我不确定这是否是最佳的 因为我真的不知道幕后发生了什么 我还认为 V8 的做法与其他环境不同 不过 我确实认为查找成员变量会非常快 希望如此 综上所
  • 在 JSON.stringify 中转义单引号

    我知道 我不需要它 但我想我确实需要 我想要一个有效的 JS 代码handlebarsjs before它甚至被渲染了 JS 缩小工具仅缩小有效的 JS 文件 并且动态缩小会增加处理时间 这是 JS 车把代码 var stringsObj
  • 如何将字节数组中的 pcm 样本转换为 -1.0 到 1.0 范围内的浮点数?

    我使用的重采样算法期望float包含范围内输入样本的数组 1 0 至 1 0 音频数据是16 bitPCM 与采样率22khz 我想将音频从 22khz 降采样到 8khz 如何将字节数组中的样本表示为浮点数 gt 1 且 并返回字节数组
  • 如何扩展现有的自定义元素?

    我有一个自定义元素 称为x foo 我想扩展它并创建一个x foo extended元素 但它不起作用 我收到此错误 未捕获的 NotSupportedError 无法在 文档 上执行 registerElement 类型注册失败 x fo
  • 如何单击并拖动某些内容而不取消选择

    该程序是一个在屏幕上创建汽车和 或卡车图标的动画 我现在的方式无法正常工作 具体来说 该程序没有单击并向右拖动 如果某个对象未被选中 则单击后 该对象会变粗以表明该对象已被选中 从那里我们希望能够拖动它 无论鼠标走到哪里 程序都会重新绘制图
  • WinForms 中的 C# OpenGL

    我必须编写一个简单的程序 使用 OpenGL 和 C 在 Windows 窗体应用程序中绘制一些内容 你能给我一个图书馆的例子和 或者一个教程吗 我找到了这个 http sourceforge net projects csgl files
  • 针对少量条目的 Java Map 最快实现

    最快的实施方式是什么java util Map http docs oracle com javase 7 docs api java util Map html对于非常少量的条目 少于 15 个元素左右 分为线程安全和非线程安全 如果所有
  • 如何使用 Moq 模拟 ILogger / ILoggerService

    我正在为我的视图模型类编写一些单元测试 此类的构造函数注入了 ILoggerService 该接口定义了 1 个 GetLog 方法 该方法返回一个 ILogger 像下面这样的地方this表示实现 ILoggable 的类 protect
  • 从数据帧中提取特定的对角线[重复]

    这个问题在这里已经有答案了 我正在尝试从我的数据中获取特定的对角线 这是我的代码 a2008 c 30 50 65 a2009 c 40 90 NA a2010 c 55 NA NA mydata rbind a2008 a2009 a20
  • 在父构造函数中获取 javascript 类名或 typeof

    我有两个 Javascript 类 如下所示 class Parent constructor console log typeof this class Child extends Parent constructor super 在Pa
  • 如何在可点击的文本视图中设置超链接? Android Java [重复]

    这个问题在这里已经有答案了 我有一个带有一个 URL 的 textView 但我不想显示整个 URL 而只想显示几个单词 例如 单击此处 当单击文本视图时 应用程序需要打开 单击此处 一词 后面 的 URL 供你参考 textView 现在
  • 通过另一个 NSMutableArray 对带有自定义对象的 NSMutableArray 进行排序 [重复]

    这个问题在这里已经有答案了 我有2个NSMutableArrays 第一个数组包含具有属性的自定义对象实例NSString itemID 第二个数组仅包含来自NSString具有相同值的对象itemID 但以另一种顺序 我需要对第一个数组进
  • 如何从两个整数中随机选择一个? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 假设我想从两个数字集中随机选择1 and 3 我该如何去做呢 我只是分配吗int a to 1 int b to 3 然后随机选择a
  • Angular 2 版本 (2.0.1) Internet Explorer (SystemJS) 语法错误

    我有一个 Angular 2 Release 2 0 1 应用程序 它在 Chrome 中运行良好 但在 Internet Explorer 11 中抛出以下错误 Error SystemJS Syntax error SyntaxErro
  • 标签打印机的自定义纸张尺寸(Brother QL 570)

    我有一台新的标签打印机 Brother QL 570 支持无限纸张 我的想法是 我可以通过打印所需的纸张来节省纸张 错误 打印机附带的纸张尺寸为 63 毫米 x 100 毫米和 63 毫米 x 29 毫米 以及其他一些 但我需要 63 毫米
  • 使用 lubridate 从 POSIXct 日期时间计算该月的第一天

    给定 POSIXct 日期时间 如何提取该月的第一天进行聚合 library lubridate full date lt ymd hms 2013 01 01 00 00 21 润滑 https cran r project org we
  • 为什么填充必须是 2 的幂?

    我正在做一些示例程序来探索 C 并且想知道为什么结构填充只能以 2 的幂来完成 include