当前位置: 首页 > 知识库问答 >
问题:

C++是否保证“普通”结构与单个普通成员具有相同的二进制布局?

萧煜
2023-03-14
struct FooIdentifier {
  int raw_id; // the only data member

  // ... more shenanigans, but it stays a "trivial" type.
};

struct BarIdentifier {
  int raw_id; // the only data member

  // ... more shenanigans, but it stays a "trivial" type.
};

注意:由于我可以静态地检查类型是否具有相同的大小(即没有填充),所以我实际上只对没有意外填充的情况感兴趣。我应该从一开始就添加这个注释

// Precodition. If platform would yield false here, I'm not interested in the result.
static_assert(sizeof(int) == sizeof(ID_t)); 

也就是说,以下内容在C++标准POV中是否成立:

int integer_array[42] = {}; // zero init
ID_t id_array[42] = {}; // zero init

static_assert(sizeof(int) == sizeof(ID_t)); // Precodition. If platform would yield false here, I'm not interested in the result.

const char* const pIntArrMem = static_cast<const char*>(static_cast<const void*>(integer_array));
const char* const pIdArrMem = static_cast<const char*>(static_cast<const void*>(id_array));
assert(0 == memcmp(pIntArrMem, pIdArrMem, sizeof(int))); // Always ???

共有1个答案

曹昊焱
2023-03-14

TL;No博士,标准似乎不能保证(据我所知)。从技术上来说,你必须依赖于有一个理智的ABI。

您可能需要放弃对DS9K的支持。

该标准并没有明确地保证布局。充其量,我们可以根据我们所拥有的保证,对实际实现可以做什么做出一些合理的假设。

[碱性化合物]

两个对象a和b是指针可互换的,如果:

  • ...
  • 一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有非静态数据成员,则是该对象的任何基类子对象([class.mem]),或者
  • 存在一个对象c,使得a和c是指针互转的,c和b是指针互转的。

这意味着integer_arrayid_array或任何数组在元素之前(之间或之后)都没有填充。

由于int子对象之前没有填充,所以第二个断言是合理的假设,除非一个对象可以在一个上下文中有一个表示,在另一个上下文中有另一个表示(free vs sub object,或者不同封闭类型的sub object)。例如,大的endian在一个中,小的endian在另一个中。我找不到标准不允许这样做,但我也无法想象这样的实现在实践中如何工作,因为编译器实际上不能总是知道特定的glvalue是否是子对象(以及包含在其中的对象)。

根据上述假设,第一个断言归结为“标准布局类在唯一的成员之后是否有填充?实际上,如果有alignas或某些影响语言扩展的布局,这是完全可能的,但如果不是这样,我们是否可以假设否定的呢?标准并没有说明太多,而且我认为对于实际的语言实现来说,添加一些填充也不是不可能的--只是不太有用。

关于对象表示的小标准:

[basic.types.general]

类型T的对象的对象表示形式是类型T的对象所占用的N个无符号char对象的序列,其中N等于sizeof(T)。类型T的对象的值表示是参与表示类型T的值的一组位。对象表示中不属于值表示的位是填充位。对于基本可复制的类型,值表示形式是对象表示形式中确定值的一组位,该值是实现定义的值集的一个离散元素。35

如果两个标准布局结构([class.prop])类型的公共初始序列包含两个类([basic.types])的所有成员和位字段,则它们是布局兼容类。

[碱性化合物]

...指向布局兼容类型的指针应具有相同的值表示和对齐要求

 类似资料:
  • 问题内容: 如果下面的代码会产生相同的结果,为什么还要使用封装? 封装的主要好处是能够修改我们已实现的代码而不会破坏其他使用我们代码的人的代码。 但是我可以在不使用封装的情况下使用此好处,对吗?因为每个对象的字段都彼此不同。 问题答案: 您的问题很有趣。我将尽力为您解答。 封装 背后的主要思想 是向 其他用户 隐藏数据及其实现 细节。如果我们将数据成员设为 私有 ,则只能在同一类中访问它。没有其他

  • 问题内容: 我有两个成员相同,我想将一个结构复制到另一个结构,请参见下面的伪代码: 然后,我有结构的,而结构的,有什么办法复制的? 问题答案: 使用转换更改类型。以下代码使用转换将type 的值复制到type 的值: 游乐场的例子 该转换仅在基础类型,除了结构标签相同的工作。

  • 问题内容: 民间, 是否有一些Numpy与python不同的陷阱,这些点令人困惑并花费时间? “那一刻的恐怖,我将永远不会忘记!” 女王说:“不过,如果您不做备忘录,您会的。” 例如,NaN在任何地方都总是麻烦。如果您不做任何解释就可以说明这一点, (我并没有敲打麻木,那里没有很多好的工作,只是认为常见问题解答或维基百科将很有用。) 编辑:我希望能收集到半个陷阱(人们学习Numpy的惊喜)。 然后

  • 问题内容: 我应该使用php PDO还是普通的mysql_connect在PHP中执行数据库查询? 哪一个更快? PDO的一大优点是接口在多个数据库之间是一致的。对于准备好的语句,也有一些很酷的功能,这些功能避免了转义所有查询字符串的麻烦。PDO的可移植性大于mysql_connect。 那么,出于这些原因,我应该使用PDO还是坚持使用传统的mysql_connect? 问题答案: PDO比mys

  • 问题内容: 我正在尝试在旧版Java / Spring / Hibernate项目中执行cron作业,因此我决定使用spring调度程序。 我希望myTask.doStuff在每个月的第一个星期日的12:00运行。 在我的application-context.xml中,我已将任务调度程序配置为: 问题cron表达式本身是: 0 0 12? 1/1 SUN#1 * 并且是一个bean,它有一个名为

  • 在阅读了关于ForkJoinPool的文章之后,我尝试了一个实验,测试与普通递归相比,实际上有多快。 我以递归的方式计算了文件夹中的文件数,令我满意的是,简单的递归比执行得更好 这是我的密码。 递归任务 纯递归 null