uint8_t占用多少内存?

How much memory does a uint8_t occupy?

我对使用 MSVC 编译器时 uint8_t 占用了多少感到有点困惑。另外,我有点熟悉结构填充的概念,这样的内存对齐以实现高效 read/writes。但是,我的测试显示了一些奇怪的结果。我定义了三个结构:

第一个特征是 uint8 和 int32。我希望这会占用 8 个字节,因为 int32 必须是字对齐的,从而强制添加 3 个填充字节。我的假设是正确的。

第二个具有单个 uint8。我本以为它会占用 4 个字节.. 但它只占用 1 个字节。这让我很困惑。

第三个(也是最令人困惑的一个)具有一个 int32 后跟一个 uint8。使用 struct 2 中的逻辑,其中一个单独的 uint8 占用一个字节,我会假设这个结构占用 5 个字节。但它占用8个字节。这种是有道理的,但是如果struct 2只占用1个字节就没有意义了。

uint8实际占用了多少space?

typedef struct StructOne
{
    uint8_t member1;
    int32_t member2;
    
} StructOne;

typedef struct StructTwo
{
    uint8_t member1;

} StructTwo;

typedef struct StructThree
{
    int32_t member2;
    uint8_t member1;

} StructThree;


int main(int argc, char* args[])
{
    size_t size_struct_one = sizeof(StructOne);
    size_t size_struct_two = sizeof(StructTwo);
    size_t size_struct_three = sizeof(StructThree);

    printf("Size of StructOne = %u\n", sizeof(StructOne));
    printf("Size of StructTwo = %u\n", sizeof(StructTwo));
    printf("Size of StructThree = %u\n", sizeof(StructThree));
    
    return 0;
    
}

uint8_t 是一个字节(在提供它的实现中,这是大多数)。

但是您缺少 struct 布局规则:struct 的大小必须是其所需对齐方式的倍数。因为正如你所说 int32_t 需要 4 字节对齐,因此 struct StructThree 也是如此,所以即使它的成员适合 5 个字节,它也会被填充到 8.

要了解原因,假设您有一个数组 struct StructThree arr[10];。保证这个数组的元素是连续放置的,所以如果 sizeof(struct StructThree) 只有 5,那么 arr[1] 必须在 arr[0] 之后刚好 5 个字节开始,这会破坏它的对齐。 (对于普通的 struct StructThree 指针算法,它们是否连续并不重要,但如果你开始逐字节处理它们就很重要,就像 memcpy 等一样)

标准东西

为了补充 Nate Eldredge 的出色答案,值得认识到 struct 不是底层 CPU 所理解的实体。这是 C 语言标准为方便程序员而定义的东西(结构是表达“所有这些数据项都属于一起”的一种非常方便的方式)。编译器必须为 CPU 生成操作码以处理语言标准定义的结构。

需要对齐的地方是 CPUs 通常对变量在内存中的存储方式很挑剔。出于速度原因,32 位 CPU 要求 32 位整数的地址为 4 字节对齐的情况并不少见,如果整数要由“添加”操作码引用的话。

但是,通常可以在计算机内部移动字节。因此,没有什么可以阻止编译器生成将整数存储在未与 4 字节对齐的地址处的代码,并在对其执行操作之前将其变戏法以将其放入寄存器中。

编译器通常不这样做的原因是它很慢。语言标准编写者理解这一点,因此他们定义了结构的行为,以便允许编译器生成快速代码。通常,大多数编译器都有 #pragma 语句,允许您告诉编译器将内容打包,而不是将它们展开以获得最佳速度,如果那是真正想要的。

字寻址

好的,到目前为止一切顺利,我们处于标准知识的范围内。问题是,有些计算机类型没有打包结构的选项,其中内存不是字节寻址的。

今天几乎所有的计算机都逐字节寻址内存,虽然它们的微电子设备一次会从内存加载 4、8 个字节,但它们的指令集允许对单个字节进行寻址。回到几十年前,情况并非如此;旧的 Crays,Prime 大型机没有字节寻址,而是字寻址。所以机器没有选择做“未对齐”的商店;首先没有字节具有地址的概念。这种机器上的最小分配是1个字。

所以在这样的机器上,你的 Struct2 将是 1 个字的大小,而不是一个字节,而 sizeof(Struct2) 将 return 字长(2 或 4,可能).

C 已经足够老了,因此它是相关的,因此它是标准中讨论的问题。

其他语言

其他语言非常抽象,甚至不让程序员知道数据是如何存储在内存中的。例如,Java 或 C# 中的 class 会将内容存储在内存中,但是语言中没有任何内容(AFAIK)告诉您这是如何完成的,成员在内存中的顺序是什么,如何他们很大,或任何东西。这使得高级语言和低级语言(例如 C/C++ )之间的互操作有点棘手;因此,所有编组工作都必须在 C# 中完成。