C中产生Segfault的最简单的标准符合方式是什么?

我认为这个问题说明了一切。 涵盖从C89到C11的大多数标准的例子将是有帮助的。 我虽然这一个,但我想这只是未定义的行为:

#include <stdio.h> int main( int argc, char* argv[] ) { const char *s = NULL; printf( "%c\n", s[0] ); return 0; } 

编辑:

正如一些投票要求澄清:我想有一个程序通常编程错误(最简单的我能想到的是段错误),这是保证 (通过标准)中止。 这与最小的段落问题有些不同,它不关心这个保险。

分段错误是实现定义的行为 。 该标准没有定义实现如何处理未定义的行为 ,实际上实现可以优化未定义的行为 ,仍然是合规的。 要明确的是, 实现定义的行为是标准没有规定的行为,但实现应该是文档化的。 未定义的行为是不可移植的或者错误的,并且其行为是不可预知的,因此不能被依赖。

如果我们看一下C99草案标准 §3.4.3中未定义的行为 ,在第1段中的术语,定义和符号部分,它说( 强调我的前进 ):

行为,在使用不可移植或错误的程序结构或错误的数据时, 本国际标准对此没有要求

在第2段中说:

注意可能存在的未定义的行为范围从忽略完全不可预知的结果,在翻译或程序执行过程中以环境特征(有或没有发布诊断消息),终止翻译或执行发出诊断消息)。

另一方面,如果你只是想在标准中定义一个在大多数类Unix系统上会导致分段错误的方法,那么raise(SIGSEGV)应该实现这个目标。 尽pipe严格来说, SIGSEGV的定义如下:

SIGSEGV无法访问存储

和§7.14 信号处理<signal.h>说:

一个实现不需要生成任何这些信号,除非由于显式调用raise函数 。 额外的信号和指向无法声明函数的指针,macros定义分别以字母SIG和大写字母开始,或者用SIG_和大写字母219开始,也可以由实现来指定。 完整的信号集合,它们的语义以及它们的缺省处理是实现定义的 ; 所有的信号编号应为正值。

raise()可以用来引发段错误:

 raise(SIGSEGV); 

该标准只提到未定义的行为。 它对内存分割一无所知。 另请注意,产生错误的代码不符合标准。 您的代码不能调用未定义的行为,并且同时符合标准。

尽pipe如此,在产生这种错误的架构上产生分段错误的最短途径是:

 int main() { *(int*)0 = 0; } 

为什么这肯定会产生段错误? 因为访问内存地址0总是被系统捕获; 它永远不能成为一个有效的访问(至less不是用户空间的代码)。

当然,并不是所有的架构都以相同的方式工作。 在其中一些上面,上面不会崩溃,而是产生其他types的错误。 或者声明可能是完全正确的,甚至,内存位置0可以访问就好了。 这是标准没有真正定义发生的原因之一。

正确的程序不会产生段错误。 而且你不能描述一个不正确的程序的确定性行为。

“分段错误”是x86 CPU所做的事情。 你试图以不正确的方式引用内存。 它也可以指存储器访问导致页面错误的情况(即尝试访问没有加载到页表中的内存),并且操作系统决定你没有权利请求该内存。 要触发这些条件,您需要直接编写您的操作系统和硬件。 这不是C语言指定的。

如果我们假设我们没有提高信号调用的raise ,那么分段错误很可能来自于未定义的行为。 未定义的行为是未定义的,编译器可以自由地拒绝翻译,所以没有答案的undefined被保证在所有实现上都失败。 另外一个调用未定义行为的程序是一个错误的程序。

但是这个是我可以在我的系统上获得segfault的最短时间:

 main(){main();} 

(我用gcc-std=c89 -O0编译)。

顺便说一句,这个程序真的调用了未定义的bevahior?

在某些平台上,如果从系统请求太多的资源,符合标准的C程序可能会出现分段错误。 例如,用malloc分配一个大对象可能会成功,但是稍后,当对象被访问时,它会崩溃。

请注意,这样的程序并不严格符合; 符合该定义的程序必须保持在每个最低执行限度内。

符合标准的C程序不能产生分段错误,因为唯一的其他方法是通过未定义的行为。

SIGSEGV信号可以明确提出,但标准C库中没有SIGSEGV符号。

(在这个答案中,“符合标准”是指:“仅使用ISO C标准某些版本中描述的特性,避免未指定的,实现定义的或未定义的行为,但不一定局限于最小实现限制。”)

这个问题的大部分答案都围绕着关键点进行了讨论,即: C标准不包括分段错误的概念。 (由于C99包含信号编号 SIGSEGV ,但它没有定义任何信号传递的情况,除了其他答案中讨论的raise(SIGSEGV)不计算在内)。

因此,没有“严格符合”的程序(即只使用行为完全由C标准定义的结构的程序)保证引起分段错误。

分段错误由不同的标准POSIX定义。 该程序保证在任何完全符合POSIX.1-2008(包括存储器保护和高级实时选项)的系统上引发分段错误或function相当的“总线错误”( SIGBUS ),只要sysconfposix_memalignmprotect成功。 我对C99的看法是这个程序只考虑了这个标准的实现定义 (不是未定义的!)行为,因此它符合但不是严格符合

 #define _XOPEN_SOURCE 700 #include <sys/mman.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> int main(void) { size_t pagesize = sysconf(_SC_PAGESIZE); if (pagesize == (size_t)-1) { fprintf(stderr, "sysconf: %s\n", strerror(errno)); return 1; } void *page; int err = posix_memalign(&page, pagesize, pagesize); if (err || !page) { fprintf(stderr, "posix_memalign: %s\n", strerror(err)); return 1; } if (mprotect(page, pagesize, PROT_NONE)) { fprintf(stderr, "mprotect: %s\n", strerror(errno)); return 1; } *(long *)page = 0xDEADBEEF; return 0; } 

在未定义的平台上很难定义一种对程序进行分段的方法。 分段错误是一个没有为所有平台定义的松散术语(例如,简单的小型计算机)。

只考虑支持进程的操作系统,进程可以收到发生分段错误的通知。

另外,将操作系统限制为'unix like'操作系统,一个接收SIGSEGV信号的过程的可靠方法是kill(getpid(),SIGSEGV)

与大多数跨平台问题一样,每个平台可能(通常都会)具有不同的分段定义。

但是实际上,目前的mac,lin和win操作系统会出现故障

 *(int*)0 = 0; 

此外,引起段错误的行为也不错。 assert()一些实现会产生一个可能产生核心文件的SIGSEGV信号。 当你需要尸体解剖时非常有用。

有什么比引起段错误更糟的是隐藏它:

 try { anyfunc(); } catch (...) { printf("?\n"); } 

它隐藏了一个错误的来源,所有你必须继续:

 ?