编程中通常碰到段错误的地方有哪些?
粗略的分一下类
(1)往受到系统保护的内存地址写数据
有些内存是内核占用的或者是其他程序正在使用,为了保证系统正常工作,
所以会受到系统的保护,而不能任意访问。
例子1:
1 #include <stdio.h>
2
3 int main()
4 {
5 int i=0;
6
7 scanf("%d",i);
8 printf("i=%d\n");
9 return 0;
10 }
编译和执行一下
$ gcc -o test test.c
$
编译通过
执行
$ ./test
1
Segmentation fault (core dumped)
输入1,报告段错误
咋一看,好像没有问题哦,不就是读取一个数据然后给输出来吗?
下面我们来调试一下,看看是什么原因?
果然 “不小心”把&i写成了i
而我们刚开始初始化了i为0,这样不是试图向内存地址0存放一个值吗?实际上很多情况下,即使没有初始化为零,默认也可能是0,所以要特别注意。
例子2:
1 #include "stdio.h"
2
3 int main()
4 {
5 char *p;
6 p=NULL;
7 *p='a';
8 printf("%c",*p);
9 return 0;
10 }
$ gcc -g -o test test.c
例子3:
1 #include "stdio.h"
2
3 int main()
4 {
5 char test[1];
6
7 printf("%c",test[1000000000]);
8 return 0;
9 }
$ gcc -g -o test test.c
例子4:
1 #include "stdio.h"
2
3 int main()
4 {
5 int b=10;
6
7 printf("%s\n",b);
8
9 return 0;
10 }
试图把一个整数按照字符串的方式输出出去,这是什么问题呢? 由于还不熟悉调试动态链接库,所以 只是找到了printf的源代码的这里
声明部分:
int pos =0 ,cnt_printed_chars =0 ,i unsigned char *chptr va_list ap %s格式控制部分: case 's':
chptr =va_arg (ap ,unsigned char *); i =0
while (chptr [i ]) {...
cnt_printed_chars ++;
putchar (chptr [i ++]);
}
仔细看看,发现了这样一个问题,在打印字符串的时候,实际上是打印某个地址开始的所有字符,但是当你想把整数当字符串打印的时候,这个整数被当成了一个地址,然后printf从这个地址开始去打印字符,直到某个位置上的值为\0。所以,如果这个整数代表的地址不存在或者不可访问,自然也是访问了不该访问的内存segmentation fault。
类似的,还有诸如:sprintf等的格式控制问题
比如,试图把char型或者是int的按照%s输出或存放起来,如:
#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型按照字符串转换
其他 :
其实大概的原因都是一样的,就是段错误的定义。
但是更多的容易出错的地方就要自己不断积累,不段发现,或者吸纳前人已经积累的经验,并且注意避免
再次发生。
例如:
<1>定义了指针后记得初始化,在使用的时候记得判断是否为NULL <2>在使用数组的时候是否被初始化,数组下标是否越界,数组元素是否存在等
<3>在变量处理的时候变量的格式控制是否合理等
如何发现程序中的段错误?
有个网友对这个做了比较全面的总结
文章名字叫《段错误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来输出调试信息。
如果加上上面提到的条件编译,那我们就可以非常方便的进行段错误的调试拉。
(3)还有一个catchsegv命令