sizeof(T) == sizeof(const T) and alignof(T) == alignof(const T)吗?

37

似乎可以合理地假设Tconst T是两种大小相同、对齐方式相同的类型,但在考虑一些实际系统后,它们可能是不同的。

让我解释一下:

假设您有一个具有两种类型内存的系统:RAM和Flash(只读)。RAM是8位可寻址的,而Flash仅是16位可寻址的。假设这是T

struct T
{
  uint8_t x;
  uint16_t y;
};

在字节寻址的RAM中,该结构体长度为3个字节...但在双字节寻址的Flash中(这是const变量所在的地方),由于对齐问题,该结构体至少需要4个字节长。
所以我的问题是:
C和C++标准是否保证了const和非const类型的大小和对齐方式?

10
@JonathonReinhart 因此有这个问题。 - DarthRubik
3
这个问题有点棘手。答案是“是的,它们必须是相同的大小”,然而,闪存并不是“常量变量所在的地方”。你可能会把许多常量值放在那里,但const并不意味着闪存。特别是,我可以将一个const int参数传递给函数调用,它将存在堆栈中,而不是闪存中。最终,我认为这样的系统违反了C内存模型,这是一种均匀的模型,因此它的行为将是编译器扩展。 - Cort Ammon
请注意,像您描述的那样的高级硬件通常配备定制的、非符合性的编译器,能够利用完整的硬件潜力,但代价是不符合标准的某些细节...因此,如果您确实需要利用Flash v RAM,仍然可以使用非符合性编译器。 - Bakuriu
2
@CortAmmon 我不同意你的说法“我认为这样的系统违反了C内存模型,这是同质的”。是的,C有一个抽象的同质内存模型,但C标准并不要求实现具有同质内存,只要它*表现得像就可以了。编译器解决这个问题的一种方法(以优化为代价)并表现得像它具有同质内存的方式是,使所有的T都是4个字节长,并且指向T的指针在其中有一个特殊的位,指示它是位于闪存还是RAM中。 - DarthRubik
关于内存模型,我想强调一下@DarthRubik的观点:我目前正在为一个具有14位Flash字和8位RAM字的平台编写代码。(RAM本身被分成每个128字节的银行,但每个银行只有80字节可用)。它将const数据存储在Flash中,作为一名顺从的C开发人员,我不关心它是如何存储的(尽管我知道)。只要我可以使用正常的C结构访问数据,我的代码就能按预期工作。 - pipe
显示剩余9条评论
3个回答

34

第3.9.3节:

类型的 cv 限定或非 cv 限定版本是不同的类型;然而,它们必须具有相同的表示和对齐要求(3.11)。53

此处的 "cv-qualified" 指的是 constvolatile。所以答案是肯定的。

constvolatile 只指定对指定对象的访问的限制/属性。它们不被认为是基本类型的一部分;因此,它们不能影响类型的属性。


1
所以在我的例子中,sizeof(T)将被强制为4,以匹配const T - DarthRubik
对于C语言,除了拼写为_Alignof外,其版本为6.2.5/26(在C11中)。 - dave_thompson_085
1
@DarthRubik 不像Sam那样,我不确定这是正确的。编译器可以选择将对象强制存储在允许较松散的3字节对齐的内存区域中。当然,这意味着禁止在你的示例中使用闪存 - 但我认为这并不像听起来那么奇怪;我记得Arduino要求按照定义的单位对数据进行特殊序列化到/从Flash中。唯一的问题是,编译器不能分配不同的cv-qualified实例,使得上述属性有所不同。我不会急于下结论 必须以何种方式做到这一点。 - underscore_d
如果你使用的是avr-gcc(也就是Arduino使用的编译器),gcc会尝试将const变量存储在flash中,但使用FLASHCONST可以保证它。 - DarthRubik

31

是的,这得到了 [basic.type.qualifier] / 1 的保证。

类型的cv限定或者非cv限定版本是不同的类型;然而,它们应该有相同的表示和对齐要求(3.11)。


4
在字节寻址的RAM中,这个结构体的长度将会是3个字节。但是在双字节寻址的Flash中(一个常量变量可能驻留的地方),由于对齐问题,这个结构体的长度至少需要是4个字节。
然而,编译器不能推断出它只是因为这里是const,就存储在ROM中。还有很多其他的东西可以防止这种情况发生,比如mutable,或者你可以将const T动态放置在堆栈上,或手动将其放置到RAM中的堆内存中,或者其他一千种情况。你也可以有一个const T&,它可以在任何位置。

这是很好的信息。记住 const 并不 保证 放置在 ROM/Flash 中,尽管通常你想要这样。 - pipe
“mutable”是相关的,因为在一个“const”对象内部的可变字段可以被修改,因此不能驻留在ROM中。 - Phil1970

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