由于堆栈溢出,C 中通常会发生啥?

Posted

技术标签:

【中文标题】由于堆栈溢出,C 中通常会发生啥?【英文标题】:What does typically happen in C due to stack overflow?由于堆栈溢出,C 中通常会发生什么? 【发布时间】:2017-05-28 19:48:39 【问题描述】:

在 Java 中会有一个堆栈跟踪,上面写着***Error,整个系统不会崩溃,只有程序会崩溃。

在 C 语言中,我知道数组索引越界会产生分段错误。 C中的堆栈溢出是否相同,并且还会出现分段错误,即类似问题的相同错误类型?

我没有在 C 中测试有意识的无限递归来看看会发生什么,因为我不知道后果。

或者有时情况会更糟,C 中的堆栈溢出可能会导致操作系统故障并迫使您重启电源才能返回?或者更糟的是,造成不可逆转的硬件损坏?堆栈溢出错误会有多糟糕的影响?

似乎很明显,Java 中的保护比 C 中更好。C 中的保护是否比汇编/机器代码更好,或者 C 中的保护实际上与汇编中的保护相同(缺少)?

【问题讨论】:

是什么让您认为没有保护措施?您是否遇到过分段错误?你看到了什么? 不,操作系统应该不会发生任何不好的事情。现代处理器和操作系统使程序不可能意外影响操作系统或其他程序。 在典型的现代操作系统(Linux、Windows、其他 Unix 等)上,C 程序(或任何其他任何语言的程序)通常不会导致操作系统故障。错误的过程只会崩溃,仅此而已。 在 C 中我知道数组索引越界会产生分段错误:这不是真的,C 中的索引越界会导致 未定义的行为(谷歌该术语)。虽然它可能导致段错误。 C 不知道堆栈,因此它不知道堆栈溢出时会发生什么。 【参考方案1】:

在 C 语言中,我知道数组索引越界会产生分段错误。 C中的堆栈溢出是否相同,并且还会出现分段错误,即类似问题的错误类型相同?

C 中不能保证会有分段错误。 C 标准说它是undefined behaviour 并保留它。这完全取决于实现/平台。

或者有时情况会更糟,C 中的堆栈溢出可能会导致操作系统故障并迫使您重启电源才能返回?或者更糟的是,造成不可逆转的硬件损坏?堆栈溢出错误会有多糟糕的影响?

在现代操作系统上,系统发生任何不愉快的事情是非常罕见的;通常,只有程序会崩溃。现代操作系统使用various memory protection techniques。

很明显,Java 中的保护比 C 中的更好。C 中的保护是否比汇编/机器代码中的更好,或者 C 中的保护实际上与汇编中的保护相同(缺少)?

这是因为在 Java 中,内存是“托管的”。在 C 中,它留给程序员;这是设计。 C 编译器最终会生成机器代码。所以它不可能更好或更糟。明显地, 一个好的编译器可以检测到其中的一些问题并发出警告,这在 C 语言中相对于汇编语言来说是一个优势。

【讨论】:

【参考方案2】:

内存故障的处理,就像任何系统资源故障一样,基本上是由操作系统来处理的,而不是语言本身。

排除一些特定的预防措施,如堆栈检查,这类问题通常会触发操作系统异常,可由语言运行时处理。

如果启用堆栈检查,通常在编译器命令行上指定一些开关,指示编译器为每个堆栈消耗操作插入检查探测代码以验证内存可用性。

默认情况下,由于任何原因,过度使用堆栈或损坏,执行尝试访问分配的堆栈空间边界之外的内存,操作系统会触发结构化异常。 Java 与许多 C 运行时一样通常处理这些异常,并提供一些方法将它们传递给用户代码以进行最终恢复(即通过信号或 SEH)。如果没有与用户代码关联的处理程序,则控件将传递给运行时,默认情况下将管理受控任务关闭(优雅关闭)。

如果没有可用的处理方法,甚至从运行时开始,操作系统将关闭任务并执行突然的资源释放(即截断文件、关闭端口等)。

在任何情况下,操作系统都会保护系统,除非操作系统存在缺陷......

在 C 中,注册一个处理程序来保护可能失败的代码片段是很正常的。处理异常的方式取决于您的操作系统(即在 Windows 下,您可以将可能失败的代码包装在异常处理程序 __try __except 中)。

【讨论】:

您的“这是正常的”评论在 Windows 上可能是正确的;这对于 Unix 是不正确的,尤其是因为所使用的语言扩展功能通常在 Unix 上不可用。 @JonathanLeffleryes 你是对的,我指定的更好。【参考方案3】:

每个执行线程在运行时创建线程时都分配了它们的堆栈。如果在程序执行(已编译原生程序)期间检测到堆栈溢出,则只有您的程序(进程)会受到影响,而不是操作系统。

【讨论】:

第二句开头有一个很大的 IF @KamiKaze — 操作系统内存管理确保检测到溢出。程序通常会尝试访问未映射的地址,而映射代码拒绝再扩展堆栈。 我完全同意。我放了一个“如果”,因为检测和管理此类问题是操作系统内存管理的责任。一些手工制作的 o/s,出于特定原因,可能不会应用此类控制。 @JeandeyBoris 甚至一些流行的公司制造的操作系统(和硬件)没有应用这种控制。实模式 x86、嵌入式系统等【参考方案4】:

这不是 C 的问题,至少 C 没有指定发生的事情。C 只会说它是未定义的行为。所以效果是运行时的问题。在任何合理的操作系统上,这都会产生某种将被捕获的错误,并且在 *nixes 中会对您的进程产生分段错误。即使是异国情调的小型操作系统也会保护自己免受您的错误进程的影响。无论如何,这永远不会使操作系统崩溃。 Java 并不比 C更好,它们是不同的语言并且具有不同的运行时。 Java 在设计上更加安全,因为它可以保护您免受许多内存问题(以及其他问题)的影响。 C 可以让您更好地控制机器,是的,它或多或少是一种汇编语言。

【讨论】:

以上是关于由于堆栈溢出,C 中通常会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章

C语言画图理解堆栈溢出

堆栈溢出一般是由啥原因导致的?

分段错误和堆栈溢出有啥区别?

递归过程中的第一个过程调用发生堆栈溢出

如何在 C++ 中处理或避免堆栈溢出

在发生堆栈溢出之前剩余堆栈的大小