C / C ++可能充满未定义的行为。一方面,这很糟糕,因为它可能导致意外的行为。另一方面,它允许进行积极的优化,通常在使用C / C ++时,您会对速度感兴趣。
编写断断续续的测试用例可能很困难-它可能涉及奇怪的体系结构或编译器,而这种架构或编译器已不复存在。同样,在任何明智的体系结构上看起来 “它都不会引起任何问题”。
但是,在某一点或另一点上,您将更改平台(例如-您想移动,以便移植到ARM或要加快处理速度并使用GPU)。此时,事情可能开始破裂,您需要对其进行调试。它可能像将编译器更新为较新版本一样琐碎(并且您可能需要/需要它)。
有问题的代码是:
int d[16];
int SATD (void)
{
int satd = 0, dd, k;
for (dd=d[k=0]; k<16; dd=d[++k]) {
satd += (dd < 0 ? -dd : dd);
}
return satd;
}
在最后一次迭代期间d[++k] => d[++15] => d[16]
访问。由于通常下一个元素是合法内存(就分页而言,不是C内存模型),即使是琐碎的编译器也不会产生任何具有奇怪行为的可执行文件。编写测试用例的唯一方法是找到具有与C完全相同的内存模型(可能是VM)的平台。
但是gcc 4.8 prerelase看到d[++k]
在每个循环中执行。因此,k < 16
否则访问将是非法的,并且馈给编译器的程序的合法性是合同的一部分。因此,给定假设,循环条件始终为真,因此它是无限循环。这听起来可能很奇怪,但这是完全合法的优化-发出system("dd if=/dev/zero of=/dev/sda"); system("format c:")
也将是loop的正确替代。您可以选择更多微妙的方式来表现行为。例如,在关于事务性内存的讲座中,我记得当两个线程增加相同的值时,演示者尝试了几次以获取错误的值。
但是,与某些语言相对的C / C ++已标准化,因此可以参考客观来源来解决此类争议:
- 如果您认为这不是问题,请尝试使用C / C ++标准进行证明(即,它会导致定义的值和行为)
- 如果他认为这是一个问题,请他使用C / C ++标准进行证明(即,它导致未定义的值或行为)
通常,如果您的团队使用C / C ++编写,手头准备标准很有用-即使团队专家有时也会在其中找到一些奇怪的东西。