微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

当不确定的行为可以被认为是公认的并被接受的时候?

我们知道未定义的行为是什么,我们(或多或less)知道其中大部分原因(性能,跨平台兼容性)。 假设一个给定的平台,比如Windows 32位,我们可以考虑一个未定义的行为,在整个平台上是众所周知和一致的吗? 我知道没有一个普遍的答案,那么我会限制到两个常见的UB,我经常看到生产代码(从几年中使用)。

1) 参考 。 给这个union :

union { int value; unsigned char bytes[sizeof(int)]; } test;

像这样初始化:

test.value = 0x12345678;

然后访问:

堆栈和堆之间的区别

调用它的时候删除仿函数是否安全?

程序在缓冲区溢出时没有崩溃

for (int i=0; i < sizeof(test.bytes); ++i) printf("%dn",test.bytes[i]);

2) 参考 。 给定一个unsigned short*转换为(例如) float*并访问它( 引用 ,数组成员之间没有填充)的数组。

代码依赖于着名的 UBs(像那些) 按情况工作 (假设编译器可能会改变,肯定编译器版本肯定会改变),或者即使它们是跨平台代码的UB,它们依赖于平台特定的细节如果我们不改变平台,它不会改变)? 同样的推理也适用于未指定的行为(当编译器文档没有提到它的时候)?

编辑根据这篇文章开始从C99types的双关只是未指定的 ,而不是未定义的 。

首先,从标准的角度来看,任何编译器实现都可以自由地定义它喜欢的任何行为,从而产生未定义的行为。

其次, 为特定编译器实现编写的代码可以自由地使用该实现记录的任何行为; 但是,这样做的代码在其他实现上可能无法使用。

C的长期缺点之一是,虽然在很多情况下,在某些实现上可能有未定义行为的构造被其他人有效地处理,但只有极少数这样的情况提供了任何方式,通过这些方式,代码可以指定编译器赢得“应该拒绝汇编。 此外,在很多情况下,标准委员会允许全面的UB,尽管在大多数实施中,“自然”后果将受到更多限制。 考虑,例如(假设int是32位)

int weird(uint16_t x,int64_t y,int64_t z) { int r=0; if (y > 0) return 1; if (z < 0x80000000L) return 2; if (x > 50000) r |= 31; if (x*x > z) r |= 8; if (x*x < y) r |= 16; return r; }

如果上面的代码运行在一个简单地忽略整数溢出的机器上,那么传递50001,0x80000000L应该导致代码返回31; 传递50000,0x80000000L可能会导致返回50000,0x80000000L或24,具体取决于代码如何处理比较操作。 然而,C标准将允许代码在任何情况下做任何事情; 因此,有些编译器可能会确定在没有调用未定义行为的情况下,前两个之外的if语句都不会是真的,因此可以假定r总是零。 请注意其中一个推论会影响未定义行为之前的语句的行为 。

有一件事我真的很希望看到的是一个“实现约束”行为的概念,这将是未定义的行为和实现定义的行为之间的交叉:编译器将被要求记录某些构造的所有可能的后果旧的规则将是未定义的行为,但是 – 与实现定义的行为不同 – 不需要实现来指定将发生的特定事件; 实现将被允许指定一个特定的构造可能会有任意的不受约束的后果(完整的UB),但不鼓励这样做。 在类似整数溢出的情况下,一个合理的妥协就是说溢出的表达式的结果可能是一个“魔术”值,如果明确地说是类型转换,将产生一个任意(和“普通”)指示类型,但是可能看起来具有任意改变的值,这些值可以或不可表示。 编译器可以假定操作的结果不会是溢出的结果,而是不会推断操作数 。 为了使用模糊的类比,行为将类似于如果明确地类型化NaN可以产生任何非NaN结果的浮点。

恕我直言,C将大大受益于结合上述“实现约束”行为的概念与一些标准的预定义的宏,这将允许代码来测试一个实现是否在各种情况下对其行为作出任何特定的承诺。 此外,如果有一段代码可以请求特定的“方言”[ int大小,实现受约束的行为等等的组合)的标准方法,那将是有帮助的。 可以为任何平台编写一个编译器,这个平台可以根据请求使得提升规则像int正好是32位一样工作。 例如,给定的代码如下所示:

uint64_t l1,l2; uint32_t w1,w2; uint16_t h1,h2; ... l1+=(h1+h2); l2+=(w2-w1);

如果16位编译器使用16位在h1和h2上执行数学运算,则它可能是最快的;如果将64位w2结果与w2相减,则64位编译器可能是最快的,但是如果代码是为32位系统编写的,能够为其他两个系统生成编译器生成代码,它将像在32位系统上那样运行,比生成执行一些不同计算的代码更有帮助,无论如何后面的代码会快多少。

不幸的是,目前还没有任何标准的方法可以让代码要求这样的语义[在很多情况下这可能会限制64位代码的效率]; 最好的做法可能是明确地将代码的环境要求记录在某个地方,并希望使用代码的人看到它们。

未定义的行为主要意味着一个非常简单的事情,所讨论的代码的行为没有被定义,因此C标准没有提供任何可能发生的线索。 不要多搜索它。

如果C标准没有定义什么东西,那么你的平台可能就是扩展。 所以如果你在这种情况下,你可以在该平台上使用它。 但是,确保它们记录了该扩展,并且在下一版本的编译器中不会更改它。

你的例子有几个原因是有缺陷的。 正如在注释中所讨论的那样, union是用于打字的,特别是对任何字符类型的访问都是允许的。 你的第二个例子是非常糟糕的,因为除了你所暗示的以外,在我所知道的任何平台上这都不是可接受的。 short , float一般具有不同的对齐属性,使用这样的东西几乎肯定会使你的程序崩溃。 那么,第三,你是在Windows上为C辩论的,因为它不遵循C标准。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐