"比特填充"或"填充位"是什么?

5
我不想用这个打扰你,但是我在互联网上找不到一个描述详细的“位补齐”(bit padding)的解释,也没有在StackOverflow的任何与位补齐相关的贴子中找到答案。
我还查阅了ISO 9899-1990文档,其中提到了“位补齐”,但并没有像我需要的那样进行解释。
我在网上找到的唯一内容是这里, 只给出了一句荒谬短小的解释,如下:
“位补齐:将一个或多个额外的位添加到传输或存储单元中,使其符合标准大小。”
一些来源将位补齐视为比特填充的一种类型。
这至少是一些信息,但对于我来说不是足够的解释。它还指称了术语"比特填充"

当我在StockOverflow上查看关于“padding”的相关标签时,描述padding为:

插入到内存结构中以实现地址对齐的额外空间 - 或 - HTML元素的框架和内容之间的额外空间 - 或 - 使用格式化打印命令(如C语言中的printf * -family函数)打印值时的额外空格或零。

背景:

我经常在数据类型相关的地方找到“位填充”这个术语,但不明白它是什么以及它究竟对这些数据类型做了什么。

非常感谢任何基于这个话题的答案。


2
"为了实现地址对齐,在内存结构中插入额外的空间。你可以更具体地问吗?我不知道该在您已发布的内容上添加什么。" - 463035818_is_not_a_number
1
我经常在数据类型相关的内容中发现“位填充”这个术语 - 你有例子吗? - 500 - Internal Server Error
1
该链接在搜索“位填充”时没有任何结果。无论如何,那个网站众所周知地充满了错误信息。 - 463035818_is_not_a_number
2
为什么会有踩?楼主显然做了研究,这个问题看起来对我来说很有用和清晰,应该被点赞。踩是给那些没有做研究、不清晰和没有用的问题的。我错过了什么吗? - 89f3a1c
显示剩余7条评论
4个回答

11

我经常在与数据类型相关的场合中找到“比特填充”的术语,但我不理解它是什么,以及它对这些数据类型的确切作用。

其本质是它们是“浪费”空间。我说“浪费”,因为虽然填充位使对象变大,但可以使使用对象变得更加容易(这意味着更快),而小的空间浪费可以产生巨大的性能提升。在某些情况下,它是必不可少的,因为CPU无法处理那么大的对象。

假设您有一个结构体如下:

struct foo
{
    short a; // 16 bits
    char  b; // 8 bits 
};

而您正在使用的机器可以在单个读取操作中读取32位数据。 读取单个foo并不是问题,因为整个对象适合于那个32位块中。 当您有一个数组时,会出现问题。 关于数组需要记住的重要事情是它们是连续的,元素之间没有空间。 这只是一个紧随其后的另一个对象。 因此,如果您有像以下这样的数组

foo array[10]{};

使用这种方法,第一个foo对象位于一个32位桶中。但是,数组的下一个元素将位于第一个32位桶和第二个32位桶中。这意味着成员a位于两个不同的桶中。一些处理器可以这样做(但需要代价),而其他处理器会在尝试执行此操作时崩溃。为了解决这两个问题,编译器会向foo添加填充位来填充其大小。这意味着实际上foo变成了

struct foo
{
    short a; // 16 bits
    char  b; // 8 bits 
    char  _; // 8 bits of padding
};

现在处理器可以很容易地单独或者在数组中处理foo对象。它不需要做任何额外的工作,你只增加了每个对象8位。在现代计算机上,您需要大量的对象才能开始关注这一点。

有时候您需要填充类型成员之间的空白以进行未对齐访问。比方说您有:

struct bar
{
    char c; // 8 bits
    int  d; // 32 bits
};

现在,bar的宽度为40位,并且d往往会被再次存储在两个不同的桶中。为了解决这个问题,编译器在cd之间添加填充位,例如:
struct bar
{
    char    c; // 8 bits
    char _[3]; // 24 bits
    int     d; // 32 bits
};

现在,d 被保证会进入一个 32 位的桶中。


'@Nathan Oliver' 非常感谢您提供的这个例子。我不知道机器是按块读取数据的。因此,逻辑上应该向该结构添加位,以使其元素更易于机器读取,当然也会在执行时带来速度优势。好的..如果我只有一个变量不适合32位的块,例如 short int a = 145;,那么如何进行位填充呢? - RobertS supports Monica Cellio
如果该成员是类的一部分,则可以添加传递。如果对象只是一个局部变量,编译器可能会决定将下一个变量放在它自己的存储桶中。很多这些都是实现定义的,因为C++模型的抽象机器并不关心。 - NathanOliver

3
假设你有一个8位的数字,它是一个 `uint8_t`,其值设置为 `4`。它可能被存储为 `a = 0000 0100`。现在,假设你想将其转换为16位数字。会发生什么?你必须给这个数字中的“新”位分配一些值。你该如何分配它们?你不能随意分配零或一,原始变量的值会改变。根据体系结构等因素,你必须使用额外的位填充值。在我的情况下,这意味着在原始 MSB(最高有效位)前添加8个零,使我们的数字变为 `a = 0000 0000 0000 0100`。
值仍然是4,但现在你可以分配 [0,2^16) 范围内的任何值,而不是 [0,2^8) 范围内的值。

2

位填充:
位填充是将一个或多个额外的位添加到传输或存储单元中,使其符合标准大小。

由于您发布的定义已经正确,我将尝试通过示例来解释:

假设您需要存储少于32位的数据,但是您有4字节插槽。通过访问每个插槽来访问该数据更容易,因此您只需完成所有32位即可。用于完成“给定空间”的附加位,但不属于数据的位组成了位填充。

我相信在多种情况下可能会有更好的例子。任何人都可以随意编辑和/或完善答案,并提供新的改进或示例。

希望这有所帮助!


虽然我看得出你非常想帮助我,而且你的风格非常优雅,对此我非常感激;-),但我还是不太明白。你说“假设你需要存储占用少于32位的数据,但你有4个字节的插槽”,这就是我理解的。但是在“通过访问每个插槽来访问数据更容易,因此您只需完成所有32位即可。”这一点上,我不太明确你的意思。1.为什么我需要单独访问int32_t变量的每个字节,我该如何做到?2.为什么我必须通过访问字节来完成所有32位? - RobertS supports Monica Cellio
我正准备回复你的评论,但是刚刚看到了Nathan的答案,他详细地解释了所有问题并提供了示例。还有什么需要补充的吗?或者那就是你一直在寻找的答案? - 89f3a1c

0

位填充可用于多种情境。两个常见的例子是网络和加密。我认为加密情境更相关。

在加密中使用填充可以使解密消息更加困难,尤其是当多个消息已知具有相同的前缀(例如“hello”)时,这会使破解密钥变得更容易。通过使用可变长度的位字段来“填充”消息,可以使破解密钥变得更加困难。

据说英国情报部门能够加速分析恩格玛密码的原因是德国人开始使用相同的标题。

如需更多技术上准确的描述,请参阅https://en.wikipedia.org/wiki/Padding_(cryptography) 中关于块密码和位填充的部分。


在内存存储中,如何使用位填充? - RobertS supports Monica Cellio
我的回答中的示例直接回答了那个问题 ;) - 89f3a1c

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接