一种调试方法
写C语言写久了,会发现一个问题:经常莫名其妙的收到这种信息: (-1073741819) 据经验来看,这个奇怪的数字来自CPU异常导致操作系统返回的错误值,来源无从考究,那么它引起的原因到底是什么? 很多次这种经历,我总结出来:就是访问了非法地址导致的. 举几个例子: (一)
char *s;
sprintf(s, "hello, %s", name);
你这不报错难不成给奖励啊,众所周知指针*s如果在64位的模式下(下文默认64位)站8个字节,这8字节存储在栈(stack)上.然而很多人没想过,栈上?内存地址不就可访问了嘛,但是,堆栈上的数据具有随机性,就是说你永远不能保证这里存的是什么.比如说,假如分配给"s"的这个指针所处栈8字节空间存的是个0x12345678,访问这个地址,你都不知道在现代操作系统页管理机制下,背映射到哪里!
(二)
struct List {
char *self;
struct List *next;
};
void func(void) {
struct List *p=malloc(sizeof(struct List));
for (struct List *tmp=p; tmp; tmp=tmp->next) {
printf("%s %d", tmp->self, *(tmp->next));
}
}
一个典型的单项链表没搞清楚数据存储保证. (顺便提一句,这个malloc返回void*但是不强转根本没事) for循环的第二部分,只保证了tmp不是空指针(NULL),但是next呢?没保证. 这也就引起了一个奇奇怪怪的事情:要么多输出好多莫名其妙的数据,要么直接返回那个奇怪的符号整数值. 首先第一种,那是因为给新的链表元素分配内存的时候,没有清零,有可能->next正好指向一个可访问的合法地址,这也就满足了下一次循环开始的tmp非空条件;
再就是第二种,有了上一段的理解,这个就是因为next指向一个非法地址,当然报错
这点没有python的exception方便,但是为了c的经典和效率,不得不引出一种调试方法. 本人windows环境,并且不喜(不)欢(会)用gdb,于是手动拿着个sublime一次次编译,运行… 面对这种前不着村后不着店的异常退出,我们能做的就俩事:
第一种 没有断点,那就手动呗!搞个printf,比如这样:
printf("L(行数) is ok!\n");
输出,看看这个输出没,如果有,那就说明非法访问在后面,否则前面 对于前者,我们可以把这个语句删掉,然后放在后面几行; 对于后者,把它删掉放在前面几行; 这个有个小技巧,就是把可能出错的语句看的更重,优先; 但是此方法存在一个问题,sublime text3和git bash的控制台都缓冲机制好像不大好,为啥这么说捏,看看这个就晓得了:
int main(int argc, char const *argv[])
{
char *s=0x123;
printf("L4 is ok!\n");
printf("%d", *s);
return 0;
}
在gitbash下: 好家伙简洁明了段错误,但是…,有啥用? 再看看sublimetext: 返回值也是怪怪的(约摸着unsigned/signed的问题). 很明显printf没问题会正常输出,但是…PS下就可以 这河狸吗(我自己都是手动拿ps而不是st,好久以前看半天,为啥都没输出?换个ps一看我人傻了)
所以这也就引出第二种调试: 看哪行不爽,按个ctrl+/,在b+r 说白了看着错误率较大的代码行,注释掉运行看看退出情况 很明显,注释掉步骤当然不会正常,但是退出信息应该是自己设定的可控范围内的异常,也就是说如果这时候没有非法访问,那么异常信息就是你看得懂的().
|