20

块是否try-catch捕获分段错误错误?

我正在使用下面给出的函数读取文本文件,但有时文件为空并且程序崩溃。我希望程序继续运行并在该文件为空或正在使用时提供另一个文件。

Path2D read_gesture(const char* filename)
{
    Path2D path;
    //MultiStrokeGesture MultiStrokes;

    vector<string> text_file;

    int no_of_paths=0;
    std::ifstream ifs(filename);

    for (std::string line; std::getline(ifs, line); )
    {
        no_of_paths=no_of_paths+1;
        double a, b;
        stringstream ss(line);
        if (!(ss >> a >> b)) {cout<<"wrong format"<<endl;}
        std::cout << "You said, " << a << ", " << b << ".\n";
        path.push_back(Point2D(a,b));

    }
    cout<<"saving gesture"<<endl;
    return path;


}

我试过类似的东西:

Path2D path;
try 
{
    path=read_gesture("test.txt");
}
catch(int e)
{
    path=read_gesture("test2.txt");
}

但程序仍然崩溃。问题可能是什么?

  • 稍微更正一下,调用的文件和 的文件catch不一样try,是错别字。
4

6 回答 6

53

C++try-catch块仅处理 C++ 异常。像分段错误这样的错误是较低级别的,try-catch 会忽略这些事件,并且表现得与没有 try-catch 块一样。

于 2012-10-19T16:10:58.217 回答
15

try/catch 只捕获 C++ 异常。只有当您的程序执行非法操作并调用未定义的行为时,才会发生分段错误。

请记住,未定义的行为可以以不同的方式表现出来,包括不崩溃。你很幸运,你的程序崩溃了,通知你有一些东西需要修复,但程序可能不会崩溃;你不能让你的后备代码依赖于崩溃。

适当的做法是不要像处理异常那样处理崩溃,而是确保即使输入不是您所期望的,您的程序也不会做任何非法的事情。在这种情况下,您需要更改代码,以便知道文件何时为空并需要提供另一个。


通常有一种方法可以处理分段错误,但它并不打算进行您正在寻找的那种恢复。机制是信号。您可以安装一个信号处理程序,该处理程序在引发指定信号时执行,例如用于分段错误的 SIGSEGV。但是,除非您使用 std::raise 显式提升它,否则不需要实际出现这样的信号。此外,当实现引发信号时,您可以在信号处理程序中执行的操作受到严格限制;

If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than the abort function, the _Exit function, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a SIG_ERR return, the value of errno is indeterminate.

于 2012-10-19T17:56:26.733 回答
8

如果你的程序有分段错误,而且这不是你故意做的,那么你就无能为力了。你不能抓住它,即使你能抓住它,你也不能在之后继续这个程序。

此外,以下代码有一个非常严重的问题:

try {
    path=read_gesture("test.txt");
}
catch(int e) {
    path=read_gesture("test.txt");
}

“如果一开始不成功,再试一次”对人类来说是一个很好的座右铭,但计算机每次都以完全相同的方式做事。如果一个操作失败了,除非是暂时性的失败(比如网络故障),再试一次都是徒劳的。

您唯一的选择是编写一个正确的程序,因为正确的程序不会出现段错误。如果你想找到段错误,你可以在 Valgrind 或 GDB 中运行程序,这两者都应该给出充满线索的回溯(但你必须用你的头脑来找到你程序中真正的错误)。

另一种选择是使用一种不会出现段错误的语言,如 Java、C#、Python、Ruby、Haskell、JavaScript、Go、Rust,或者除了 C 或 C++ 之外的几乎所有现在使用的语言。

脚注:这有点简化,因为实际上可以编写得到分段错误的正确程序。然而,这不是你在做什么。

于 2012-10-19T16:18:29.533 回答
2

您可以尝试添加一个小测试来查看 ifs 是否有效:

#include <iostream>
#include <fstream>

int main(int argc, char * argv[]){
   std::ifstream ifs( "notExists" );
   if( ifs.is_open()) {
      char line[1024];
      while(! ifs.fail() && ifs.getline( line, sizeof( line ))) {
        std::cout << line << std::endl;
      }
   }
   else {
      std::cout << "file doesn't exists." << std::endl;
   }
   std::cout << "done." << std::endl;
    return 0;
}

该程序运行并输出:

file doesn't exists.
done.

布尔 std::ifstream::is_open();

有关详细信息,请参阅getline全局函数,如果它失败,它会设置一些未在此处检查的位。

通过修改内部状态标志来发出错误信号:

  • eofbit:在其操作过程中到达字符源的末尾。
  • failbit:没有提取字符,因为过早地找到了结尾。请注意,某些 eofbit 情况也会设置失败位。
  • badbit:发生了上述以外的错误。

此外,在任何这些情况下,如果使用 is 的成员函数 ios::exceptions 设置了适当的标志,则会引发 ios_base::failure 类型的异常。

于 2012-10-19T16:19:04.770 回答
1

首先,您可以(阅读应该)以永远不会产生异常(例如分段错误)的方式编写代码。

首先,您应该检查所有可能无效的指针(例如,类的用户将调用的类的公共函数可能会收到一些无效的指针,但内部实现可能会假定该指针已被检查)。

其次,您应该检查可能失败的功能的结果,例如malloc可能无法分配内存或您尝试打开的文件可能被删除或您没有打开它的权限,甚至打开后它的数据可能是无效的,所以你应该检查你的行动的结果。C++ 中的这个过程比 C 容易得多,例如new在失败时抛出异常,或者在任何错误或 eof 的情况下stream都可以转换为false

但是要回答您的问题,通常catch只阻止捕获类型化的异常,但在某些编译器catch(...)中也可能捕获诸如分段错误之类的异常,甚至将这些异常转换为 C++ 异常,例如在 Windows 平台下,您可以使用_set_se_translator为类似这样的异常设置全局翻译器C++ 异常和使用 MSVC,您还可以编译您的程序以/EHa使其能够捕获此类异常,catch(...)但所有这些都是特定于特定平台或编译器的扩展,因此可以正确编写您的代码,并且永远不会想到这样的方法来解决您的问题问题。

于 2012-10-19T16:40:49.927 回答
-1

尝试检查它是否可以捕获catch(...){cout<<'catched';}

也试试这条线,这样可以防止推送错误的格式点:

if (!(ss >> a >> b)) {cout<<"wrong format"<<endl; continue;}

于 2012-10-19T16:19:29.257 回答