分享

linux下段错误(Segmentation fault)

 七天小胜 2011-02-23

1、段错误的定义

段错误:访问的内存超出了系统给这个程序的内存空间,通常这个值是由gdtr来保存的,gdtr是一个48位的寄存 器,其中的32位是保存由它指向的 gdt表,中间13位保存相应于gdt的下标,最后3位包括了程序是否在内存中以及程序的在cpu中的运行级别,指向的gdt是由以64位为一个单位的 表,在这张表中就保存着程序运行的代码段以及数据段的起始地址以及与此相应的段限和页面交换还有程序运行级别还有内存粒度等等的信息。一旦一个程序发生了 越界访问,cpu就会产生相应的异常保护,于是segmentation fault就出现了。
    通过上面的解释,段错误就是访问了不可访问的内存,这个内存区要么是不存在的,要么是受到系统保护的。

2、段错误是如何产生的?

Segment fault 之所以能够流行于世,是与Glibc库中基本所有的函数都默认型参指针为非空有着密切关系的。


1)代码1:
#include <stdio.h>

int main(){      
int i = 0;      
   scanf ("%d", i);   //应该使用&i,否则产生段错误,因为访问了保护地址0      
printf ("%d\n", i);      
return 0;

}

2)代码2:
#include <stdio.h>

int main(){      
char *p;    
   p = NULL;      
*p = 'x';      //往内存地址0处写东西,所以会产生段错误
printf("%c", *p);      
return 0;

}

3)代码3:

#include <stdio.h>

int main(){     
   char test[1];      
printf("%c", test[1000000000]);    //数组越界,或者这个地址根本不存在

return 0;

}

4)代码4:

#include <stdio.h>

int main(){      
int b = 10;      
printf("%s\n", b);
//在打印字符串的时候,实际上是打印某个地址开始的所有字符,但是当你想把整数当字符串打印的时候,这个整数被当成了一个地址,然后printf从这个地址开始去打印字符,直到某个位置上的值为\0。所以,如果这个整数代表的地址不存在或者不可访问,自然也是访问了不该访问的内存——segmentation fault。

return 0;

}

5)代码5:

#include <stdio.h>

#include <string.h>

char c = 'c';

int i = 10;

char buf[100];

printf("%s", c);      
出现段错误的原因:试图把char型按照字符串格式输出,这里的字符会解释成整数,再解释成地址,所以原因同上面那个例子printf("%s", i),试图把int型按照字符串输出。


memset(buf, 0, 100);

sprintf(buf, "%s", c);  
试图把char型按照字符串格式转换。


memset(buf, 0, 100);

sprintf(buf, "%s", i);
试图把int型按照字符串转换。

6)代码6:

我在进行一个多线程编程的例子里头,定义了一个线程数组

#define THREAD_MAX_NUM

pthread_t thread[THREAD_MAX_NUM];

用pthread_create创建了各个线程,然后用pthread_join来等待线程的结束。

刚开始我就直接等待,在创建线程都成功的时候,pthread_join能够顺利等待各个线程结束,但是一旦创建线程失败,用pthread_join等待那个本不存在的线程时,自然会存在访问不能访问的内存的情况,从而导致段错误。

解决办法为:在创建线程之前,先初始化我们的线程数组,在等待线程的结束的时候,判断线程是否为我们的初始值。如果是的话,说明我们的线程并没有创建成功,所以就不能等,否则就会存在释放那些并不存在或者不可访问的内存空间。

3、怎样避免段错误?
   1)定义了指针后记得初始化,在使用的时候记得判断是否为NULL。
   2)在使用数组的时候是否被初始化,数组下标是否越界,数组元素是否存在等。
   3)在变量处理的时候变量的格式控制是否合理等。



4、怎样发现程序的段错误?

常用的调试方法有:

1)在程序内部的关键位置输出(printf)信息,那样可以跟踪段错误在代码中可能的位置。为了方便使用这种调试方法,可以用条件编译指令#ifdef DEBUG和#endif把printf函数给包含起来,编译的时候加上-DDEBUG参数就可以查看调试信息。反之,不加上该参数进行调试就可以。

2)用gdb来调试,在运行到段错误的地方,会自动停下来并显示出错的行和行号。这个应该是很常用的,如果需要用gdb调试,记得在编译的时候加上-g参数,用来显示调试信息。

3)使用catchsegv命令:通过查看帮助信息,可以看到Catch segmentation faults in programs

转:http://blog.sina.com.cn/s/blog_5c0153620100dstl.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多