在 C++ 中捕获分段错误
Posted
技术标签:
【中文标题】在 C++ 中捕获分段错误【英文标题】:Catch Segmentation fault in c++ 【发布时间】:2012-10-10 07:16:41 【问题描述】: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
调用的文件不一样,是错字。
【问题讨论】:
问“如何防止崩溃”是没有意义的。唯一的答案是“编写一个正确的程序”。catch(int e)
不会像 std::exception
这样捕获 c++ 异常,它会捕获整数
您应该修复异常的来源,而不是试图捕捉它。如果您的程序中有分段错误,那么您是最有可能的原因。 (不是想对你刻薄,这是最可能的原因。:))
如果read_gesture
可能产生异常并且在catch 块中,你应该在那里还有另一个try-catch 块,以此类推......尝试做一些不同的事情(那不是' t 生成异常!)
您在 try
和 catch
块中调用相同的代码。如果您确实捕获了异常,则说明发生了严重错误,因此您应该最后做的事情就是再次做同样的事情。异常处理是关于保留控制权并纠正问题或优雅退出。这就是精神错乱的定义:一遍又一遍地做同样的事情,却期待不同的结果。
【参考方案1】:
C++ try-catch
块仅处理 C++ 异常。分段错误之类的错误是较低级别的,try-catch 会忽略这些事件,其行为与没有 try-catch 块相同。
【讨论】:
如果你用 /EHs 选项编译?【参考方案2】:try/catch 只捕获 C++ 异常。只有当您的程序执行非法操作并调用未定义的行为时,才会发生分段错误。
请记住,未定义的行为可以以不同的方式表现出来,包括不崩溃。你很幸运,你的程序崩溃了,通知你需要修复一些东西,但程序可能不会崩溃;你不能让你的后备代码依赖于崩溃。
适当的做法是不要像处理异常那样处理崩溃,而是确保即使输入不是您所期望的,您的程序也不会做任何非法的事情。在这种情况下,您需要更改代码,以便知道文件何时为空并需要提供另一个。
通常有一种方法可以处理分段错误,但它并不打算进行您正在寻找的那种恢复。机制是信号。您可以安装一个信号处理程序,该处理程序在引发指定信号时执行,例如用于分段错误的 SIGSEGV。但是,除非您使用 std::raise 显式提升它,否则不需要实际出现这样的信号。此外,当信号由实现引发时,您可以在信号处理程序中执行的操作受到严格限制;
如果信号不是由于调用 abort 或 raise 函数,如果信号处理程序引用,则行为未定义 到具有静态存储持续时间的任何对象,而不是通过分配一个 声明为 volatile sig_atomic_t 的对象或信号的值 处理程序调用标准库中的任何函数,而不是 abort 函数、_Exit 函数或带有 第一个参数等于信号对应的信号编号 这导致了处理程序的调用。此外,如果这样的电话 到信号函数导致 SIG_ERR 返回,errno 的值 是不确定的。
【讨论】:
确切地说,简化“tl;博士很高兴您的代码段错误,因为这可以防止更多潜在的损坏,您不能处理未定义的行为。这不是它的工作原理。”我只是要从自我中添加一些东西-尝试使用最简单的语言-处理器会将每条数据解释为命令,因此如果您 seg 失败,您的处理器可能会从内存中获得随机垃圾,但不是您想要的,这将导致它做一些随机的事情,包括弄乱其他程序和操作系统。这就是为什么每次尝试执行此操作时都会触发 seg 错误(希望如此)。为了防止损坏。【参考方案3】:如果您的程序存在分段错误,而这不是您故意做的,那么您将无能为力。你抓不到它,即使你能抓到,之后你也无法继续程序。
另外,下面的代码有一个很严重的问题:
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++。
脚注:这有点简化,因为实际上可以编写得到分段错误的正确程序。然而,这不是你正在做的。
【讨论】:
【参考方案4】:你可以尝试添加一个小测试,看看 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.
bool std::ifstream::is_open();
更多信息请参见getline 全局函数,如果失败,则设置一些未在此处检查的位。
通过修改内部状态标志来发出错误信号:
eofbit:在其操作过程中到达字符源的末尾。 failbit:未提取字符,因为过早地找到了结尾。请注意,某些 eofbit 情况也会设置 failbit。 badbit:发生了上述以外的错误。此外,在任何这些情况下,如果已使用 is 的成员函数 ios::exceptions 设置了适当的标志,则会引发 ios_base::failure 类型的异常。
【讨论】:
重点是什么——添加的条件完全没有效果。 因为如果流不好,getline
无论如何都会失败,并且它将返回在测试中评估为false
的流对象。所以,没必要。充其量您可以在程序开头添加一个if (!ifs)
以生成有意义的错误消息。
当它失败时,这里设置了一个未检查的位置
您能否描述一种情况,在这种情况下,您的代码在没有额外检查的情况下与更简单的代码表现不同?如果可能的话,举个例子。
是:当文件不存在时【参考方案5】:
首先,您可以(阅读应该)以永远不会产生异常(例如分段错误)的方式编写代码。
首先,您应该检查所有可能无效的指针(例如,类的用户将调用的类的公共函数可能会收到一些无效的指针,但内部实现可能会假定该指针已被检查)。
其次你应该检查可能失败的函数的结果,例如malloc
可能无法分配内存或者你试图打开的文件可能被删除或者你没有打开它的权限,甚至在打开它之后数据可能无效,因此您应该检查您的操作结果。 C++ 中的这个过程比 C 简单得多,例如 new
在失败时抛出异常或 stream
将在任何错误或 eof 的情况下转换为 false
。
但要回答您的问题,一般catch
块仅捕获类型化异常,但在某些编译器中catch(...)
也可能捕获诸如分段错误之类的异常,甚至将这些异常转换为 C++ 异常,例如在 Windows 平台下您可以使用 @ 987654327@ 为 C++ 异常之类的异常设置全局翻译器,使用 MSVC,您还可以使用 /EHa
编译您的程序,以使其能够在 catch(...)
中捕获此类异常,但所有这些都是特定于特定的扩展平台或编译器因此正确地编写您的代码,并且永远不会想到这样的方法来解决您的问题。
【讨论】:
【参考方案6】:尝试用catch(...)cout<<'catched';
检查它是否可以捕获
也试试这条线,这样可以防止推送错误的格式点:
if (!(ss >> a >> b)) cout<<"wrong format"<<endl; continue;
【讨论】:
你不应该在不重新抛出的情况下使用 catch-all 子句。 为什么这个答案会被接受?它在不止一个方面是不正确的:(1)即使 catch(...) 也不会捕获低级错误; (2) 从句法上讲,'catched' 是非法的。看看 H2CO3 的答案。 @OldPeculier 关于(1)你完全正确;关于(2)虽然你也是对的,但请记住我们中的很多人都不是以英语为母语的人(显然包括我)。 @ebasconp 你在说什么? @OldPeculier 表示 (2)'catched'
在 C++ 中的语法不正确(单引号不构成字符串),而不是英语以上是关于在 C++ 中捕获分段错误的主要内容,如果未能解决你的问题,请参考以下文章