zoukankan      html  css  js  c++  java
  • gdb

    背景

    不管是C开发还是做pwn的ctf题,调试总是少不了的,gdb又是很多大佬经常用来分析的工具,不学是不行的啊,醉了。现在文章的内容只是gdb工具很少的一部分,但对于初学者仍有参考价值。

    参考链接:

    • 《Linux C编程:一站式学习》

    测试环境:

    • centos 7
    • GNU gdb (GDB) Red Hat Enterprise Linux 8.2-15.el8

    调试的基本思想分析现象-假设错误原因-产生新的现象去验证假设

    基础命令

    书籍中的实例:

    #include <stdio.h>
    
    int add_range(int low, int high)
    {
    	int i, sum = 0;
    	for (i = low; i <= high; i++)
    		sum = sum +  i;
    	return sum;
    }
    
    int main(void)
    {
    	int result[100];
    	result[0] = add_range(1, 10);
    	result[1] = add_range(1, 100);
    	printf("result[0] = %d
    result[1] = %d
    ", result[0], result[1]);
    	return 0;
    }
    

    从main函数开始阅读本程序,定义了一个int型的数组,数组长度为100,add_range函数的作用是将传入的值进行累加,之后将得出的值传入到result数组中,但只传入两个值,最后将结果输出出来。

    在程序编译时,需要加上-g选项,生成的可执行文件才能进行源码级调试。参考链接:https://blog.csdn.net/qq_40860852/article/details/89388723

    [root@i-co6yw0e5 c]# gcc -g for_gdb.c -o test
    [root@i-co6yw0e5 c]# ./test 
    result[0] = 55
    result[1] = 5050
    [root@i-co6yw0e5 c]# gdb --version
    GNU gdb (GDB) Red Hat Enterprise Linux 8.2-15.el8
    Copyright (C) 2018 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    

    注:gdb的-g参数不是将源代码嵌入到可执行文件中,所以在调试的时候,源代码不可删除,不然会存在gdb找不到源文件的情况。

    使用gdb开始调试编译后的可执行程序,命令gdb 可执行程序名。使用help命令,可以查看命令的类别。

    [root@i-co6yw0e5 c]# gdb test
    GNU gdb (GDB) Red Hat Enterprise Linux 8.2-15.el8
    ………………
    
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from test...done.
    (gdb) help  #help命令
    List of classes of commands:
    
    aliases -- Aliases of other commands
    breakpoints -- Making program stop at certain points
    ………………
    tracepoints -- Tracing of program execution without stopping the program
    user-defined -- User-defined commands
    

    也可以进一步查看某一类别有哪些命令,例如files:

    (gdb) help files
    Specifying and examining files.
    
    List of commands:
    
    add-symbol-file -- Load symbols from FILE
    ……
    symbol-file -- Load symbol table from executable file FILE
    ……
    

    使用list命令,从第一行开始列出代码,一次只显示10行,如果要继续显示,可以再输入list或者直接回车,直接回车表示重复上一条命令。

    (gdb) list
    1	#include <stdio.h>
    2	
    3	int add_range(int low, int high)
    4	{
    5		int i, sum = 0;
    6		for (i = low; i <= high; i++)
    7			sum = sum +  i;
    8		return sum;
    9	}
    10	
    

    gdb很多命令有简写形式,list可以简写为l,列出一个函数的源代码,也可以使用函数名作为参数:

    (gdb) l main
    7			sum = sum +  i;
    8		return sum;
    9	}
    10	
    11	int main(void)
    12	{
    13		int result[100];
    14		result[0] = add_range(1, 10);
    15		result[1] = add_range(1, 100);
    16		printf("result[0] = %d
    result[1] = %d
    ", result[0], result[1]);
    

    使用start命令开始执行程序,gdb停在main函数中变量定义后的第一条语句,列出的语句是即将执行的下一条语句,可以使用next(简写n)控制语句一条一条的执行。

    (gdb) start
    Temporary breakpoint 1 at 0x4005d1: file for_gdb.c, line 14.
    Starting program: /root/c/test 
    Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-151.el8.x86_64
    
    Temporary breakpoint 1, main () at for_gdb.c:14
    14		result[0] = add_range(1, 10);
    (gdb) n
    15		result[1] = add_range(1, 100);
    (gdb) 
    16		printf("result[0] = %d
    result[1] = %d
    ", result[0], result[1]);
    (gdb) 
    result[0] = 55
    result[1] = 5050
    17		return 0;
    

    从结果上看,虽然控制了程序的执行过程,但是并不详细,可以使用step(简写s)进入到add_range函数中跟踪执行,使用start命令重新执行可执行程序。

    (gdb) start
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Temporary breakpoint 2 at 0x4005d1: file for_gdb.c, line 14.
    Starting program: /root/c/test 
    
    Temporary breakpoint 2, main () at for_gdb.c:14
    14		result[0] = add_range(1, 10);
    (gdb) s
    add_range (low=1, high=10) at for_gdb.c:5
    5		int i, sum = 0;
    (gdb) backtrace
    #0  add_range (low=1, high=10) at for_gdb.c:5
    #1  0x00000000004005e0 in main () at for_gdb.c:14
    

    这次停在了add_range函数中变量定义之后的第一条语句,通过backtrace(简写bt),可以查看函数调用的栈帧。从bt命令返回的内容中可以看到,add_range函数是被main函数调用的,main函数传进来的值是low=1,high=10main函数栈帧编号为1add_range的栈帧编号为0。可以通过info(简写i)来查看add_range函数局部变量的值。info的命令内容有很多,可以使用help info命令查看使用方法。

    (gdb) i locals
    i = 0
    sum = 0
    

    如果想要查看main函数当前局部变量的值,可以使用frame(简写f)选择1号栈帧,然后再查看局部变量的值。

    (gdb) f 1
    #1  0x00000000004005e0 in main () at for_gdb.c:14
    14		result[0] = add_range(1, 10);
    (gdb) i locals
    result = {-134223960, 32767, 1, 0, 0, 0, 0, 0, 0, 0, 1700966438, 0, -7032, 32767, -6976, 32767, -134223080,…… , 0, 4195949, 0, -136447344, 32767, 0, 0, 4195872, 0, 4195504, 0, -6784, 32767, 0, 0}
    

    result数组中的值是杂乱的,因为未经初始化的局部变量具有不确定的值。使用s命令继续运行程序,然后使用print(简写p)打印变量sum的值。

    (gdb) s
    7			sum = sum +  i;
    (gdb) 
    6		for (i = low; i <= high; i++)
    (gdb) p sum
    $1 = 1
    (gdb) s
    7			sum = sum +  i;
    (gdb) 
    6		for (i = low; i <= high; i++)
    (gdb) p sum
    $2 = 3
    

    如果程序运行一切正常,再继续往下跟踪没有意义,可以使用finish命令让程序一直运行到当前函数返回为止,当前正在进行赋值操作。

    (gdb) finish
    Run till exit from #0  add_range (low=1, high=10) at for_gdb.c:6
    main () at for_gdb.c:14
    14		result[0] = add_range(1, 10);
    Value returned is $3 = 55 #赋值
    

    使用s命令继续执行,将进入到add_range(1, 100)的执行步骤中,如果在执行中发现变量有误,可以使用set命令修改变量的值,再使用finish命令查看有没有其他错误。

    (gdb) s
    15		result[1] = add_range(1, 100);
    (gdb) 
    add_range (low=1, high=100) at for_gdb.c:5
    5		int i, sum = 0;
    (gdb) p sum
    $7 = 55
    (gdb) s
    6		for (i = low; i <= high; i++)
    (gdb) 
    7			sum = sum +  i;
    (gdb) p sum
    $8 = 0
    (gdb) set var sum = 100
    (gdb) s
    6		for (i = low; i <= high; i++)
    (gdb) p sum
    $9 = 101
    (gdb) finish
    Run till exit from #0  add_range (low=1, high=100) at for_gdb.c:6
    main () at for_gdb.c:15
    15		result[1] = add_range(1, 100);
    Value returned is $10 = 5150
    

    修改变量的值除了使用set命令,也可以使用print命令,因为print命令后面可以跟表达式,而赋值和函数调用也可以是表达式,所以可以用print命令修改变量的值或者调用函数。(实验显示'printf' has unknown return type; )

    总结本节所使用的命令:

    命令 描述
    backtrace,bt 查看各级函数调用及参数
    finish 连续运行到当前函数返回为止,然后停下来等待命令
    frame,f (帧编号) 选择栈帧
    info,i (locals) 查看当前栈帧局部变量的值
    list,l 列出源代码,接着上次的位置往下列,每次列10行
    list 行号 列出从第几行开始的源代码
    list 函数名 列出某个函数的源代码
    next,n 执行下一行语句
    print,p 打印表达式的值,通过表达式可以修改变量的值,或者调用函数
    quit,q 推出gdb环境
    set var 修改变量的值
    start 开始执行程序,停在main函数第一行语句前面等待命令
    step,s 执行下一行语句,如果有函数调用则进入函数中

    断点

    书籍中的实例:

    #include <stdio.h>
    
    int main(void)
    {       
            int sum = 0, i = 0;
            char input[5];
            while(1)
            {       
                    scanf("%s", input);
                    for (i = 0; input[i] !=''; i++)
                    {       
                            sum = sum * 10 + input[i] - '0';
                    }       
                    printf("input = %d
    ", sum);
            }       
            return 0;
    }       
    

    首先从键盘读入一串数字存储到字符数组input中,然后经过计算后再打印出来。数组的最后一位会添加,这个我们不需要,然后由字符型(比如'1'),转换为int型,可以使用-'0'操作,然后再一直循环下去。编译并运行程序。

    [root@i-co6yw0e5 c]# ./duandian 
    123
    input = 123
    134
    input = 123134
    (ctrl + c 退出程序)
    

    从结果中可以看到,第一次结果是对的,但第二次结果是不对的,这是因为sum一直保存了上一次的结果。在使用gdb进行调试的时候,可以使用display命令来跟踪显示每次停下来时sum的值。

    (gdb) display sum
    2: sum = 0
    (gdb) n
    12				sum = sum * 10 + input[i] - '0';
    2: sum = 0
    (gdb) 
    10			for (i = 0; input[i] !=''; i++)
    2: sum = 1
    (gdb) 
    12				sum = sum * 10 + input[i] - '0';
    2: sum = 1
    (gdb) 
    10			for (i = 0; input[i] !=''; i++)
    2: sum = 12
    (gdb) 
    12				sum = sum * 10 + input[i] - '0';
    2: sum = 12
    

    undisplay命令可以取消跟踪显示,变量sum的编号为2,可以使用undisplay 2命令取消它的跟踪显示。

    (gdb) undisplay 2
    (gdb) n
    10			for (i = 0; input[i] !=''; i++)
    

    如果不像一步一步跟踪这个循环,可以使用break(简写b)命令设置一个断点(breakpoint)。比如在第9行,b 9

    (gdb) l
    1	#include <stdio.h>
    2	
    3	int main(void)
    4	{
    5		int sum = 0, i = 0;
    6		char input[5];
    7		while(1)
    8		{
    9			scanf("%s", input);
    10			for (i = 0; input[i] !=''; i++)
    (gdb) b 9
    Note: breakpoint 3 also set at pc 0x40060c.
    Breakpoint 5 at 0x40060c: file duandian.c, line 9.
    (gdb) display sum
    3: sum = 0
    (gdb) n
    
    Breakpoint 3, main () at duandian.c:9
    9			scanf("%s", input);
    3: sum = 0
    (gdb) 
    123
    10			for (i = 0; input[i] !=''; i++)
    3: sum = 0
    (gdb) 
    12				sum = sum * 10 + input[i] - '0';
    3: sum = 0
    (gdb) c
    Continuing.
    input = 123
    
    Breakpoint 3, main () at duandian.c:9
    9			scanf("%s", input);
    3: sum = 123
    (gdb) 
    Continuing.
    345
    input = 123345
    
    Breakpoint 3, main () at duandian.c:9
    9			scanf("%s", input);
    3: sum = 123345
    

    break命令的参数也可以是函数名,表示在某个函数的开头设置断点。设置断点后,使用continue(简写c)命令连续运行,而不是单步运行,程序到达断点会自动停下来,这样就可以停在下一次循环的开头。

    通过上面的调试,可以发现出错的原因是因为新的循环没有把sum归零。至于应该在哪里设置断点,怎么知道哪些代码可以跳过而哪些代码要慢慢跟踪,也要通过对错误现象的分析和假设来确定。

    可以通过info breakpoints命令来查看已经设置的断点。

    (gdb) i breakpoints
    Num     Type           Disp Enb Address            What
    3       breakpoint     keep y   0x000000000040060c in main at duandian.c:9
    	breakpoint already hit 3 times
    5       breakpoint     keep y   0x000000000040060c in main at duandian.c:9
    	breakpoint already hit 3 times
    

    每个断点都有一个编号,可以通过指定编号删除某个断点。

    (gdb) delete breakpoint 3
    (gdb) delete breakpoint 5
    (gdb) i breakpoints
    No breakpoints or watchpoints.
    

    如果有断点暂时不用,可以将其禁用而不必删除,之后用的时候再启用。

    (gdb) b 9
    Breakpoint 6 at 0x40060c: file duandian.c, line 9.
    (gdb) i breakpoints
    Num     Type           Disp Enb Address            What
    6       breakpoint     keep y   0x000000000040060c in main at duandian.c:9
    (gdb) disable breakpoints 6
    (gdb) i breakpoints
    Num     Type           Disp Enb Address            What
    6       breakpoint     keep n   0x000000000040060c in main at duandian.c:9
    

    区别就在于Enb下面的y/n

    gdb的断点功能非常灵活,可以设置在满足某个条件时才激活,break 9 if sum != 0,然后使用run命令(简写r),重新从程序开头连续运行。

    命令 描述
    break,b(行号) 在某一行设置断点
    break 函数名 在某个函数开头设置断点
    break……if…… 设置条件断点
    continue,c 从当前位置开始连续运行程序
    delete breakpoints 断点号 删除断点
    display 变量名 跟踪查看某个变量,每次停下来都显示它的值
    disable breakpoints 断点号 禁用断点
    enable 断点号 启用断点
    info,i (breakpoints) 查看当前设置了哪些断点
    run,r 从头开始连续运行程序
    undisplay 跟踪显示号 取消跟踪显示

    观察点

    书籍中的实例:

    #include <stdio.h>
    
    int main(void)
    {       
            int sum = 0, i = 0;
            char input[5];
            while(1)
            {       
                    sum = 0;
                    scanf("%s", input);
                    for(i = 0; input[i] != ''; i++)
                            sum = sum * 10 + input[i] - '0';
                    printf("input = %d
    ", sum);
            }       
            return 0;
    }
    

    上面代码修正了2中sum不归零的问题,但是因为使用了scanf函数,如果输入的字符串过长,会造成数组越界,而数组访问越界是不会检查的,所以scanf会写出界,执行结果如下所示:

    [root@i-co6yw0e5 c]# ./guancha 
    123
    input = 123
    67
    input = 67
    12345
    input = 123407
    

    input数组只有5个元素,当我们输入12345后,scanf还会在数组末尾自动添加,使用x命令可以查看的更清楚一点。

    (gdb) start
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Temporary breakpoint 6 at 0x4005fe: file guancha.c, line 5.
    Starting program: /root/c/guancha 
    
    Temporary breakpoint 6, main () at guancha.c:5
    5		int sum = 0, i = 0;
    (gdb) n
    9			sum = 0;
    (gdb) 
    10			scanf("%s", input);
    (gdb) 
    12345
    11			for(i = 0; input[i] != ''; i++)
    (gdb) x/7b input
    0x7fffffffe483:	0x31	0x32	0x33	0x34	0x35	0x00	0x00
    

    x命令打印指定存储单元的内容,7b是打印格式,b表示每个字节一组,7表示打印7组,从input数组的第一个字节开始连续打印7个字节。从结果可以看出,前5个字节是input数组的存储单元字节,打印的是十六进制ASCII码的1~5,第6个字节是写出界的''

    从执行12345的结果看,第5个字符错了,也就是i从0到3的循环没有错,我们可以设置一个条件断点,从i等于4开始单步调试。

    (gdb) start
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Temporary breakpoint 11 at 0x4005fe: file guancha.c, line 5.
    Starting program: /root/c/guancha 
    
    Temporary breakpoint 11, main () at guancha.c:5
    5		int sum = 0, i = 0;
    (gdb) b 12 if i == 4
    Breakpoint 12 at 0x400632: file guancha.c, line 12.
    (gdb) c
    Continuing.
    12345
    
    Breakpoint 12, main () at guancha.c:12
    12				sum = sum * 10 + input[i] - '0';
    (gdb) p sum
    $5 = 1234
    

    从上面的结果看,在i=4之前,sum的值为1234是没有错的,但最终结果却是123407,说明input[4]的值不是'5'了。这种推理,行,但是不严谨,看。

    (gdb) x/7b input
    0x7fffffffe483:	0x31	0x32	0x33	0x34	0x35	0x04	0x00
    

    input[4]的值确实为0x35。产生123407还有一种可能,这个数是123450减掉一个数得到的,按道理讲,input此时应该是字符串的末尾了,不应该有下一次循环,但控制条件是input[i] != '',而本应该是0x00的位置变成了0x04,因此循环不会结束。

    (gdb) p sum
    $5 = 1234
    (gdb) x/7b input
    0x7fffffffe483:	0x31	0x32	0x33	0x34	0x35	0x04	0x00
    (gdb) n
    11			for(i = 0; input[i] != ''; i++)
    (gdb) p sum
    $6 = 12345
    (gdb) x/7b input
    0x7fffffffe483:	0x31	0x32	0x33	0x34	0x35	0x04	0x00
    (gdb) n
    12				sum = sum * 10 + input[i] - '0';
    (gdb) x/7b input
    0x7fffffffe483:	0x31	0x32	0x33	0x34	0x35	0x05	0x00
    

    单步执行一步,此时sum的值就是我们期望输出的12345,注意列出的for循环内容是下一步要执行的内容,所以这一步其实执行的是列出for循环上一步的sum = sum * 10 + input[i] - '0';。继续单步执行,这一步是for循环,判断循环条件,因为'5'后面的值是0x04,所以又进入到了循环体中。同时,input数组中的0x04又变为了0x05,这个原因没有解释。

    下一步,对sum求值,也就是这一步,求出来了123407,这个数是12345*10+0x05-0x30得到的。在进行多余一次的循环后,下次一定会退出循环,因为0x05后面是''

    input[4]后面那个字节是什么时候变的?可以使用观察点(watchpoint)来观察。断点是当程序执行到某一代码行时中断,而观察点是当程序访问某个存储单元时中断,如果我们不知道某个存储单元是在哪里被改动的,这时候观察点尤其有用。

    接下来我们删除之前的断点,防止其对我们的操作有影响。重新运行程序,使用watch命令设置观察点,跟踪input[4]后面的那个字节(可以用input[5]表示,虽然这是访问越界)。

    (gdb) delete breakpoints
    Delete all breakpoints? (y or n) y
    (gdb) start
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Temporary breakpoint 13 at 0x4005fe: file guancha.c, line 5.
    Starting program: /root/c/guancha 
    
    Temporary breakpoint 13, main () at guancha.c:5
    5		int sum = 0, i = 0;
    (gdb) n
    9			sum = 0;
    (gdb) 
    10			scanf("%s", input);
    (gdb) 
    12345
    11			for(i = 0; input[i] != ''; i++)
    (gdb) watch input[5] #设置观察点
    Hardware watchpoint 14: input[5]
    (gdb) i watchpoints
    Num     Type           Disp Enb Address            What
    14      hw watchpoint  keep y                      input[5]
    (gdb) c
    Continuing.
    
    Hardware watchpoint 14: input[5]
    
    Old value = 0 '00'
    New value = 1 '01'
    0x0000000000400659 in main () at guancha.c:11
    11			for(i = 0; input[i] != ''; i++)
    (gdb) 
    Continuing.
    
    Hardware watchpoint 14: input[5]
    
    Old value = 1 '01'
    New value = 2 '02'
    0x0000000000400659 in main () at guancha.c:11
    11			for(i = 0; input[i] != ''; i++)
    (gdb) 
    Continuing.
    
    Hardware watchpoint 14: input[5]
    
    Old value = 2 '02'
    New value = 3 '03'
    0x0000000000400659 in main () at guancha.c:11
    11			for(i = 0; input[i] != ''; i++)
    (gdb) 
    Continuing.
    
    Hardware watchpoint 14: input[5]
    
    Old value = 3 '03'
    New value = 4 '04'
    0x0000000000400659 in main () at guancha.c:11
    11			for(i = 0; input[i] != ''; i++)
    

    从上面内容可以看到,每次回到for循环开头前,该值都会发生增1现象,而循环变量i正是在每次回到循环开头之前加1,所以input[5]就是变量i的存储单元,i的存储单元是紧跟在input数组后面的。

    书中的试题:

    #include <stdio.h>
    
    int main(void)
    {
        int sum = 0, i = 0;
        char input[5];
        while(1)
        {
            sum = 0;
            scanf("%s", input);
            for(i = 0; input[i] != ''; i++)
            {
                if(input[i] < '0' || input[i] > '9')
                {
                    printf("Invalid input
    ");
                    sum = -1;
                    break;
                }
                sum = sum * 10 + input[i] - '0';
            }
            printf("input = %d
    ", sum);
        }
        return 0;
    }
    

    这个题就不详细讲了,这个出现在判断的时候。因为多进行了一次循环,也就是input[5]进入到了循环体中,而input[5]的值为'05',注意,它的值是要比'0'的ascii码值(48)小的,所以最终输出Invalid input, input = -1

    (gdb) 
    13				if(input[i] < '0' || input[i] > '9')
    1: i = 5
    2: input[i] = 5 '05'
    (gdb) 
    15					printf("Invalid input
    ");
    1: i = 5
    2: input[i] = 5 '05'
    (gdb) 
    Invalid input
    16	
    

    本节用到的gdb命令。

    命令 描述
    watch 设置观察点
    info,i (watchpoints) 查看设置了哪些观察点
    x 从某个位置开始打印存储单元的内容,全部当成字节看,而不区分哪个字节属于哪个变量

    段错误

    参考链接:

    当程序试图访问不允许访问的内存位置,或试图以不允许的方式访问内存位置(例如尝试写入只读位置,或覆盖部分操作系统)时会发生段错误。

    书籍中的实例一:

    #include <stdio.h>
    
    int main(void)
    {
    	int man = 0;
    	scanf("%d", man);
    	return 0;
    }
    

    在gdb中运行,遇到段错误,程序会自动停下来。

    (gdb) r
    Starting program: /root/c/duan 
    Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-151.el8.x86_64
    1234
    
    Program received signal SIGSEGV, Segmentation fault.
    0x00007ffff7a6e19c in __GI__IO_vfscanf () from /lib64/libc.so.6
    

    gdb显示段错误出现在__GI__IO_vfscanf函数中,用bt命令可以看到这个函数是被scanf函数调用的,所以是scanf这一行代码引发的段错误,原因是man前面少了一个&

    书籍中的实例二:

    #include <stdio.h>
    
    int main(void)
    {
    	int sum = 0, i = 0;
    	char input[5];
    	scanf("%s", input);
    	for(i = 0; input[i] != ''; i++)
    	{
    		if(input[i] < '0' || input[i] > '9')
    		{
    			printf("Invalid input!
    ");
    			sum = -1;
    			break;
    		}
    		sum = sum * 10 + input[i] - '0';
    	}
    	printf("input = %d
    ", sum);
    	return 0;
    }
    

    编译并使用gdb进行调试。

    (gdb) r
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Starting program: /root/c/duan2 
    123dsfasgeg43rt5dfgwerg34gsdgf
    Invalid input!
    input = -1
    
    Program received signal SIGSEGV, Segmentation fault.
    0x00000000004006f9 in main () at duan2.c:20
    20	}
    (gdb) l
    15			}
    16			sum = sum * 10 + input[i] - '0';
    17		}
    18		printf("input = %d
    ", sum);
    19		return 0;
    20	}
    

    gdb提示段错误出现在20行,但是20行只有main函数结束的花括号。这可以算是一条规律,如果某个函数的局部变量发生访问越界,有可能并不立即产生段错误,而是在函数返回时产生段错误。

    本博客虽然很垃圾,但所有内容严禁转载
  • 相关阅读:
    qt自定义的串口类判断断开
    ubuntu16.04永久修改有线接口名称(enp0s3->eth0)
    记录一下读过的书
    Qt 主界面卡死
    Mysql5.7及版本以上导入sql提示Incorrect date value: '0000-00-00' for column
    webpack打包css
    ant-design-vue中的a-directory-tree更换图标
    解决php-fpm占用内存过高问题
    centos,解压源代码安装,没有configure文件
    OSS存储上遇到The difference between the request time and the current time is too large
  • 原文地址:https://www.cnblogs.com/ahtoh/p/15246820.html
Copyright © 2011-2022 走看看