visualis除以零并伴有运行时错误,这在c++中非常有用
visual Is dividing by zero accompanied with a runtime error ever useful in C++?
根据c++标准(5/5)除零是未定义的行为。现在考虑这段代码(这里有很多无用的语句来防止编译器优化代码):
int main()
{
char buffer[1] = {};
int len = strlen( buffer );
if( len / 0 ) {
rand();
}
}
Visual c++编译if
-语句如下:
sub eax,edx
cdq
xor ecx,ecx
idiv eax,ecx
test eax,eax
je wmain+2Ah (40102Ah)
call rand
显然编译器看到代码是要除以0 -它使用xor x,x
模式将ecx
归零,然后作为整数除法的第二个操作数。这段代码肯定会在运行时触发"整数除零"错误。
在我看来,这种情况(当编译器知道代码总是被0除时)值得一个编译时错误——标准并没有禁止这种情况。这将有助于在编译时而不是在运行时诊断此类情况。
然而,我和其他几个开发人员谈过,他们似乎不同意——他们的反对意见是"如果作者想除以0得到……嗯…测试错误处理?"
在没有编译器意识的情况下故意除以零并不难——使用__declspec(noinline)
Visual c++特定函数装饰器:
__declspec(noinline)
void divide( int what, int byWhat )
{
if( what/byWhat ) {
rand();
}
}
void divideByZero()
{
divide( 0, 0 );
}
更具可读性和可维护性。当"需要测试错误处理"时,可以使用该函数,并且在所有其他情况下都有一个不错的编译时错误。
我错过了什么吗?是否有必要允许编译器知道除零的代码的发射?
可能有代码在从未调用的函数中意外被零除(例如,由于某些平台特定的宏扩展),并且这些将不再与您的编译器编译,使您的编译器变得不那么有用。
而且,我在实际代码中看到的大多数除零错误都是与输入相关的,或者至少不适合静态分析。也许不值得进行检查。
除以0是未定义的行为,因为它可能在某些平台上触发硬件异常。我们都希望有一个性能更好的硬件,但是因为没有人认为有-INF/+INF和NaN值的整数是合适的,所以这是相当没有意义的。
现在,因为是未定义的行为,有趣的事情可能会发生。我鼓励您阅读Chris Lattner关于未定义行为和优化的文章,我将在这里给出一个快速示例:
int foo(char* buf, int i) {
if (5 / i == 3) {
return 1;
}
if (buf != buf + i) {
return 2;
}
return 0;
}
因为i
被用作除数,所以它不为0。因此,第二个if
通常为真,可以被优化掉。
面对这样的变换,任何希望相同的除以0的行为的人…会非常失望的。
在整型(int, short, long
等)的情况下,我想不出有意立即除以零的任何用途。
然而,对于符合ieee标准的硬件上的浮点类型,显式除以零是非常有用的。你可以用它来产生正极&负无穷(+/- 1/0),而不是一个数字(NaN, 0/0)值,这可能非常有用。
在排序算法的情况下,可以使用无穷大作为初始值,表示大于或小于所有可能的值。
出于数据分析的目的,您可以使用nan来指示丢失或无效的数据,然后可以优雅地处理这些数据。例如,Matlab使用显式NaN值来抑制图中缺失的数据等。
虽然您可以通过宏和std::numeric_limits(在c++中)访问这些值,但是能够自己创建它们是有用的(并且允许您避免大量的"特殊情况"代码)。它还允许标准库的实现者避免诉诸黑客手段(例如手动组装正确的FP位序列)来提供这些值。
如果编译器检测到除0,则编译器错误绝对没有问题。与您交谈的开发人员是错误的-您可以将这种逻辑应用于每个编译错误。除以0是没有意义的
在编译时检测除零是你想要的编译器警告。这真是个好主意。
我和microsoftvisualc++没有任何关系,但是g++ 4.2.1 确实做了这样的检查。试着编译:
#include <iostream>
int main() {
int x = 1;
int y = x / 0;
std::cout << y;
return 0;
}
它会告诉你:
test.cpp: In function ‘int main()’:
test.cpp:5: warning: division by zero in ‘x / 0’
但是考虑到错误是一个滑坡,懂行的人知道不要花太多的业余时间去爬它。考虑一下为什么当我写
时,c++没有什么可说的:int main() {
while (true) {
}
return 0;
}
你认为它应该编译,还是给出一个错误?应该总是给出警告吗?如果您认为它必须干预所有这些情况,我急切地等待您编写的编译器的副本,该编译器只编译保证成功终止的程序!: -)
- 以相反的顺序打印数组 - 为什么这在C++中是错误的?
- 这在 C++ 类和对象中指向此指针
- 这在C++是什么类型的错误?
- 这在C++ "It does not own the underlying data, and so is cheap to copy or assign"中意味着什么
- 为什么这在 c++ 中不起作用,但在 python 中工作
- 这->...在模板包装类中必需
- 这"在比赛中为m,n"做什么?
- 这在技术上可能是可能的
- 这在 C# 中意味着什么以及如何使用它
- 如果不允许我分配 rvalues 来引用为什么以下代码片段有效,这在内部如何工作?
- 是返回*这在 C++ 中是安全的
- 通过值或通过引用返回类对象,这在这里会更快
- 这在 C++ 中是如何工作的,并且在 JavaScript 中有任何粗略的等价物?
- 在插入时同时迭代一个映射,这在什么方面是不安全的
- 模板中的友元函数(为什么这在Visual Studio中失败,而不是GCC和Clang)
- 这在C++中不正确吗?
- 返回指针、返回值或传入引用,这在C++中很优雅
- 这在Visual Studio中是否被视为.NET或IL
- 这在 C 语言中是什么意思
- 为什么这在 c++ 中返回零