Tag: undefined behavior

如何发现未定义的行为

有没有办法知道你的程序是否在C ++(甚至C)中有未定义的行为,而不是记住整个规范? 我问的原因是我注意到很多程序在调试中工作但没有发布是由于未定义的行为。 如果有一个工具至少可以帮助发现UB,那就太好了,所以我们知道存在问题的可能性。

当printf的相应参数不是short / char时,使用h或hh长度修饰符是不合法的吗?

printf系列提供了一系列长度修饰符,其中两个是hh (表示有signed char或unsigned char参数提升为int )和h (表示有signed short或unsigned short参数,提升为int )。 从历史上看,这些长度修改器仅用于创建与scanf的长度修饰符对称,并且很少用于printf 。 以下是ISO 9899:2011§7.21.6.1“fprintf函数”¶7的摘录: 7长度修饰符及其含义是: hh指定以下d , i , o , u , x或X转换说明符适用于signed char或unsigned char参数(该参数将根据整数提升进行提升,但其值应转换为signed char或打印前unsigned char ); 或者后面的n转换说明符适用于指向signed char参数的指针。 h指定以下d , i , o , u , x或X转换说明符适用于short int或unsigned short int参数(该参数将根据整数提升进行提升,但其值应转换为short int打印前的short int或unsigned short int ); 或者后面的n转换说明符适用于指向short int参数的指针。 … 忽略n转换说明符的情况,这些几乎相同的段落对h和hh的行为有何看法? 在这个答案中 ,声称传递的参数超出了signed char , […]

括号是否强制执行评估顺序并定义未定义的表达式?

当我遇到这个问题时,我正在阅读我的教科书 在下面的表达式之后,a的值是多少? 假设a的初始值为5.Mention步骤 一个+ =(A ++)+(++ A) 起初我认为这是未定义的行为,因为a已被多次修改。 那么我就读了这个问题并说它提到了步骤所以我可能认为这个问题是正确的。 所以我的问题是: 应用括号是否定义了未定义的行为? 是否在评估括号表达式后创建了序列点? 如果已定义,则括号如何重要,因为++和()具有相同的优先级 注意:一个解释清楚且明确的答案将得到我的投票

“未定义的行为”是否延伸到编译时?

我们都听过警告,如果你在C或C ++中调用未定义的行为 , 任何事情都可能发生。 这是否仅限于任何运行时行为 ,还是包含任何编译时行为? 特别是,编译器在遇到调用未定义行为的构造时,允许拒绝代码(在标准中没有其他要求的情况下这样做),甚至崩溃?

论未定义的行为

一般来说,UB被认为是必须避免的东西,目前的C标准本身列出了附录J中的不少例子。 但是,在某些情况下,除了牺牲可移植性之外,我认为利用UB没有任何害处。 考虑以下定义: int a = INT_MAX + 1; 评估该表达导致UB。 但是,如果我的程序打算在32位CPU上运行,模块化算术代表Two’s Complement中的值,我倾向于相信我可以预测结果。 在我看来,UB有时只是C标准告诉我的方式:“我希望你知道你在做什么,因为我们无法保证会发生什么。” 因此我的问题是:即使C标准认为它要调用UB,或者无论在什么情况下都要避免“UB”,有时依赖依赖于机器的行为是否安全?

“sizeof(arr )”会导致未定义的行为吗?

有一个众所周知的模式来计算数组长度: int arr[10]; size_t len = sizeof(arr) / sizeof(arr[0]); assert(len == 10); 此模式适用于静态数组和常量大小的自动数组。 它也适用于C99中的可变长度数组。 我想应用类似的想法来计算动态数组大小(以字节为单位): size_t known_len = 10; int *ptr = malloc(known_len * sizeof(int)); size_t size = known_len * sizeof(ptr[0]); assert(size == known_len * sizeof(int)); 这比known_len * sizeof(int)更好,因为sizeof(ptr[0])不引用实际的数组元素类型。 因此,它不需要代码的读者知道类型。 但是我不清楚表达式sizeof(ptr[0])会导致不确定的行为。 随着它扩大: sizeof(ptr[0]) -> sizeof(*((ptr) + (0))) -> sizeof(*ptr) 如果ptr为0 ,结果表达式是有问题的: sizeof(*((int*) 0)) 根据C99标准: (C99,6.3.2.3p3):“值为0的整型常量表达式,或者类型为void […]

当sizeof(int)== 4时,1 << 31在C中定义得很好

根据这个问题的答案: E1 << E2的结果是E1左移E2位位置; 腾出的位用零填充。 如果E1具有无符号类型,则结果的值为E1×2 E2 ,比结果类型中可表示的最大值减少一个模数。 如果E1具有带符号类型和非负值,并且E1×2 E2可在结果类型中表示,那么这就是结果值; 否则,行为是未定的。 这似乎意味着1 << 31未定义。 但是,如果我使用1 << 31 GCC不会发出警告。 它确实为1 << 32发出一个。 链接 那是哪个呢? 我误解了这个标准吗? 海湾合作委员会有自己的解释吗?

序列点和副作用:C11的安静变化?

C99§6.5 表达式 (1)表达式是操作符和操作数的序列,其指定值的计算,或指定对象或函数,或者生成副作用,或执行其组合。 (2)在前一个和下一个序列点之间,一个对象的存储值最多只能通过表达式的计算来修改一次。 72)此外,先前的值应该是只读的,以确定要存储的值。 73) 用脚注 72)浮点状态标志不是对象,可以在表达式中多次设置。 73)此段落呈现未定义的语句表达式,如 i = ++i + 1; a[i++] = i; 同时允许 i = i + 1; a[i] = i; C11§6.5改为((1)的文本有附录): (1)[…]运算符的操作数的值计算在运算符的结果的值计算之前被排序。 (2)如果对标量对象的副作用相对于对同一标量对象的不同副作用或使用相同标量对象的值进行的值计算未被排序,则行为未定义。 如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的。 84) 其中C11中的脚注84与C99中的73相同。 我有点困惑……我把C11(2)看作是“[…](对同一个标量对象的不同副作用)或(使用相同标量对象的值进行值计算)[…]”甚至不允许foo = ++i (有副作用,我们根据更改的对象使用值)。 不过,我不是母语人士,所以如果能告诉我这句话应该如何“解析”会更好。 我理解C99,但我不太明白C11的措辞。 无论如何,实际问题:这是从C99到C11的变化,还是这些措辞相当? 如果是这样,为什么它会被改变? 如果没有,有人可以给出一个表达式的例子,这个表达式在C99中是UB但在C11中不是,反之亦然?

是否假定C / C ++中的所有函数都返回?

我正在阅读关于未定义行为的本文 ,其中一个示例“优化”看起来非常可疑: if (arg2 == 0) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg(“division by zero”))); /* No overflow is possible */ PG_RETURN_INT32((int32) arg1 / arg2); 图2 :意外的优化使PostgreSQL的src/backend/utils/adt/int8.c中的除零检查src/backend/utils/adt/int8.c 。 对ereport(ERROR, :::)的调用将引发exception。 本质上,编译器假定 ereport将返回,并删除arg2 == 0检查,因为除法的存在意味着非零分母,即arg2 != 0 。 这是一个有效的优化吗? 编译器是否可以自由地假设函数将始终返回? 编辑:整个事情取决于电子报,因此描述: 84 /*———- 85 * New-style error reporting API: to be used in this way: 86 * ereport(ERROR, 87 * […]

符合C标准的方式来访问空指针地址?

在C中,引用空指针是未定义行为,但是空指针值具有位表示,在某些体系结构中它使其指向有效地址(例如,地址0)。 为了清楚起见,我们将此地址称为空指针地址 。 假设我想在C中编写一个软件,在一个无限制访问内存的环境中。 假设我想在空指针地址处写一些数据: 我将如何以标准兼容的方式实现这一点? 示例案例(IA32e): #include int main() { uintptr_t zero = 0; char* p = (char*)zero; return *p; } 当使用带有-O3的 gcc与IA32e编译时,此代码将转换为 movzx eax, BYTE PTR [0] ud2 由于UB(0是空指针的位表示)。 由于C接近低级编程,我相信必须有一种方法来访问空指针地址并避免UB。 只是要清楚 我问的是标准对此有何看法,而不是如何以实现定义的方式实现这一点。 我知道后者的答案。