Tag: 未定义 行为

C运算符+ =序列点?

这是定义的行为吗? *p += *p–; 并且,如果是,它是否等于{ p[0] += p[0]; –p; } { p[0] += p[0]; –p; } { p[0] += p[0]; –p; }或{ p[-1] = p[0]; –p; } { p[-1] = p[0]; –p; } { p[-1] = p[0]; –p; } ? 我猜测是否定义取决于+=是否具有隐式序列点,如果有,我的猜测是第二个块应该是正确的。 编辑:我认为这不是建议问题的重复,因为主要问题是什么是序列点以及如何影响行为。 在我的情况下,我清楚地知道序列点是什么,问题是关于+=运算符是否具有隐式序列点。

超出翻译限制是不确定的行为,是否有检查工具来找到它?

原始问题: 我正在搜索C90标准,以便在编写高可移植代码时注意事项,同时对编译器供应商的良好意愿缺乏信任,并假设我的软件有时可能会杀死某人,如果我做错了。 让我们说我有点偏执。 目前我在考虑“翻译限制”(5.2.4.1 ANSI / ISO 9899:1990)。 正如标准中所指出的那样:“ ansi C是否限制了程序中外部变量的数量? ”,这些是标准符合实现的最低要求。 现在另一方面,这意味着,任何实现都不需要做更多 – 如果我想确保我的代码适用于任何confrom实现,这些限制代表我的绝对限制。 到目前为止很烦人。 因此编译器供应商选择的限制等于或高于最低要求的转换限制。 如果超出特定实施的实施定义的转换限制,现在会发生什么? 在我的ANSI / IO 9899:1990(C90)副本中,我没有找到任何东西,所以我认为它是“3种类型的未定义行为”(通过省略)。 另一方面,这不是第一次,我误解了标准或没有找到合适的通道。 所以这是我的问题: IS是否超过C90中特定实现未定义行为的转换限制? C90行为是否适用于C95 / C96以及新迭代C99和C11的修正版本? 有没有人看过那里的检查工具,检查最小的或(工具)用户定义的限制? 超出原始问题的方面: 答案和评论中有趣的方面: 1)正如迈克尔·伯尔在对该问题的直接评论中指出的那样,根据C标准(我只检查了没有更正的C90,以及C99草案,Michael 在这里引用),符合C的实现只需要接受一个程序,它同时包含所有限制,在最严格的解释中取消任何最小限制保证。 2)正如rubenvb和Keith Thompson指出的那样,某些质量的实现应该为案例提供诊断,超出它们的实现定义限制,特别是如果不符合最低要求(rubenvb在评论中链接了MSVC的示例) 。 3)由于超出编译器限制可能是未定义的行为,但肯定会导致某些错误,转换限制适用于我的某段代码的“变量”的值代表重用的前提条件。 我个人的策略来处理它们 1)因此,对于最大的偏执狂,我会愚弄自己,并惹恼编译器供应商的支持,请求保证我,实现选择的限制适用于任何程序。 🙁 2)因此,我将研究编译器文档以及编译器支持获得确认的痛苦程度: – 对于每个转换限制,如果超出,将引发诊断,并且 – 因为它是未定义的行为,如果每个超出翻译限制的实例都会引发诊断 – 或者另一个错误已经阻止了编译。 3)因此,我将尝试开发一个工具(或者如果我真的必须开发自己),测量这些值,并将它们作为我的程序的代码重用的前提条件。 正如Keith Thompson在本回答中指出的那样,一些价值观可能需要更深入地了解实施如何实施。 我不能完全确定在这种情况下除了2)中的行为之外还能提供什么帮助。但是,据我所知,我必须进行测试 – 但我只需要测试是否有UB(没有诊断),如果这是在这种情况下,成功的测试不能保证一般情况下的正确性。 回答说: 是的,它是未定义的行为。 […]

未定义的行为是否适用于asm代码?

假设您知道您的软件只能在两个补码机器上运行,其中已经很好地定义了带符号的溢出行为。 签名溢出仍然是C和C ++中未定义的行为,编译器可以自由地用“ret”替换你的整个程序,开始核战争,格式化你的驱动器,或让恶魔飞出你的鼻子。 假设您已在内联asm中签名溢出,您的程序是否仍然调用UB? 如果是,那么单独编译和链接汇编程序呢?

在刚刚超过数组末尾的指针上调用长度为零的memcpy是否合法?

正如在其他地方所解释的那样,即使长度参数为零,调用具有无效或NULL指针的memcpy函数也是未定义的行为。 在这样一个函数的上下文中,尤其是memcpy和memmove ,是一个指针刚刚超过数组末尾的一个有效指针? 我问这个问题,因为一个指针刚好超过一个数组的末尾是合法的(相反,例如一个指针超过一个数组末尾的两个元素),但你不能取消引用它,但是脚注106 ISO 9899:2011表明这样的指针指向程序的地址空间,这是指针根据§7.1.4有效所需的标准。 这种用法发生在我想在一个数组的中间插入一个项目的代码中,要求我在插入点之后移动所有项目: void make_space(type *array, size_t old_length, size_t index) { memmove(array + index + 1, array + index, (old_length – index) * sizeof *array); } 如果我们想在数组的末尾插入,则index等于length , array + index + 1指向刚好超过数组的末尾,但复制元素的数量为零。

获取未初始化指针的地址是未定义的行为吗?

N1570声明这是未定义的行为: §J.2/ 1具有自动存储持续时间的对象的值在不确定时使用(6.2.4,6.7.9,6.8)。 在这种情况下,我们的指针具有不确定的值: §6.7.9/ 10如果没有显式初始化具有自动存储持续时间的对象,则其值是不确定的。 如果未显式初始化具有静态或线程存储持续时间的对象,则: – 如果它有指针类型,则将其初始化为空指针; 然后,我假设以下测试程序显示未定义的行为: #include int main(void) { char * ptr; printf(“%p”, (void*)&ptr); } 我的动机是strtolfunction。 首先,让我引用与endptr参数相关的endptr : §7.22.1.4/ 5如果主题序列具有预期forms且base的值为零,则根据6.4.4.1的规则将以第一个数字开头的字符序列解释为整数常量。 […]指向最终字符串的指针存储在endptr指向的对象中,前提是endptr不是空指针。 §7.22.1.4/ 7如果主题序列为空或没有预期的forms,则不进行转换; 如果endptr不是空指针,则nptr的值存储在endptr指向的对象中。 这意味着endptr需要指向一个对象,并且该endptr在某个时候被解除引用。 例如, 此实现是这样做的 : if (endptr != 0) *endptr = (char *)(any ? s – 1 : nptr); 然而, 这个高度赞成的答案以及这个手册页都显示endptr被传递给strtol未初始化。 是否有一个exception导致这种未定义的行为?

数组外的指针比较的基本原理是UB

因此,标准(参考N1570 )说明了以下关于比较指针的内容: C99 6.5.8 / 5关系运算符 比较两个指针时,结果取决于指向的对象的地址空间中的相对位置。 … [在聚合中剪切明显的比较定义] … 在所有其他情况下,行为是未定义的。 这个UB实例的基本原理是什么,而不是指定(例如)转换为intptr_t并进行比较? 是否存在一些机器架构,其中指针的合理总排序难以构建? 是否存在一些优化或分析,不受限制的指针比较会阻碍? 这个问题的删除答案提到这块UB允许跳过段寄存器的比较并且仅比较偏移。 保存特别有价值吗? (同样删除的答案,以及此处的答案,请注意,在C ++中, std::less等是实现指针总顺序所必需的,无论普通比较运算符是否执行。)

C11表达式中的赋值运算符排序

介绍 C11标准(ISO / IEC 9899:2011)在表达式中引入了新的副作用测序定义( 参见相关问题 )。 序列点概念已经在关系之前进行了序列补充,并且在关系之后 进行了 排序,这些关系现在是所有定义的基础。 第6.5节“表达式”,第2点说: 如果对标量对象的副作用相对于对同一标量对象的不同副作用或使用相同标量对象的值进行的值计算未进行排序,则行为未定义。 如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的。 稍后,第6.5.16节“分配操作员”,第3点指出: 在左右操作数的值计算之后,对更新左操作数的存储值的副作用进行排序。 对操作数的评估是不确定的。 问题 第一个引用的段落(6.5 / 2)由两个例子支持(与C99标准相同): 第一个例子 a[i++] = i; //! undefined a[i] = i; // allowed 这可以通过定义轻松解释: 如果相对于使用相同标量对象的值的(…)值计算,对标量对象的副作用未被排序,则行为未定义。 (6.5 / 2), 对操作数的评估是不确定的。 [在任务内](6.5.16 / 3)。 因此, i++ (LHS)的副作用与i (RHS)无关,这给出了未定义的行为。 第二个例子 i = ++i + 1; //! undefined i = i […]