例 10.4. 段错误调试实例一

    调试过程如下:

    1. $ gdb main
    2. ...
    3. (gdb) r
    4. Starting program: /home/akaedu/main
    5. 123
    6.  
    7. Program received signal SIGSEGV, Segmentation fault.
    8. 0xb7e1404b in _IO_vfscanf () from /lib/tls/i686/cmov/libc.so.6
    9. (gdb) bt
    10. #0 0xb7e1404b in _IO_vfscanf () from /lib/tls/i686/cmov/libc.so.6
    11. #2 0x0804839f in main () at main.c:6

    继续调试上一节的程序,上一节最后提出修正Bug的方法是在循环中加上判断条件,如果不是数字就报错退出,不仅输入字母可以报错退出,输入超长的字符串也会报错退出。表面上看这个程序无论怎么运行都不出错了,但假如我们把while (1)循环去掉,每次执行程序只转换一个数:

    例 10.5. 段错误调试实例二

    1. $ ./main
    2. 1234567890
    3. Invalid input!
    4. input=-1

    看起来正常。再来一次,这次输个更长的:

    又出段错误了。我们按同样的方法用gdb调试看看:

    1. $ gdb main
    2. ...
    3. (gdb) r
    4. Starting program: /home/akaedu/main
    5. 1234567890abcdef
    6. input=-1
    7.  
    8. Program received signal SIGSEGV, Segmentation fault.
    9. 0x0804848e in main () at main.c:19
    10. 19 }
    11. (gdb) l
    12. 14 }
    13. 15 sum = sum*10 + input[i] - '0';
    14. 16 }
    15. 17 printf("input=%d\n", sum);
    16. 19 }

    想要写出Bug-free的程序是非常不容易的,即使读入字符串这么一个简单的函数调用都会隐藏着各种各样的错误,有些错误现象是我们暂时没法解释的:为什么变量i的存储单元紧跟在input数组后面?为什么同样是访问越界,有时出段错误有时不出段错误?为什么访问越界的段错误在函数返回时才出现?还有最基本的问题,为什么scanf输入整型变量就必须要加&,否则就出段错误,而输入字符串就不要加&?这些问题在后续章节中都会解释清楚。其实现在讲scanf这个函数为时过早,读者还不具备充足的基础知识。但还是有必要讲的,学完这一阶段之后读者应该能写出有用的程序了,然而一个只有输出而没有输入的程序算不上是有用的程序,另一方面也让读者认识到,学C语言不可能不去了解底层计算机体系结构和操作系统的原理,不了解底层原理连一个scanf函数都没办法用好,更没有办法保证写出正确的程序。