为什么可以将自动值初始化为陷阱表示而不会导致UB?

时间:2015-11-14 04:19:29

标签: c language-lawyer

在6.2.6.1 p5中,C11说:

  

某些对象表示不需要表示对象类型的值。如果对象的存储值具有这样的表示并且由不具有字符类型的左值表达式读取,则行为是未定义的。如果这样的表示是由副作用产生的,该副作用通过不具有字符类型的左值表达式修改对象的全部或任何部分,则行为是未定义的.50)这种表示称为陷阱表示。

和第50个脚注是:

  

因此,可以将自动变量初始化为陷阱表示而不会导致未定义的行为,但只有在其中存储了正确的值之后才能使用该变量的值。

事实上,我无法理解他们。

3 个答案:

答案 0 :(得分:2)

作为可能具有陷阱表示的示例的最简单类型是_Bool。事实上,如果_Bool的值为01,则只能很好地定义它。所有其他值都是(或可以看作)陷阱表示。

现在看看像

这样的东西
struct both {
  _Bool b;
  char  c;
};

both two = { .c = 'a', };

printf("char: %c\n", two.c);                     // All fine
printf("bool: %s\n", two.b ? "true" : "false");  // error

所以two.b有一个值是一个陷阱,只有当你尝试使用你的程序出错的值时,才会再定义行为。例如,您的编译器可以决定通过等同于

的东西来实现两个字符串的选择
(char const*[]){ "false", "true" }[two.b]

并且使用特定值(不是01),您的数组访问权限将超出范围。

答案 1 :(得分:1)

首先,auto变量中的'trap'值只表示变量的内容对变量的类型无效。

'不可用'语句只表示不应使用未正确初始化的变量。通常,编译器会在代码尝试使用未初始化的变量并发出警告时捕获。

这是在编译时始终启用所有警告,然后修复这些警告的另一个原因。

答案 2 :(得分:1)

陷阱表示意味着组成对象的字节不会形成对象类型的正确值。 32位int *的典型陷阱表示是11 11 11 11:许多实现需要(在硬件本身或编译器的优化过程中)所有int *具有一定的对齐,这些字节不能满足这些要求。未定义的行为部分意味着如果您尝试读取这样的int *值,事情可能会中断。即使您不尝试取消引用指针,硬件也可能使程序崩溃。编译器可以优化对齐检查,使其看起来像地址已正确对齐 - 因为已知每个 int *都已对齐。

脚注涵盖了这个事实

void f() {
  { int i = 0x11111111; }
  { int *p; puts("Hello"); p = 0; }
}
即使ip被赋予相同的地址且0x11111111未转换为有效的int *

仍然有效。在调用puts时,没有未定义的行为。如果硬件捕获分配给指针寄存器的无效值,则编译器必须确保它不会将p值加载到寄存器中,直到它被赋予有效值。