当我尝试除以0时,下面的代码没有捕获异常。我是否需要抛出异常,或者计算机是否在运行时自动抛出异常?
int i = 0;
cin >> i; // what if someone enters zero?
try {
i = 5/i;
}
catch (std::logic_error e) {
cerr << e.what();
}
答案 0 :(得分:52)
您需要自己检查并抛出异常。整数除以零在标准C ++中不是例外。
浮点数除以零,但至少具有处理它的特定方法。
ISO标准中列出的例外情况是:
namespace std {
class logic_error;
class domain_error;
class invalid_argument;
class length_error;
class out_of_range;
class runtime_error;
class range_error;
class overflow_error;
class underflow_error;
}
你会认为overflow_error
非常适合用零来表示。
但是5.6
(C++11
部分,虽然我认为这不会改变上一次迭代),但明确指出:
如果
/
或%
的第二个操作数为零,则行为未定义。
因此,可以抛出该(或任何其他)异常。它还可以格式化您的硬盘并嘲笑: - )
如果您想要实现这样的野兽,可以在以下程序中使用intDivEx
之类的内容:
#include <iostream>
#include <stdexcept>
// Integer division, catching divide by zero.
inline int intDivEx (int numerator, int denominator) {
if (denominator == 0)
throw std::overflow_error("Divide by zero exception");
return numerator / denominator;
}
int main (void) {
int i = 42;
try {
i = intDivEx (10, 2);
} catch (std::overflow_error e) {
std::cout << e.what() << " -> ";
}
std::cout << i << std::endl;
try {
i = intDivEx (10, 0);
} catch (std::overflow_error e) {
std::cout << e.what() << " -> ";
}
std::cout << i << std::endl;
return 0;
}
输出:
5
Divide by zero exception -> 5
你可以看到它抛出并捕获除以零的异常。
%
等价物几乎完全相同:
// Integer remainder, catching divide by zero.
inline int intModEx (int numerator, int denominator) {
if (denominator == 0)
throw std::overflow_error("Divide by zero exception");
return numerator % denominator;
}
答案 1 :(得分:14)
更新了ExcessPhase的评论
GCC(至少版本4.8)将允许您模拟此行为:
#include <signal.h>
#include <memory>
#include <iostream>
int main() {
std::shared_ptr<void(int)> handler(
signal(SIGFPE, [](int signum) {throw std::logic_error("FPE"); }),
[](__sighandler_t f) { signal(SIGFPE, f); });
int i = 0;
std::cin >> i; // what if someone enters zero?
try {
i = 5/i;
}
catch (std::logic_error e) {
std::cerr << e.what();
}
}
这会设置一个新的信号处理程序,它会抛出异常,并向旧信号处理程序shared_ptr
发送一个自定义删除&#39;当旧处理程序超出范围时恢复旧处理程序的函数。
您需要至少使用以下选项进行编译:
g++ -c Foo.cc -o Foo.o -fnon-call-exceptions -std=c++11
Visual C ++也可以让你做类似的事情:
#include <eh.h>
#include <memory>
int main() {
std::shared_ptr<void(unsigned, EXCEPTION_POINTERS*)> handler(
_set_se_translator([](unsigned u, EXCEPTION_POINTERS* p) {
switch(u) {
case FLT_DIVIDE_BY_ZERO:
case INT_DIVIDE_BY_ZERO:
throw std::logic_error("Divide by zero");
break;
...
default:
throw std::logic_error("SEH exception");
}
}),
[](_se_translator_function f) { _set_se_translator(f); });
int i = 0;
try {
i = 5 / i;
} catch(std::logic_error e) {
std::cerr << e.what();
}
}
当然,您可以跳过所有C ++ 11-ishness,并将它们放在传统的RAII管理结构中。
答案 2 :(得分:7)
据我所知,C ++规范没有提及除零除外的任何内容。我相信你需要自己做...
Stroustrup在“C ++的设计和演变”(Addison Wesley,1994)中说,“低级事件,例如算术溢出和除以零,假设由专用的低级机制处理,而不是这使得C ++能够在算术运算时与其他语言的行为相匹配。它还避免了在流水线严重的体系结构中出现的问题,例如除以零的事件是异步的。“
答案 3 :(得分:1)
您应该检查是否i = 0
而不是分开。
(可选择在检查之后,您可以抛出异常并稍后处理)。
答案 4 :(得分:0)
您需要使用throw
关键字手动抛出异常。
示例:
#include <iostream>
using namespace std;
double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}
int main ()
{
int x = 50;
int y = 0;
double z = 0;
try {
z = division(x, y);
cout << z << endl;
}catch (const char* msg) {
cerr << msg << endl;
}
return 0;
}
答案 5 :(得分:0)
setjmp
+ longjmp
https://stackoverflow.com/a/25601100/895245提到了可能从信号处理程序中抛出C ++异常的可能性,但是Throwing an exception from within a signal handler提到了一些警告,因此我会非常小心。
作为另一种潜在的危险可能性,您也可以尝试使用较旧的C setjmp
+ longjmp
机制,如下所示:C handle signal SIGFPE and continue execution
#include <csetjmp>
#include <csignal>
#include <cstring>
#include <iostream>
jmp_buf fpe;
void handler(int signum) {
longjmp(fpe, 1);
}
int main() {
volatile int i, j;
for(i = 0; i < 10; i++) {
struct sigaction act;
struct sigaction oldact;
memset(&act, 0, sizeof(act));
act.sa_handler = handler;
act.sa_flags = SA_NODEFER | SA_NOMASK;
sigaction(SIGFPE, &act, &oldact);
if (0 == setjmp(fpe)) {
std::cout << "before divide" << std::endl;
j = i / 0;
sigaction(SIGFPE, &oldact, &act);
} else {
std::cout << "after longjmp" << std::endl;
sigaction(SIGFPE, &oldact, &act);
}
}
return 0;
}
编译并运行:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out
输出:
i = 0
before divide
after longjmp
i = 1
before divide
after longjmp
i = 2
before divide
after longjmp
man longjmp
说,您可以从信号处理程序中longjmp
,但要注意以下几点:
POSIX.1-2008技术勘误2将longjmp()和siglongjmp()添加到异步信号安全功能列表中。但是,该标准建议避免使用信号处理程序中的这些功能,并继续指出 可以看出,如果这些信号是从信号处理程序中调用的,该信号处理程序中断了对非异步信号安全功能的调用(或某些等效操作,例如与从初始返回时发生的exit(3)等效的步骤) 调用main()),如果程序随后调用了非异步信号安全函数,则该行为是不确定的。避免未定义行为的唯一方法是确保以下一项:
从信号处理程序中长时间跳转之后,该程序不会调用任何非异步信号安全函数,并且不会从初始调用返回到main()。
在每次调用非异步信号安全函数期间,必须阻塞其处理程序执行长跳转的任何信号,并且从初始调用返回到main之后,不会调用任何非异步信号安全函数()。
另请参阅:Longjmp out of signal handler?
然而,Throwing an exception from within a signal handler提到使用C ++会有进一步的危险:
但是,setjmp和longjmp与异常和RAII(ctors / dtors)不兼容。 :(您可能会因此而导致资源泄漏。
所以您也必须非常小心。
我想这是信号处理程序很难做到的道理,除非您确切知道自己在做什么,否则应尽量避免使用它们。
检测浮点零除
也可以通过glibc调用检测浮点除以零:
#include <cfenv>
feenableexcept(FE_INVALID);
如What is difference between quiet NaN and signaling NaN?
所示这使它提高了SIGFPE以及像整数除以零,而不是仅仅默默地qnan和设置标志。
答案 6 :(得分:0)
这个呢?用 Clang 测试,GCC 抛出 SIGILL。
#include <iostream>
#include <cassert>
int main()
{
unsigned int x = 42;
unsigned int y = x;
y -= x;
x /= y;
std::cout << x << " != "<< *(&x) << std::endl;
assert (*(&x) == x);
}
答案 7 :(得分:-2)
do i need to throw an exception or does the computer automatically throws one at runtime?
您需要自己throw
例外,catch
它。 e.g。
try {
//...
throw int();
}
catch(int i) { }
或catch
您的代码抛出的异常。
try {
int *p = new int();
}
catch (std::bad_alloc e) {
cerr << e.what();
}
在您的情况下,我不确定是否有任何标准异常意味着除以零。如果没有这样的例外,那么你可以使用,
catch(...) { // catch 'any' exception
}
答案 8 :(得分:-4)
你可以做assert(2 * i != i)
这会抛出一个断言。如果你需要更高级的东西,你可以编写自己的异常类。