【整理】段错误(segmentation fault)
2011-04-09 13:35
411 查看
1)往受到系统保护的内存地址写数据
有些内存是内核占用的或者是其他程序正在使用,为了保证系统正常工作,所以会受到系统的保护,而不能任意访问.
编译和执行一下,咋一看,好像没有问题哦,不就是读取一个数据然后给输出来吗?
falcon@falcon:~/temp$gcc-g-osegerrsegerr.c–加-g选项查看调试信息
falcon@falcon:~/temp$gdb./segerr
GNUgdb6.4-debian
Copyright2005FreeSoftwareFoundation,Inc.
GDBisfreesoftware,coveredbytheGNUGeneralPublicLicense,andyouare
welcometochangeitand/ordistributecopiesofitundercertainconditions.
Type“showcopying”toseetheconditions.
ThereisabsolutelynowarrantyforGDB.Type“showwarranty”fordetails.
ThisGDBwasconfiguredas“i486-linux-gnu”…Usinghostlibthread_dblibrary“/lib/tls/i686/cmov/libthread_db.so.1″.
(gdb)l–用l(list)显示我们的源代码
(gdb)b8–用b(break)设置断点
Breakpoint1at0×80483b7:filesegerr.c,line8.
(gdb)pi–用p(print)打印变量i的值[看到没,这里i的值是0哦]
$1=0
(gdb)r–用r(run)运行,直到断点处
Startingprogram:/home/falcon/temp/segerr
Breakpoint1,main()atsegerr.c:8
8scanf(”%d”,i);/*shouldhaveused&i*/–[试图往地址0处写进一个值]
(gdb)n–用n(next)执行下一步
10
ProgramreceivedsignalSIGSEGV,Segmentationfault.
0xb7e9a1cain_IO_vfscanf()from/lib/tls/i686/cmov/libc.so.6
(gdb)c–在上面我们接收到了SIGSEGV,然后用c(continue)继续执行
Continuing.
ProgramterminatedwithsignalSIGSEGV,Segmentationfault.
Theprogramnolongerexists.
(gdb)quit–退出gdb
果然
我们“不小心”把&i写成了i
而我们刚开始初始化了i为0,这样我们不是试图向内存地址0存放一个值吗?
[补充:
可以通过man7signal查看SIGSEGV的信息。
falcon@falcon:~/temp$man7signal|grepSEGV
Reformattingsignal(7),pleasewait…
SIGSEGV11CoreInvalidmemoryreference
例子2:
很容易发现,这个例子也是试图往内存地址0处写东西。
这里我们通过gdb来查看段错误所在的行
falcon@falcon:~/temp$gcc-g-osegerrsegerr.c
falcon@falcon:~/temp$gdb./segerr
GNUgdb6.4-debian
Copyright2005FreeSoftwareFoundation,Inc.
GDBisfreesoftware,coveredbytheGNUGeneralPublicLicense,andyouare
welcometochangeitand/ordistributecopiesofitundercertainconditions.
Type“showcopying”toseetheconditions.
ThereisabsolutelynowarrantyforGDB.Type“showwarranty”fordetails.
ThisGDBwasconfiguredas“i486-linux-gnu”…Usinghostlibthread_dblibrary“/lib/tls/i686/cmov/libthread_db.so.1″.
(gdb)r–直接运行,我们看到抛出段错误以后,自动显示出了出现段错误的行,这就是一个找出段错误的方法
Startingprogram:/home/falcon/temp/segerr
ProgramreceivedsignalSIGSEGV,Segmentationfault.
0×08048516inmain()atsegerr.c:10
10*p=‘x’;
(gdb)
2)内存越界(数组越界,变量类型不一致等)
这里是比较极端的例子,但是有时候可能是会出现的,是个明显的数组越界的问题
或者是这个地址是根本就不存在的
例子4:
我们试图把一个整数按照字符串的方式输出出去,这是什么问题呢?
由于还不熟悉调试动态链接库,所以
我只是找到了printf的源代码的这里
声明部分:
由于我没有仔细分析代码,大致的原因也可能是地址越界的原因?不过我可不确定哦。
如果大家知道怎么调试printf函数,麻烦帮忙找出越界的真正原因吧,这个段错误也可能是
处在va_start和va_arg等函数里头?或者直接看看这个这里的printf源代码的分析,看看是否
可以找出出错的地方:http://www.wangchao.net.cn/bbsdetail_47325.html类似的,还有诸如:sprintf等的格式控制问题
比如,试图把char型或者是int的按照%s输出或存放起来,如:
3)其他
其实大概的原因都是一样的,就是段错误的定义。
但是更多的容易出错的地方就要自己不断积累,不段发现,或者吸纳前人已经积累的经验,并且注意避免再次发生。
例如:
<1>定义了指针后记得初始化,在使用的时候记得判断是否为NULL
<2>在使用数组的时候是否被初始化,数组下标是否越界,数组元素是否存在等
<3>在变量处理的时候变量的格式控制是否合理等
一个比较不错的例子:
我在进行一个多线程编程的例子里头,定义了一个线程数组
#defineTHREAD_MAX_NUM
pthread_tthread[THREAD_MAX_NUM];
用pthread_create创建了各个线程,然后用pthread_join来等待线程的结束
刚开始
我就直接等待,在创建线程都成功的时候,pthread_join能够顺利等待各个线程结束
但是一旦创建线程失败,那用pthread_join来等待那个本不存在的线程时自然会存在访问不存在的内存的情况,从而导致段错误的发生
后来
通过不断调试和思考,并且得到网络上资料的帮助,找到了上面的出错原因和解决办法
解决办法是:
在创建线程之前,先初始化我们的线程数组
在等待线程的结束的时候,判断线程是否为我们的初始值
如果是的话,说明我们的线程并没有创建成功,所以就不能等拉。
上面给出了很常见的几种出现段错误的地方,这样在遇到它们的时候就容易避免拉。
但是人有时候肯定也会有疏忽的,甚至可能还是会经常出现上面的问题或者其他常见的问题
所以对于一些大型一点的程序,如何跟踪并找到程序中的段错误位置就是需要掌握的一门技巧拉。
4。如何发现程序中的段错误?
有个网友对这个做了比较全面的总结,除了感谢他外,我把地址弄了过来。
文章名字叫《段错误bug的调试》
地址是:http://www.cublog.cn/u/5251/showart.php?id=173718
应该说是很全面的。
而我常用的调试方法有:
1)在程序内部的关键部位输出(printf)信息,那样可以跟踪段错误在代码中可能的位置
为了方便使用这种调试方法,可以用条件编译指令#ifdefDEBUG和#endif把printf函数给包含起来,编译的时候加上-DDEBUG参数就可以查看调试信息。反之,不加上该参数进行调试就可以。
2)用gdb来调试,在运行到段错误的地方,会自动停下来并显示出错的行和行号
这个应该是很常用的,如果需要用gdb调试,记得在编译的时候加上-g参数,用来显示调试信息
对于这个,网友在《段错误bug的调试》文章里创造性的使用这样的方法,使得我们在执行程序的时候就可以动态扑获段错误可能出现的位置:
通过扑获SIGSEGV信号来触发系统调用gdb来输出调试信息。
如果加上上面提到的条件编译,那我们就可以非常方便的进行段错误的调试拉。
[align=center][/align]
有些内存是内核占用的或者是其他程序正在使用,为了保证系统正常工作,所以会受到系统的保护,而不能任意访问.
1 | #include<stdio.h> |
2 | int |
3 | main() |
4 | { |
5 | int i=0; |
6 | scanf( "%d" ,i); /*shouldhaveused&i*/ |
7 | printf( "%d\n" ,i); |
8 | return 0; |
9 | } |
falcon@falcon:~/temp$gcc-g-osegerrsegerr.c–加-g选项查看调试信息
falcon@falcon:~/temp$gdb./segerr
GNUgdb6.4-debian
Copyright2005FreeSoftwareFoundation,Inc.
GDBisfreesoftware,coveredbytheGNUGeneralPublicLicense,andyouare
welcometochangeitand/ordistributecopiesofitundercertainconditions.
Type“showcopying”toseetheconditions.
ThereisabsolutelynowarrantyforGDB.Type“showwarranty”fordetails.
ThisGDBwasconfiguredas“i486-linux-gnu”…Usinghostlibthread_dblibrary“/lib/tls/i686/cmov/libthread_db.so.1″.
(gdb)l–用l(list)显示我们的源代码
01 | #include<stdio.h> |
02 |
03 | int |
04 | main() |
05 | { |
06 | int i= 0 ; |
07 |
08 | scanf(”%d”,i); /*shouldhaveused&i*/ |
09 | printf(”%d\n”,i); |
10 | return 0 ; |
Breakpoint1at0×80483b7:filesegerr.c,line8.
(gdb)pi–用p(print)打印变量i的值[看到没,这里i的值是0哦]
$1=0
(gdb)r–用r(run)运行,直到断点处
Startingprogram:/home/falcon/temp/segerr
Breakpoint1,main()atsegerr.c:8
8scanf(”%d”,i);/*shouldhaveused&i*/–[试图往地址0处写进一个值]
(gdb)n–用n(next)执行下一步
10
ProgramreceivedsignalSIGSEGV,Segmentationfault.
0xb7e9a1cain_IO_vfscanf()from/lib/tls/i686/cmov/libc.so.6
(gdb)c–在上面我们接收到了SIGSEGV,然后用c(continue)继续执行
Continuing.
ProgramterminatedwithsignalSIGSEGV,Segmentationfault.
Theprogramnolongerexists.
(gdb)quit–退出gdb
果然
我们“不小心”把&i写成了i
而我们刚开始初始化了i为0,这样我们不是试图向内存地址0存放一个值吗?
[补充:
可以通过man7signal查看SIGSEGV的信息。
falcon@falcon:~/temp$man7signal|grepSEGV
Reformattingsignal(7),pleasewait…
SIGSEGV11CoreInvalidmemoryreference
例子2:
01 | #include<stdio.h> |
02 | int |
03 | main() |
04 | { |
05 | char *p; |
06 | p=NULL; |
07 | *p=‘x’; |
08 | printf (”%c”,*p); |
09 | return 0; |
10 | } |
这里我们通过gdb来查看段错误所在的行
falcon@falcon:~/temp$gcc-g-osegerrsegerr.c
falcon@falcon:~/temp$gdb./segerr
GNUgdb6.4-debian
Copyright2005FreeSoftwareFoundation,Inc.
GDBisfreesoftware,coveredbytheGNUGeneralPublicLicense,andyouare
welcometochangeitand/ordistributecopiesofitundercertainconditions.
Type“showcopying”toseetheconditions.
ThereisabsolutelynowarrantyforGDB.Type“showwarranty”fordetails.
ThisGDBwasconfiguredas“i486-linux-gnu”…Usinghostlibthread_dblibrary“/lib/tls/i686/cmov/libthread_db.so.1″.
(gdb)r–直接运行,我们看到抛出段错误以后,自动显示出了出现段错误的行,这就是一个找出段错误的方法
Startingprogram:/home/falcon/temp/segerr
ProgramreceivedsignalSIGSEGV,Segmentationfault.
0×08048516inmain()atsegerr.c:10
10*p=‘x’;
(gdb)
2)内存越界(数组越界,变量类型不一致等)
1 | #include<stdio.h> |
2 | int |
3 | main() |
4 | { |
5 | char test[1]; |
6 | printf(”%c”,test[1000000000]); |
7 | return 0; |
8 | } |
或者是这个地址是根本就不存在的
例子4:
1 | #include<stdio.h> |
2 | int |
3 | main() |
4 | { |
5 | int b=10; |
6 | printf (”%s\n”,b); |
7 | return 0; |
8 | } |
由于还不熟悉调试动态链接库,所以
我只是找到了printf的源代码的这里
声明部分:
01 | int pos=0,cnt_printed_chars=0,i; |
02 | unsigned char *chptr; |
03 | va_list ap; |
04 | /*%s格式控制部分:*/ |
05 | case 's' : |
06 | chptr= va_arg (ap,unsigned char *); |
07 | i=0; |
08 | while (chptr[i]) |
09 | {... |
10 | cnt_printed_chars++; |
11 | putchar (chptr[i++]); |
12 | } |
如果大家知道怎么调试printf函数,麻烦帮忙找出越界的真正原因吧,这个段错误也可能是
处在va_start和va_arg等函数里头?或者直接看看这个这里的printf源代码的分析,看看是否
可以找出出错的地方:
比如,试图把char型或者是int的按照%s输出或存放起来,如:
01 | #include<stdio.h> |
02 | #include<string.h> |
03 | char c=’c'; |
04 | int i=10; |
05 | char buf[100]; |
06 | printf (”%s”,c); //试图把char型按照字符串格式输出 |
07 | printf (”%s”,i); //试图把int型按照字符串输出 |
08 | memset (buf,0,100); |
09 | sprintf (buf,“%s”,c); //试图把char型按照字符串格式转换 |
10 | memset (buf,0,100); |
11 | sprintf (buf,“%s”,i); //试图把int型按照字符串转换 |
其实大概的原因都是一样的,就是段错误的定义。
但是更多的容易出错的地方就要自己不断积累,不段发现,或者吸纳前人已经积累的经验,并且注意避免再次发生。
例如:
<1>定义了指针后记得初始化,在使用的时候记得判断是否为NULL
<2>在使用数组的时候是否被初始化,数组下标是否越界,数组元素是否存在等
<3>在变量处理的时候变量的格式控制是否合理等
一个比较不错的例子:
我在进行一个多线程编程的例子里头,定义了一个线程数组
#defineTHREAD_MAX_NUM
pthread_tthread[THREAD_MAX_NUM];
用pthread_create创建了各个线程,然后用pthread_join来等待线程的结束
刚开始
我就直接等待,在创建线程都成功的时候,pthread_join能够顺利等待各个线程结束
但是一旦创建线程失败,那用pthread_join来等待那个本不存在的线程时自然会存在访问不存在的内存的情况,从而导致段错误的发生
后来
通过不断调试和思考,并且得到网络上资料的帮助,找到了上面的出错原因和解决办法
解决办法是:
在创建线程之前,先初始化我们的线程数组
在等待线程的结束的时候,判断线程是否为我们的初始值
如果是的话,说明我们的线程并没有创建成功,所以就不能等拉。
上面给出了很常见的几种出现段错误的地方,这样在遇到它们的时候就容易避免拉。
但是人有时候肯定也会有疏忽的,甚至可能还是会经常出现上面的问题或者其他常见的问题
所以对于一些大型一点的程序,如何跟踪并找到程序中的段错误位置就是需要掌握的一门技巧拉。
4。如何发现程序中的段错误?
有个网友对这个做了比较全面的总结,除了感谢他外,我把地址弄了过来。
文章名字叫《段错误bug的调试》
地址是:http://www.cublog.cn/u/5251/showart.php?id=173718
应该说是很全面的。
而我常用的调试方法有:
1)在程序内部的关键部位输出(printf)信息,那样可以跟踪段错误在代码中可能的位置
为了方便使用这种调试方法,可以用条件编译指令#ifdefDEBUG和#endif把printf函数给包含起来,编译的时候加上-DDEBUG参数就可以查看调试信息。反之,不加上该参数进行调试就可以。
2)用gdb来调试,在运行到段错误的地方,会自动停下来并显示出错的行和行号
这个应该是很常用的,如果需要用gdb调试,记得在编译的时候加上-g参数,用来显示调试信息
对于这个,网友在《段错误bug的调试》文章里创造性的使用这样的方法,使得我们在执行程序的时候就可以动态扑获段错误可能出现的位置:
通过扑获SIGSEGV信号来触发系统调用gdb来输出调试信息。
如果加上上面提到的条件编译,那我们就可以非常方便的进行段错误的调试拉。
[align=center][/align]
相关文章推荐
- ubuntu segmentation fault 段错误
- C/C++中的段错误(Segmentation fault)
- 段错误(segmentation fault)
- C/C++中的段错误(Segmentation fault)
- Linux 下的段错误(Segmentation fault)调试方法
- 段错误(segmentation fault)
- Linux 下的段错误(Segmentation fault)调试方法
- C/C++中的段错误(Segmentation fault)
- 总结段错误(Segmentation fault)
- C/C++中的段错误(Segmentation fault)[转]
- Linux 下的段错误(Segmentation fault)调试方法(转)
- Linux 下的段错误(Segmentation fault)调试方法(转)
- C/C++中的段错误(Segmentation fault)[转]
- C/C++中的段错误(Segmentation fault)
- 总结段错误(Segmentation fault)
- Linux下的段错误(Segmentation fault)产生的原因及调试方法(经典)
- C/C++中的段错误(Segmentation fault)
- C/C++中的段错误(Segmentation fault)
- 段错误(Segmentation fault)
- 段错误总结(segmentation fault)