对于所有对象类型T,sizeof(T)> = alignof(T)是否总是成立?

50

对于任何对象类型 Tsizeof(T) 大小至少与 alignof(T) 相同吗?

直观上看似乎是这样的,因为即使调整对象的对齐方式:

struct small {
  char c;
};

在正常情况下,它们的“大小”也会向上调整,以使数组中对象之间的关系保持一致,同时保持对齐(至少在我的测试中)。例如:

struct alignas(16) small16 {
  char c;
};

这个元素的大小和对齐方式均为16。


2
@tadman - 针对任何架构。我想知道标准的保证或意味着什么。显然,在这两个示例中,sizeof(分别为12和I * 200)比alignof(分别为1和I)大,其中I是sizeof(I)。 - BeeOnRope
2
@tadman - 这不是关于x86处理器的问题。当你说“这些将是相同的”时,指的是什么? - BeeOnRope
4
假设开始于“float 占用 4 个字节, sizeof(float) 返回值为 4,但系统体系结构要求 float 必须位于 8 字节边界上”,那么会发生什么?直接说,我认为这意味着 float 数组会出现错误。 - Andrew Henle
2
@tadman 当然,一般情况下 sizeof(T) == alignof(T) 并不成立。例如,struct S { char a,b; }; 通常大小为2,对齐方式为1,这是显而易见的。我的问题是关于 >= 而不是 == ... - BeeOnRope
3
尽管在硬件层面上可能存在奇怪的情况,比如需要16字节对齐的10字节值,但从C++角度来看,我认为这必须简单地具有sizeof 16。这就是为什么我问@tadman,是否有一个平台的sizeof(long double)小于alignof(long double),因为这似乎不可能是一种合规的实现! - BeeOnRope
显示剩余21条评论
4个回答

37

至少在标准C++中,对于任何您可以制作长度大于1的数组的内容,这都是正确的。如果您有

Foo arr[2];

如果 alignof(Foo) > sizeof(Foo),那么 arr[0]arr[1] 不能同时对齐。

然而,正如 Zalman Stern 的示例 所示,至少一些编译器允许您声明一个具有大于其大小的对齐方式的类型,结果是编译器不会让您声明该类型的数组。这不是符合标准的 C++(它使用类型属性,这是 GCC 扩展),但它意味着您可以在实践中拥有 alignof(T) > sizeof(T)

数组参数假设 sizeof(Foo) > 0,这对于标准支持的任何类型都是正确的,但是 o11c 显示了 编译器扩展破坏了该保证:某些编译器允许长度为零、sizeof 为零且 alignof 为正数的数组。


我不确定有任何情况下这个参数不适用。也许是一种你根本无法创建实例的对象类型,或者是一个如此巨大的类型,你无法在同一地址空间中容纳两个它们? - user2357112
评论区的Hans似乎在暗示sizeof(Foo[2]) > 2*sizeof(Foo)是被允许的,这将会打破你的论点。不过我不确定是否应该相信他。 - BeeOnRope
1
@user2357112 你不能声明一个抽象类的数组,甚至是单个实例。因此它的 sizeof 不是很有用。 - curiousguy
1
@curiousguy:嗯,你可以将抽象类的实例作为具体子类实例的基类子对象。我认为没有任何方法可以获得它们的数组,因此即使没有编译器扩展,这似乎是一个数组参数失败的示例。 - user2357112
我不知道那是否严格符合行为规范 - 实际上并不是。Zalman的例子使用了类型属性__attribute__ ((aligned (64)))。类型属性是GCC扩展,而不是C或C++标准的一部分。我进行了编辑以澄清 - 希望这样可以。 - sleske
@sleske:我应该用不同的措辞表达:符合规范的实现允许具有哪些类型的扩展存在是受限制的,而我不确定这个扩展的行为是否在限制范围内。我认为它是允许的,但我不确定。 - user2357112

16
#include <iostream>

typedef double foo __attribute__ ((aligned (64)));
alignas(64) double bar;
double baz __attribute__ ((aligned (64)));

int main(int argc, char *argv[]) {
    std::cout << "foo sizeof: " << sizeof(foo) << " alignof: " << alignof(foo) << "\n";
    std::cout << "bar sizeof: " << sizeof(bar) << " alignof: " << alignof(decltype(bar)) << "\n";
    std::cout << "baz sizeof: " << sizeof(baz) << " alignof: " << alignof(decltype(baz)) << "\n";
}

使用以下命令进行编译:

clang++ -std=c++11 alignof_test.cpp -o alignof_test && ./alignof_test

输出:

foo sizeof: 8 alignof: 64
bar sizeof: 8 alignof: 8
baz sizeof: 8 alignof: 8

严格来说,不可以,但是上述关于数组的论点必须被保留。


1
尝试使用foo arr[2]; std :: cout << sizeof(arr)<<“\ n”; - Andrew Henle
3
“error: alignment of array elements is greater than element size” 的意思是数组元素的对齐方式大于元素本身的大小,因此无法创建这样的数组。我想知道这是否严格符合规范行为。 - user2357112
1
如果您声明了一个类型为 foo 的对象,则它将保持 64 字节的 alignof。有趣的是,如果您将 aligned 属性附加到 typedef 并使用 typedef,与直接将其附加到变量声明相比,它会以不同的方式与 alignof 交互。 - user2357112
2
attribute ((aligned))” 是标准的 C++ 吗?您能否使用 “alignof” 完成相同的技巧? - BeeOnRope
3
__attribute__ 是GCC的扩展。我还没有找到使用 alignof 能够获得相同结果的方法;我可以创建一个对象,使其 alignof(obj) > sizeof(obj),但无法创建该类型。 - user2357112
显示剩余3条评论

10
根据引入 C++11 标准alignof 操作符,sizeof 的定义如下(参见5.3.3表达式.sizeof):

sizeof操作符返回其操作数的对象表示中所占用的字节数。

alignof 的定义为(参见5.3.6表达式.alignof):

一个 alignof 表达式返回其操作数类型的对齐要求。

由于 alignof 的定义规定了可能由用户制定的需求,而不是语言规范,因此我们可以操纵编译器:
typedef uint32_t __attribute__ ((aligned (64))) aligned_uint32_t;
std::cout << sizeof(aligned_uint32_t) << " -> " << alignof(aligned_uint32_t);
// Output: 4 -> 64

编辑

正如其他人指出的那样,这种类型不能用于数组中,例如尝试编译以下内容:

aligned_uint32_t arr[2];

出现错误:error: 数组元素的对齐方式大于元素大小

由于数组要求指定的类型符合以下条件:sizeof(T) >= alignof(T)


1
是的,但请注意aligned_uinit32_t并不是一种类型,尽管您给它命名的方式是这样的。它声明了一个具有不同对齐方式的堆栈变量。我知道在这种情况下,对齐方式是您要求的,并且sizeof不会改变。问题是是否可以在使用时使该行为成为一种类型。 - BeeOnRope
@BeeOnRope,你是对的,我不小心发布了中间代码。我已经调整了我的答案,以展示可以这样声明类型。 - Daniel Trugman

7
许多编译器都允许大小为0的数组。对齐方式与唯一元素的对齐方式相同。
(除其他用途外,在无法使用位域的情况下,这对于强制特定对齐方式非常有用)

2
哦,是的,在某些编译器上,0长度数组是一种东西。大小为0的类型会影响很多东西。 - user2357112
@user2357112:将语言概念推广到大小为零的情况会增加一些棘手的特殊情况,但可以让程序消除许多其他问题。例如,如果在结构体的某个位置可能需要填充,允许大小为零的数组将根据编译器的计算决定是否包含填充。 - undefined

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