添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

1)往受到系统保护的内存地址写数据
有些内存是内核占用的或者是其他程序正在使用,为了保证系统正常工作,所以会受到系统的保护,而不能任意访问.

   1: #include <stdio.h> 
   2: int 
   3: main() 
   4: { 
   5: int i = 0; 
   6: scanf ("%d", i); /* should have used &i */ 
   7: printf ("%d\n", i); 
   8: return 0; 
   9: } 
   1: #include <stdio.h> 
   2:  
   3:  int 
   4:  main() 
   5:  { 
   6:  int i = 0; 
   7:  
   8:  scanf (”%d”, i); /* should have used &i */ 
   9:  printf (”%d\n”, i); 
  10:  return 0; 

(gdb) b 8 –用b(break)设置断点
Breakpoint 1 at 0×80483b7: file segerr.c, line 8.
(gdb) p i –用p(print)打印变量i的值[看到没,这里i的值是0哦]
$1 = 0
(gdb) r –用r(run)运行,直到断点处
Starting program: /home/falcon/temp/segerr
Breakpoint 1, main () at segerr.c:8
8 scanf (”%d”, i); /* should have used &i */ –[试图往地址0处写进一个值]
(gdb) n –用n(next)执行下一步
10
Program received signal SIGSEGV, Segmentation fault.
0xb7e9a1ca in _IO_vfscanf () from /lib/tls/i686/cmov/libc.so.6
(gdb) c –在上面我们接收到了SIGSEGV,然后用c(continue)继续执行
Continuing.
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb) quit –退出gdb
果然
我们“不小心”把&i写成了i
而我们刚开始初始化了i为0,这样我们不是试图向内存地址0存放一个值吗?
[补充:
可以通过man 7 signal查看SIGSEGV的信息。
falcon@falcon:~/temp$ man 7 signal | grep SEGV
Reformatting signal(7), please wait…
SIGSEGV 11 Core Invalid memory reference
例子2:

   1: #include <stdio.h> 
   2: int 
   3: main() 
   4: { 
   5: char *p; 
   6: p = NULL; 
   7: *p = ‘x’; 
   8: printf(”%c”, *p); 
   9: return 0; 
  10: } 
   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的源代码的这里
声明部分:

   1: int pos =0 ,cnt_printed_chars =0 ,i ; 
   2: unsigned char *chptr ; 
   3: va_list ap ; 
   4: /* %s格式控制部分:*/ 
   5: case 's': 
   6: chptr =va_arg (ap ,unsigned char *); 
   7: i =0 ; 
   8: while (chptr [i ]) 
   9: {... 
  10: cnt_printed_chars ++; 
  11: putchar (chptr [i ++]); 
  12: } 

由于我没有仔细分析代码,大致的原因也可能是地址越界的原因?不过我可不确定哦。
如果大家知道怎么调试printf函数,麻烦帮忙找出越界的真正原因吧,这个段错误也可能是
处在va_start和va_arg等函数里头?或者直接看看这个这里的printf源代码的分析,看看是否
可以找出出错的地方:
http://www.wangchao.net.cn/bbsdetail_47325.html
类似的,还有诸如:sprintf等的格式控制问题
比如,试图把char型或者是int的按照%s输出或存放起来,如:

   1: #include <stdio.h> 
   2: #include <string.h> 
   3: char c=’c'; 
   4: int i=10; 
   5: char buf[100]; 
   6: printf(”%s”, c); //试图把char型按照字符串格式输出 
   7: printf(”%s”, i); //试图把int型按照字符串输出 
   8: memset(buf, 0, 100); 
   9: sprintf(buf, “%s”, c); //试图把char型按照字符串格式转换 
  10: memset(buf, 0, 100); 
  11: sprintf(buf, “%s”, i); //试图把int型按照字符串转换 

3)其他
其实大概的原因都是一样的,就是段错误的定义。
但是更多的容易出错的地方就要自己不断积累,不段发现,或者吸纳前人已经积累的经验,并且注意避免再次发生。
例如:
<1>定义了指针后记得初始化,在使用的时候记得判断是否为NULL
<2>在使用数组的时候是否被初始化,数组下标是否越界,数组元素是否存在等
<3>在变量处理的时候变量的格式控制是否合理等
一个比较不错的例子:
我在进行一个多线程编程的例子里头,定义了一个线程数组
#define THREAD_MAX_NUM
pthread_t thread[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)信息,那样可以跟踪 段错误 在代码中可能的位置
为了方便使用这种调试方法,可以用条件编译指令#ifdef DEBUG和#endif把printf函数给包含起来,编译的时候加上-DDEBUG参数就可以查看调试信息。反之,不加上该参数进行调试就可以。
2)用gdb来调试,在运行到段错误的地方,会自动停下来并显示出错的行和行号
这个应该是很常用的,如果需要用gdb调试,记得在编译的时候加上-g参数,用来显示调试信息
对于这个,网友在《段错误bug的调试》文章里创造性的使用这样的方法,使得我们在执行程序的时候就可以动态扑获段错误可能出现的位置:
通过扑获SIGSEGV信号来触发系统调用gdb来输出调试信息。
如果加上上面提到的条件编译,那我们就可以非常方便的进行段错误的调试拉。