结构体的大小是否需要是该结构体对齐方式的精确倍数?

发布于 2024-10-11 05:35:29 字数 1070 浏览 6 评论 0 原文

我再一次质疑一个长期存在的信念。

直到今天,我相信以下结构的对齐通常为 4,大小通常为 5...

struct example
{
  int   m_Assume_32_Bits;
  char  m_Assume_8_Bit_Bytes;
};

由于这个假设,我有数据结构代码,使用 offsetof 来确定中两个相邻项之间的字节距离一个数组。今天,我发现一些旧代码在不应该使用 sizeof 的地方使用了 sizeof,无法理解为什么我没有从中发现错误,编写了一个单元测试 - 测试的通过让我感到惊讶。

一些调查表明,我用于测试的类型的大小(类似于上面的结构)是对齐的精确倍数 - 即 8 个字节。它在最后一个成员之后有填充。这是为什么我从未预料到这一点的一个例子...

struct example2
{
  example m_Example;
  char    m_Why_Cant_This_Be_At_Offset_6_Bytes;
};

一些谷歌搜索显示了一些示例,这些示例清楚地表明最终成员之后的填充是允许的 - 例如 http://en.wikipedia.org/wiki/Data_struct_alignment#Data_struct_padding(“或位于结构末尾”位)。

这有点尴尬,因为我最近发表了此评论 - Use of struct padding(我对该答案的第一条评论)。

我似乎无法确定这种填充到对齐的精确倍数是否由 C++ 标准保证,或者它是否只是允许的并且某些(但可能不是全部)编译器会这样做。

那么 - 根据 C++ 标准,结构体的大小是否需要是该结构体对齐方式的精确倍数?

如果 C 标准做出不同的保证,我也对此感兴趣,但重点是 C++。

Once again, I'm questioning a longstanding belief.

Until today, I believed that the alignment of the following struct would normally be 4 and the size would normally be 5...

struct example
{
  int   m_Assume_32_Bits;
  char  m_Assume_8_Bit_Bytes;
};

Because of this assumption, I have data structure code that uses offsetof to determine the distance in bytes between two adjacent items in an array. Today, I spotted some old code that was using sizeof where it shouldn't, couldn't understand why I hadn't had bugs from it, coded up a unit test - and the test surprised me by passing.

A bit of investigation showed that the sizeof the type I used for the test (similar to the struct above) was an exact multiple of the alignment - ie 8 bytes. It had padding after the final member. Here is an example of why I never expected this...

struct example2
{
  example m_Example;
  char    m_Why_Cant_This_Be_At_Offset_6_Bytes;
};

A bit of Googling showed examples that make it clear that this padding after the final member is allowed - for example http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding (the "or at the end of the structure" bit).

This is a bit embarrassing, as I recently posted this comment - Use of struct padding (my first comment to that answer).

What I can't seem to determine is whether this padding to an exact multiple of the alignment is guaranteed by the C++ standard, or whether it is just something that is permitted and that some (but maybe not all) compilers do.

So - is the size of a struct required to be an exact multiple of the alignment of that struct according to the C++ standard?

If the C standard makes different guarantees, I'm interested in that too, but the focus is on C++.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

尛丟丟 2024-10-18 05:35:29

5.3.3/2

当应用于类时,[sizeof] 的结果是该类的对象中的字节数,包括将该类型的对象放入数组中所需的任何填充。

所以是的,对象大小是其对齐方式的倍数。

5.3.3/2

When applied to a class, the result [of sizeof] is the number of bytes in an object of that class, including any padding required for placing objects of that type in an array.

So yes, object size is a multiple of its alignment.

尐偏执 2024-10-18 05:35:29

alignment 大小的一种定义:

结构体的 alignment 大小是当您拥有该结构体的数组时从一个元素到下一个元素的偏移量。

就其本质而言,如果您有一个包含两个元素的结构体数组,那么两个元素都需要具有对齐的成员,因此这意味着是的,大小必须是对齐的倍数。 (我不确定是否有任何标准明确强制执行此操作,但因为结构的大小和对齐方式不取决于该结构是单独的还是在数组内,所以相同的规则适用于两者,所以它不能真正可以是任何其他方式。)

One definition of alignment size:

The alignment size of a struct is the offset from one element to the next element when you have an array of that struct.

By its nature, if you have an array of a struct with two elements, then both need to have aligned members, so that means that yes, the size has to be a multiple of the alignment. (I'm not sure if any standard explicitly enforce this, but because the size and alignment of a struct don't depend on whether the struct is alone or inside an array, the same rules apply to both, so it can't really be any other way.)

孤城病女 2024-10-18 05:35:29

标准说([dcl.array] 部分:

数组类型的对象包含连续分配的非空 N 个类型为 T 的子对象。

因此数组元素之间没有填充。

标准不要求结构内部填充,但标准不允许任何其他方式对齐数组元素。

The standard says (section [dcl.array]:

An object of array type contains a contiguously allocated non-empty set of N subobjects of type T.

Therefore there is no padding between array elements.

Padding inside structures is not required by the standard, but the standard doesn't permit any other way of aligning array elements.

仅冇旳回忆 2024-10-18 05:35:29

C++ 没有明确这么说,但它是其他两个要求的结果:

首先,所有 对象必须良好对齐。

3.8/1 说

T 类型的对象的生命周期开始于 [...]具有正确对齐方式的存储以及 T 类型的大小获得

,并且 3.9 /5:

对象类型具有*对齐要求(3.9.1、3.9.2)。完整对象类型的对齐是一个实现定义的表示字节数的整数值;对象被分配到满足其对象类型对齐要求的地址。

因此每个对象都必须根据其对齐要求进行对齐。

另一个要求是数组中的对象是连续分配的:

8.3.4/1:

数组类型的对象包含连续分配的非空集 NT 类型的子对象。

对于连续分配的数组中的对象,它们之间不能有填充。但为了使数组中的每个对象正确对齐,必须填充每个单独的对象,以便紧邻对象末尾之后的字节也正确对齐。换句话说,对象的大小必须是其对齐方式的倍数。

C++ doesn't explicitly says so, but it is a consequence of two other requirements:

First, all objects must be well-aligned.

3.8/1 says

The lifetime of an object of type T begins when [...] storage with the proper alignment and size for type T is obtained

and 3.9/5:

Object types have *alignnment requirements (3.9.1, 3.9.2). The alignment of a complete object type is an implementation-defined integer value representing a number of bytes; an object is allocated at an address that meets the alignment requirements of its object type.

So every object must be aligned according to its alignment requirements.

The other requirement is that objects in an array are allocated contigulously:

8.3.4/1:

An object of array type contains a contiguously allocated non-empty set of N subobjects of type T.

For the objects in an array to be contiguously allocated, there can be no padding between them. But for every object in the array to be properly aligned, each individual object must be padded so that the byte immediately after the end of the object is also well aligned. In other words, the size of the object must be a multiple of its alignment.

弃爱 2024-10-18 05:35:29

我不确定这是否在实际的 C/C++ 标准中,我倾向于说这取决于编译器(只是为了安全起见)。然而,几个月前,我度过了一段“有趣”的时光,当时我必须将动态生成的 C 结构作为字节数组作为协议的一部分通过网络发送,以便与芯片进行通信。所有结构的对齐和大小必须与芯片上运行的代码中的结构一致,该代码是使用 MIPS 架构的 GCC 变体编译的。我将尝试给出算法,它应该适用于 gcc 的所有变体(希望也适用于大多数其他编译器)。

所有基本类型,如 charshortint 都与其大小对齐,并且它们对齐到下一个可用位置,无论父级的对齐。回答最初的问题,是的,总大小是对齐的倍数。

// size 8
struct {
    char A; //byte 0
    char B; //byte 1
    int C; //byte 4
};

尽管结构的对齐方式是 4 个字节,但字符仍然尽可能紧密地排列在一起。

结构体的对齐方式等于其成员的最大对齐方式

示例:

//size 4, but alignment is 2!
struct foo {
    char A; //byte 0
    char B; //byte 1
    short C; //byte 3
}

//size 6
struct bar {
    char A;         //byte 0
    struct foo B;   //byte 2
}

这也适用于工会,而且以一种奇怪的方式。联合的大小可以大于其成员的任何大小,这仅仅是由于对齐:

//size 3, alignment 1
struct foo {
    char A; //byte 0
    char B; //byte 1
    char C; //byte 2
};

//size 2, alignment 2
struct bar {
    short A; //byte 0
};

//size 4! alignment 2
union foobar {
    struct foo A;
    struct bar B;
}

使用这些简单的规则,您应该能够计算出任何可怕的嵌套联合的对齐/大小/struct 你遇到过。这全都是凭记忆,所以如果我错过了无法从这些规则中决定的极端情况,请告诉我!

I am unsure if this is in the actual C/C++ standard, and I am inclined to say that it is up to the compiler (just to be on the safe side). However, I had a "fun" time figuring that out a few months ago, where I had to send dynamically generated C structs as byte arrays across a network as part of a protocol, to communicate with a chip. The alignment and size of all the structs had to be consistent with the structs in the code running on the chip, which was compiled with a variant of GCC for the MIPS architecture. I'll attempt to give the algorithm, and it should apply to all variants of gcc (and hopefully most other compilers).

All base types, like char, short and int align to their size, and they align to the next available position, regardless of the alignment of the parent. And to answer the original question, yes the total size is a multiple of the alignment.

// size 8
struct {
    char A; //byte 0
    char B; //byte 1
    int C; //byte 4
};

Even though the alignment of the struct is 4 bytes, the chars are still packed as close as possible.

The alignment of a struct is equal to the largest alignment of its members.

Example:

//size 4, but alignment is 2!
struct foo {
    char A; //byte 0
    char B; //byte 1
    short C; //byte 3
}

//size 6
struct bar {
    char A;         //byte 0
    struct foo B;   //byte 2
}

This also applies to unions, and in a curious way. The size of a union can be larger than any of the sizes of its members, simply due to alignment:

//size 3, alignment 1
struct foo {
    char A; //byte 0
    char B; //byte 1
    char C; //byte 2
};

//size 2, alignment 2
struct bar {
    short A; //byte 0
};

//size 4! alignment 2
union foobar {
    struct foo A;
    struct bar B;
}

Using these simple rules, you should be able to figure out the alignment/size of any horribly nested union/struct you come across. This is all from memory, so if I have missed a corner case that can't be decided from these rules please let me know!

等往事风中吹 2024-10-18 05:35:29

可以生成一个 C 或 C++ typedef,其对齐方式不是其大小的倍数。这是最近在 这个bindgen错误。这是一个最小的示例,我将在下面将其称为 test.c

#include <stdio.h>
#include <stdalign.h>

__attribute__ ((aligned(4))) typedef struct {
    char x[3];
} WeirdType;

int main() {
    printf("sizeof(WeirdType) = %ld\n", sizeof(WeirdType));
    printf("alignof(WeirdType) = %ld\n", alignof(WeirdType));
    return 0;
}

在我的 Arch Linux x86_64 机器上,gcc -dumpversion && gcc test.c && ./a.out 打印:

9.3.0
sizeof(WeirdType) = 3
alignof(WeirdType) = 4

类似地 clang -dumpversion && clang test.c && ./a.out 打印:

9.0.1
sizeof(WeirdType) = 3
alignof(WeirdType) = 4

将文件保存为 test.cc 并使用 g++/clang++ 给出相同的结果。 (几年后更新:我从 GCC 11.1.0 和 Clang 13.0.0 得到了相同的结果。)

但是值得注意的是,Windows 上的 MSVC 似乎没有重现任何类似的行为。

It is possible to produce a C or C++ typedef whose alignment is not a multiple of its size. This came up recently in this bindgen bug. Here's a minimal example, which I'll call test.c below:

#include <stdio.h>
#include <stdalign.h>

__attribute__ ((aligned(4))) typedef struct {
    char x[3];
} WeirdType;

int main() {
    printf("sizeof(WeirdType) = %ld\n", sizeof(WeirdType));
    printf("alignof(WeirdType) = %ld\n", alignof(WeirdType));
    return 0;
}

On my Arch Linux x86_64 machine, gcc -dumpversion && gcc test.c && ./a.out prints:

9.3.0
sizeof(WeirdType) = 3
alignof(WeirdType) = 4

Similarly clang -dumpversion && clang test.c && ./a.out prints:

9.0.1
sizeof(WeirdType) = 3
alignof(WeirdType) = 4

Saving the file as test.cc and using g++/clang++ gives the same result. (Update from a couple years later: I get the same results from GCC 11.1.0 and Clang 13.0.0.)

Notably however, MSVC on Windows does not seem to reproduce any behavior like this.

宛菡 2024-10-18 05:35:29

因此,将您的问题分为两个:

1。合法吗?

[5.3.3.2] 当应用于类时,[sizeof() 运算符]的结果是该类的对象中的字节数,包括所需的任何填充将该类型的对象放入数组中。

所以,不,不是。

2.那么,为什么不是呢?

在这里,我只能推测。

2.1。指针算术变得更奇怪
如果对齐是“在数组元素之间”但不会影响大小,则 zthigns 会变得不必要的复杂,例如

(char *)(X+1) != ((char *)X) + sizeof(X)

(我有一种预感,即使没有上述声明,标准也隐含地要求这样做,但我无法证明它)

2.2 简单性
如果对齐影响大小,则可以通过查看单个类型来决定对齐和大小。考虑一下:

struct A  {  int x; char y;  }
struct B  { A left, right;   }

根据当前标准,我只需要知道 sizeof(A) 即可确定 B 的大小和布局。
使用您建议的替代方案,我需要了解 A 的内部结构。与您的 example2 类似:对于“更好的包装”, sizeof(example) 是不够的,您需要考虑 example 的内部结构。

So to split your question up into two:

1. Is it legal?

[5.3.3.2] When applied to a class, the result [of the sizeof() operator] is the number of bytes in an object of that class including any padding required for placing objects of that type in an array.

So, no, it's not.

2. Well, why isn't it?

Here, I cna only speculate.

2.1. Pointer arithmetics get weirder
If alignment would be "between array elements" but would not affect the size, zthigns would get needlessly complicated, e.g.

(char *)(X+1) != ((char *)X) + sizeof(X)

(I have a hunch that this is required implicitely by the standard even without above statement, but I can't put it to proof)

2.2 Simplicity
If alignment affects size, alignment and size can be decided by looking at a single type. Consider this:

struct A  {  int x; char y;  }
struct B  { A left, right;   }

With the current standard, I just need to know sizeof(A) to determine size and layout of B.
With the alternate you suggest I need to know the internals of A. Similar to your example2: for a "better packing", sizeof(example) is not enough, you need to consider the internals of example.

旧情勿念 2024-10-18 05:35:29

该标准很少提及填充和对齐。得到保证的很少。您唯一可以押注的是第一个元素位于结构的开头。之后...对齐和填充可以是任何东西。

The standard says very little about padding and alignment. Very little is guaranteed. About the only thing you can bet on is that the first element is at the beginning of the structure. After that...alignment and padding can be anything.

娜些时光,永不杰束 2024-10-18 05:35:29

似乎 C++03 标准没有说(或者我没有找到)对齐填充字节是否应该包含在对象表示中。

C99 标准表示结构类型或联合类型的“sizeof”包括内部和尾随填充,但我不确定所有对齐填充是否都包含在该“尾随填充”中。

现在回到你的例子。确实没有什么混乱。 sizeof(example) == 8 表示该结构确实需要 8 个字节来表示自身,包括尾部 3 个填充字节。如果第二个结构中的字符偏移量为 6,它将覆盖 m_Example 使用的空间。某种类型的布局是实现定义的,并且应该在整个实现中保持稳定。

不过,p+1 是否等于 (T*)((char*)p + sizeof(T)) 仍不确定。我希望能找到答案。

Seems the C++03 standard didn't say (or I didn't find) whether the alignment padding bytes should be included in the object representation.

And the C99 standard says the "sizeof" a struct type or union type includes internal and trailing padding, but I'm not sure if all alignment padding is included in that "trailing padding".

Now back to your example. There is really no confusion. sizeof(example) == 8 means the structure does take 8 bytes to represent itself, including the tailing 3 padding bytes. If the char in the second structure has an offset of 6, it will overwrite the space used by m_Example. The layout of a certain type is implementation-defined, and should be kept stable in the whole implementation.

Still, whether p+1 equals (T*)((char*)p + sizeof(T)) is unsure. And I'm hoping to find the answer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文