【问题标题】:Do C++ programs ever access non-scalar objects?C++ 程序是否曾经访问过非标量对象?
【发布时间】:2021-01-17 18:24:42
【问题描述】:

在本次讨论中,我假设标量对象是整数、浮点数、字符、布尔值和指针之类的东西。非标量对象是由标量类型和递归聚合组成的聚合(结构)。

鉴于此假设,C++ 程序是否曾经从它们的标量组件中区分访问聚合?

举个例子:

struct s { int a; float b; };
void assign1(s& out, s const& in) { out = in; }
void assign2(s& out, s const& in) { out.a = in.a; out.b = in.b; }

显然assign1assign2 在实践中是等价的,并且都可以访问int s::afloat s::b。但是它们中的任何一个是否在任何意义上都访问了整个聚合?

只有标量对象才被实际访问的解释产生了有趣的结果。

例如,根据我的另一个问题here 的解决方案,形成对对象的引用并不构成访问。鉴于该分辨率,我可以编写如下函数:

void assign3(s& out, s const& in) {
    int& a_out = out.a;  // no access
    int const& a_in = in.a;  // no access
    a_out = a_in;  // access some ints
}

没有“访问”出现,除了第三行,它访问一些整数。 outin 是否实际引用 s 类型的对象是无关紧要的。只有a_outa_in 必须实际引用整数。

鉴于这一点,以及对象的地址是其第一个非静态数据成员的地址这一事实,我有权写

int out, const in = 42;
assign3(reinterpret_cast<s&>(out), reinterpret_cast<s const&>(in));

如果所有这些假设都成立,那么 C 和 C++ 在很大程度上只是可移植的汇编语言,而别名规则只是为了帮助编译器正确地从 x87 浮点协处理器中读取寄存器。

当然,假设不成立。我错了。但是为什么我错了?为什么标准文档有这些关于有效类型或动态类型的规则?

考虑到struct a { int a; }; struct b { int b; };,通过b::b undefined 访问a::a 有什么好处,除非在涉及工会的一些有限情况下?

【问题讨论】:

  • 这个问题有一个有趣的倒置:语言不会使任何东西未定义。反过来说:最初,什么都没有定义。然后标准定义了一些东西。它有时会强调它遗漏了一些东西,或者明确遗漏了原本会被规则涵盖的东西。
  • 您是正确的,该文档没有“取消定义”事物。但是撰写该文档的人通常是有充分理由的。他们认为通过使行为的某些方面“未定义”可以获得一些东西。我想知道为什么。
  • @LanguageLawyer 这个问题是相关的,但它没有解决object.member 是否构成对member 或整个object 的访问。
  • ????也许this 和/或this 会让你满意?

标签: c++ language-lawyer strict-aliasing


【解决方案1】:

对编译器来说重要的一件事是从内存中重新加载寄存器。这需要时间,最好避免。因此,如果您知道在地址p 上存在一个包含float[2]Foo 结构,那么您在寄存器中有第一个浮点数,然后您可以写入地址q 的浮点数,您是否需要重新加载第一个浮动?如果您无法证明p!=q,这可能是必要的。但是如果你知道地址q 的浮点数是Bar 的一部分,因此后面跟着int,那么这就是p!=q 的证明。因此,写入 q 不会强制从 p 重新加载。

注意pq 后面的数据在此处不被读取或写入。只是这些类型不同的事实允许编译器通过p优化冗余读取。

【讨论】:

  • 确实如此。但是如果我只访问p-&gt;floats[0] 而从不访问p-&gt;floats[1],编译器真的可以假设那里存在一个完整的Foo 对象吗?根据我对标量类型的解释,没有访问Foo 这样的东西。仅访问floats。按照这个逻辑,只要pq 后面的数据没有被访问,它们实际上可以别名。
  • @Filipp:在 Dennis Ritchie 的原始语言中,任何存储区域都将同时包含所有适合的对象。 foo-&gt;bar 之类的东西会取foo 中的地址,添加结构成员bar 的偏移量,并在结果地址访问bar 类型的对象,而不考虑是否有foo 的任何结构的类型实际上存在。 C++ 增加了额外的要求,暗示只有在结构类型的对象实际存在时才会定义行为,但由于没有达成共识,因此存在一些永远无法解决的模棱两可的极端情况......
  • ...关于标准的含义,没有人会同意与他们的解释不相容的提议措辞。
  • @Filipp:对p-&gt;floats[0] 的访问要求p-&gt; 是合法的,这通常要求那里存在一个完整的Foo 对象。 (理论上,Foo ctor 可以有p=this; p-&gt;floats[0]=0;,dtor 也可以,但这是一个例外,我不认为你在寻找)。 p-&gt; 不是一个标量“访问”,但它是到达那个 Salar 的必要条件。
  • @supercat:是的。就我个人而言,我站在那些认为有大量有效的 C 程序仍然是有效的 C++ 的人的一边。因此,如果 ISO C++ 中“malloc, cast, write”是否有效存在任何歧义,则应该通过运行代码来解决。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-06
  • 2012-09-11
  • 1970-01-01
  • 2012-03-15
  • 2015-11-12
  • 2021-08-21
  • 2020-12-08
相关资源
最近更新 更多