【问题标题】:_Bool type and strict aliasing_Bool 类型和严格别名
【发布时间】:2019-02-09 08:15:45
【问题描述】:

我正在尝试编写一些宏以安全使用_Bool,然后对我的代码进行压力测试。为了邪恶的测试目的,我想出了这个肮脏的黑客:

_Bool b=0;
*(unsigned char*)&b = 42;

鉴于 _Bool 在实现 sizeof(_Bool)==1 上是 1 个字节),我看不出这种 hack 是如何违反 C 标准的。这不应该是严格的别名违规。

然而,当通过各种编译器运行这个程序时,我遇到了问题:

#include <stdio.h>

int main(void)
{
  _Static_assert(sizeof(_Bool)==1, "_Bool is not 1 byte");

  _Bool b=0;
  *(unsigned char*)&b = 42;
  printf("%d ", b);
  printf("%d", b!=0 );

  return 0;
}

(代码依赖printf隐式默认参数提升为int

某些版本的 gcc 和 clang 输出 42 42,其他版本输出 0 0。即使禁用了优化。我会期待42 1

似乎编译器假定_Bool 只能是10,但同时它在第一种情况下很高兴打印42

Q1:为什么会这样?上面的代码是否包含未定义的行为?

Q2:sizeof(_Bool) 的可靠性如何? C17 6.5.3.4 根本没有提到_Bool

【问题讨论】:

  • 输出怎么可能是42 42?第二个 printf 只能打印 1 或 0。
  • O_O I tried it。我的心现在被严重炸毁了。这加入了我收集的 UB 示例。
  • 我不知道。虽然我最初对第二个 42 感到震惊,但回想起来还是有道理的。因为b != 0_Bool 可以优化为简单的b。不过我还在摸不着头脑。
  • @RbMm 字符类型在严格的别名规则中是一个例外。优化器无法基于此处导致 UB。
  • 请发布已发布问题的汇编代码。然后我们可以很容易地确定编译器在想什么

标签: c boolean language-lawyer c11 strict-aliasing


【解决方案1】:

Q1:为什么会这样?上面的代码是否包含未定义的行为?

是的,确实如此。商店是有效的,但随后将其读取为_Bool 无效。

6.2.6 类型的表示

6.2.6.1 常规

5 某些对象表示不需要表示对象类型的值。如果对象的存储值具有这样的表示形式并且由不具有字符类型的左值表达式读取,则行为未定义。 [...]

Q2:sizeof(_Bool) 的可靠性如何? C17 6.5.3.4 根本没有提到_Bool

它将可靠地告诉您存储一个_Bool 所需的字节数。 6.5.3.4也没有提到int,但你不是问sizeof(int)是否可靠,是吗?

【讨论】:

  • 那么_Bool a; *(unsigned char*)&amp;a = 42; printf("%d", *(unsigned char*)&amp;a); 有效吗?
  • @KamilCuk 据我所知,是的,这是完全有效的。
  • @user694733 - 拥有 UB 不需要是陷阱表示。上面的引用根本不需要访问陷阱。只需为_Bool 产生 42 就足够了 UB。
  • @Lundin no_trap_t no_trap = {b}; 仍然无效,因为它读取了一个陷阱表示。 p6 的意思是,如果这是有效的(或者如果您以其他方式在结构成员中获得陷阱表示),那么执行 no_trap = no_trap; 也是有效的:即使它复制具有陷阱表示的成员,结构为整体没有。
  • @Lundin:保证结构或联合绝不是陷阱表示的目的本质上是说,如果没有可能的位,编译器只能用逐个成员分配替换结构分配结构可能持有的模式会导致此类操作产生副作用。如果一个结构成员持有一个作为陷阱表示的位模式或与任何其他成员共享其含义,我认为标准允许目标结构的相应成员持有任何等效的位模式(或者如果源是一个陷阱则根本没有任何模式代表)。
猜你喜欢
  • 2019-01-29
  • 2012-02-02
  • 2013-10-01
  • 1970-01-01
  • 2020-08-01
  • 2017-10-23
  • 2012-11-08
  • 2020-03-31
  • 1970-01-01
相关资源
最近更新 更多