【问题标题】:How to catch divide-by-zero error in Visual Studio 2008 C++?如何在 Visual Studio 2008 C++ 中捕获除零错误?
【发布时间】:2010-12-22 09:55:34
【问题描述】:

如何在 Visual Studio 2008 C++ 中捕获除零错误(而不是其他错误;并且能够访问异常信息)?

我试过了:

try {
  int j=0;
  int i= 1/j;//actually, we call a DLL here, which has divide-by-zero
} catch(std::exception& e){
  printf("%s %s\n", e.what()); 
}  catch(...){
  printf("generic exception");
}

但这是通用的 ... catch 块。我知道特定于 MS 的 __try 在这里可能很有用,但我更喜欢标准 C++,而且无论如何我都有析构函数阻止使用 __try。

澄清:为了讨论目的,上面的代码被简化了。实际上,被零除是一个错误,它发生在我没有源代码的第三方 DLL 中。该错误取决于我传递给库的参数(复杂结构的句柄),但不是以任何明显的方式。所以,我希望能够优雅地恢复。

【问题讨论】:

  • "我更喜欢标准 C++" 那么你必须不希望除以零,因为这是未定义的行为。它是特定于平台的,它根本就抛出一个异常,所以如果捕获它的方法也是特定于平台的,那也不是什么大问题。在不被零除的平台上,您无法从 dll 中的此错误中恢复,因此您不需要独立于平台的恢复方式。
  • @Steve Jessop 在实际示例中,除以零在我没有源代码的 DLL 中很深。

标签: c++ exception visual-studio-2008 try-catch


【解决方案1】:

您可以使用try-except Statement。 但是,不要忘记您需要为每个线程设置它。

【讨论】:

    【解决方案2】:

    要在 Visual C++ try->catch (...) 中捕获除零异常,只需在项目设置中启用 /EHa 选项。请参阅项目属性 -> C/C++ -> 代码生成 -> 将 Enable C++ Exceptions 修改为“Yes With SEH Exceptions”。就是这样!

    在此处查看详细信息: http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx

    【讨论】:

      【解决方案3】:

      试试下面的代码:

      try
      { 
        const int j=0;
        if (j == 0) { throw std::exception("j was 0"); } 
        const int i= 1/j;    
      }
      catch(std::exception& e)
      { 
        printf("%s %s\n", e.what());  
      }
      catch(...)
      { 
        printf("generic exception"); 
      }
      

      当然,如果您可以毫无例外地这样做,您可以这样做:

      const int j = 0;
      if (j == 0)
      {
        /* do something about the bad pre-condition here */
      }
      else
      {
        const int i = 1 / j;
      }
      

      根据澄清进行编辑:您必须弄清楚您交给第三方的输入是什么导致他们事先被零除,并在调用他们的函数之前处理它。

      【讨论】:

        【解决方案4】:

        一个好的方法是使用安全的面向对象的包装器,比如SafeInt。在 Visual Studio 2010 中也是 seems to be integrated

        更新:
        如果除零发生在第三方代码中,您唯一的选择是 SEH 或与 answered by Seb Rose 等效的东西

        【讨论】:

          【解决方案5】:

          假设您不能简单地修复异常生成代码的原因(可能是因为您没有该特定库的源代码,也可能是因为您无法在输入参数引起问题之前对其进行调整) .

          您必须跳过一些障碍才能按照您的意愿完成这项工作,但这是可以完成的。

          首先,您需要通过调用_set_se_translator()(请参阅here)安装结构化异常处理转换函数,然后您可以检查在发生 SEH 异常时传递的代码并引发适当的 C++ 异常。

          void CSEHException::Translator::trans_func(
              unsigned int code, 
              EXCEPTION_POINTERS *pPointers)
          {
             switch (code)
             {
                 case FLT_DIVIDE_BY_ZERO : 
                    throw CMyFunkyDivideByZeroException(code, pPointers);
                 break;
             }
          
             // general C++ SEH exception for things we don't need to handle separately....
             throw CSEHException(code, pPointers);
          }
          

          然后,您可以以正常方式在 C++ 中简单地捕获您的CMyFunkyDivideByZeroException()

          请注意,您需要在要翻译异常的每个线程上安装异常翻译功能。

          【讨论】:

          • 只想提一下_set_se_translator 在每个线程的基础上工作,并且只有在您控制所有线程的创建时才有用。在调用 DLL 时,这很可能会落在您的脚上。您根本无法翻译他们的 SEH。例如,Excel C AddIn DLL 可能因此很容易使整个 Excel 进程崩溃。只有catch(...) 块会捕获它们 - 但您和 Excel 都不知道捕获了哪个 SEH。它可能是EXCEPTION_ACCESS_VIOLATION(在其他操作系统上别名为SIGSEGV),在这种情况下,无论如何你都注定要失败。我认为_set_se_translator 会产生一种虚假的安全感。
          • Andreas,这是真的,但是如果你在一个不属于你的线程上,那么你可以将你的函数包装在一个 SEH 异常处理程序中,并将它作为一个正常的 SEH 异常处理,因此你一定要知道你捕捉到的异常类型(如果你想在一个也处理 C++ 异常的函数中捕捉 SEH 异常,你必须跳过障碍并且可能创建一个额外的 shim 函数)。哦,EXCEPTION_ACCESS_VIOLATION 并没有结束游戏,必然 - 操作系统只是告诉您您无法访问内存。
          • 我的意思是在catch(...) 的情况下,您自然不知道类型是什么。无论如何...除非您的程序是调试器、间谍软件或病毒,否则您为什么要访问不属于您的内存?在大多数情况下,程序很糟糕。即使在“无害”的 NULL 指针访问的情况下,程序也无法继续,因为它会再次崩溃。
          • 我并不是说你会想要访问你不拥有的内存,只是“你注定要失败”有点强,因为你总是可以创建一个迷你转储和在许多情况下,可能仍会完全关闭。
          • 这对你有用,因为你已经得到了异常,但是对于任何试图使用它却没有得到任何东西的人,你可能必须用一些丑陋的代码来启用浮点异常。这是一个示例(这是我们在 VS2015 中使用的,对于错误的格式表示抱歉): // 设置浮点除以零异常 _clearfp(); uint32 control_word = 0; _controlfp_s(&control_word, _MCW_EM, _MCW_EM); _controlfp_s(0, ~(uint32) (_EM_UNDERFLOW | _EM_OVERFLOW | _EM_ZERODIVIDE | _EM_INVALID), _MCW_EM);
          【解决方案6】:

          C++ 本身不会将除零作为异常处理。

          引用 Stroustrup:

          "低级事件,例如算术 溢出并被零除,是 假定由专门的处理 下层机制而不是通过 例外。这使 C++ 能够匹配 其他语言的行为 它涉及算术。它也避免了 严重发生的问题 流水线架构,其中事件 例如除以零是 异步。”

          “C++ 的设计和演变”(Addison Wesley,1994 年)

          在任何情况下,异常永远不会替代适当的前置条件处理。

          【讨论】:

          • 谢谢,您的回答是正确的。但是我无法处理前提条件,因为除以零是一个错误,它发生在我没有源代码的第三方 DLL 中。 (我上面给出的代码为了讨论目的(过度)简化了。)我需要从错误中恢复过来。
          【解决方案7】:

          您可以使用结构化异常处理(使用 __try 等),也可以安装结构化异常处理程序转换器:_set_se_translator

          这两个都是特定于操作系统的。

          【讨论】:

          【解决方案8】:

          您不能使用标准 C++ 做到这一点,因为它不是标准 C++ 异常。这是一个结构化的异常。对于标准 C++ 异常,必须从代码中执行 throw exception;

          【讨论】:

            【解决方案9】:

            为什么不先检查一下呢?与用于异常处理的上下文切换相比,简单的j == 0 的性能将是微不足道的。

            【讨论】:

            • 谢谢,您的回答是正确的。但我无法处理前提条件,因为除以零是一个错误,它发生在我没有源代码的第三方 DLL 中。 (出于讨论目的,我上面给出的代码(过度)简化了。)我需要从错误中优雅地恢复。
            猜你喜欢
            • 2021-08-03
            • 1970-01-01
            • 2014-01-12
            • 2019-09-19
            • 1970-01-01
            • 2011-08-26
            • 2011-06-12
            • 2013-02-22
            • 2010-11-28
            相关资源
            最近更新 更多