首页 > 解决方案 > 如何查找指针错误?

问题描述

我正在尝试通过创建国际象棋人工智能来学习 C。当我尝试释放一些内存时,我不小心释放了一些我正在使用的内存。

我通过 valgrind 运行程序,所以我可以看到内存错误在哪里,但我不明白为什么要释放内存。我正在运行 ubuntu 变体(PopOS!)。

这是生成可能的移动并选择一个的代码,我相信错误出现在带有 tempOut->next 变量的 while 循环附近。

void initDataSet()
{

    printf("Starting Data init\n");
    setupBoard(currentBoard->board);
    currentBoard->parent = 0;
    currentBoard->children = 0;
    findPossibleMoves(1, currentBoard); //aiutils.c:189
    depth++;
    struct boardStateList* temp[6];
    int isAITurn[6];
    int bestValue[6];

    int count = 0;
    struct boardStateList* counter = currentBoard->children;

    while(counter)
    {
        count++;
        counter = counter->next;
    }


    for(int i = 0;i < 6;i++)
        if(((depth + i)% 2) == isAIWhite)
        {
            isAITurn[i] = 1; 
        }
        else
        {
            isAITurn[i] = 0;
        }
        int turn = 1;
    temp[0] = currentBoard->children;
            bestValue[0] = (depth - 1) % 2 == isAIWhite ? INT_MIN : INT_MAX;    
        while(temp[0])
        {
            printf("calc Move %d out of %d\n", turn++, count);

            bestValue[1] = (depth) % 2 == isAIWhite ? INT_MIN : INT_MAX;  
            findPossibleMoves((depth % 2), temp[0]->element);
            temp[1] = temp[0]->element->children;
            while(temp[1])
            {
                bestValue[2] = (depth + 1) % 2 == isAIWhite ? INT_MIN : INT_MAX;  
                findPossibleMoves(((depth + 1) % 2), temp[1]->element);
                temp[2] = temp[1]->element->children;
                while(temp[2])
                {
                    bestValue[3] = (depth + 2) % 2 == isAIWhite ? INT_MIN : INT_MAX;  
                    findPossibleMoves(((depth + 2) % 2), temp[2]->element);
                    temp[3] = temp[2]->element->children;
                    while(temp[3])
                    {
                        bestValue[4] = (depth + 3) % 2 == isAIWhite ? INT_MIN : INT_MAX;  
                        findPossibleMoves(((depth + 3) % 2), temp[3]->element);
                        temp[4] = temp[3]->element->children;
                        while(temp[4])
                        {
                            bestValue[5] = (depth + 4) % 2 == isAIWhite ? INT_MIN : INT_MAX;  
                            findPossibleMoves(((depth + 4) % 2),temp[4]->element);
                            temp[5] = temp[4]->element->children;
                            while(temp[5])
                            {
                                temp[5]->element->boardValue = evaluateBoard(temp[5]->element->board, isAIWhite);
                                if((depth + 4) % 2 == isAIWhite ? temp[5]->element->boardValue > bestValue[5] : temp[5]->element->boardValue < bestValue[5])
                                    bestValue[5] = temp[5]->element->boardValue;
                                temp[5] = temp[5]->next;    
                            }
                            temp[4]->element->boardValue = bestValue[5];
                            if((depth + 3) % 2 == isAIWhite ? temp[4]->element->boardValue > bestValue[4] : temp[4]->element->boardValue < bestValue[4])
                                bestValue[4] = temp[4]->element->boardValue;
                            temp[4] = temp[4]->next;
                        }
                        temp[3]->element->boardValue = bestValue[4];
                        if((depth + 2) % 2 == isAIWhite ? temp[3]->element->boardValue > bestValue[3] : temp[3]->element->boardValue < bestValue[3])
                            bestValue[3] = temp[3]->element->boardValue;
                        temp[3] = temp[3]->next;
                    }


                    temp[2]->element->boardValue = bestValue[3];
                    if((depth + 1) % 2 == isAIWhite ? temp[2]->element->boardValue > bestValue[2] : temp[2]->element->boardValue < bestValue[2])
                        bestValue[2] = temp[2]->element->boardValue;
                    temp[2] = temp[2]->next;
                }
                temp[1]->element->boardValue = bestValue[2];
                if((depth) % 2 == isAIWhite ? temp[1]->element->boardValue > bestValue[1] : temp[1]->element->boardValue < bestValue[1])
                    bestValue[1] = temp[1]->element->boardValue;
                temp[1] = temp[1]->next;
            }
            temp[0]->element->boardValue = bestValue[1];
            if((depth - 1) % 2 == isAIWhite ? temp[0]->element->boardValue > bestValue[0] : temp[0]->element->boardValue < bestValue[0])
                bestValue[0] = temp[0]->element->boardValue;
            temp[0] = temp[0]->next;
        }


        if((depth - 1 ) % 2 == isAIWhite)
        {

            struct boardStateList* tempOut = malloc(sizeof(struct boardStateList));
            struct boardStateList* tempOutCopy = tempOut;   
            tempOut->next = currentBoard->children;


            while(tempOut->next)
            {
                if(tempOut->next->element->boardValue == bestValue[0])
                {
                    currentBoard = tempOut->next->element;
                    tempOut->next = tempOut->next->next;
                    break;
                }
                tempOut = tempOut->next;
            }

            free(tempOutCopy);

            killChildren(currentBoard->parent);

            printBoard(currentBoard->board); //aiutils.c:303
        }
        else
        {
            int sourceRow, sourceCol, destRow, destCol;
            printf("Source Row:");
            scanf("%d", &sourceRow);
            printf("Source Col:");
            scanf("%d", &sourceCol);
            printf("destRow:");
            scanf("%d", &destRow);
            printf("destCol:");
            scanf("%d", &destCol);

            struct boardStateList* tempOut = malloc(sizeof(struct boardStateList));
            struct boardStateList* tempOutCopy = tempOut;   
            tempOut->next = currentBoard->children;


            while(tempOut->next)
            {
                if(currentBoard->board[sourceRow * 8 + sourceCol] == tempOut->next->element->board[destRow * 8 + destCol])
                {
                    currentBoard = tempOut->next->element;
                    tempOut->next = tempOut->next->next;
                    break;
                }
                tempOut = tempOut->next;
            }

            if(!tempOut)
            {
                printf("No Move Found\n");
                exit(1);
            }

            free(tempOutCopy);

            killChildren(currentBoard->parent);

        }



}

此代码为板分配内存

void addChildBoard(struct boardState* parent, int sourceRow, int sourceCol, int destRow, int destCol)
{
    //create new childboard and init values
    struct boardState* child = malloc(sizeof(struct boardState));
    if(!child)
        printf("OOM");
    child->parent = parent;
    child->children = 0;
    //add child to parent linked lisk
    struct boardStateList* current = parent->children;
    if(!current)
    {
        parent->children = malloc(sizeof(struct boardStateList));
        if(!parent->children)
            printf("OOM");  
        parent->children->element = child;
        parent->children->next = 0;
    }
    else
    {
        while((current->next))
            current = current->next;
        current->next = malloc(sizeof(struct boardStateList));
        if(!current->next)
            printf("OOM");
        current->next->element = child;
        current->next->next = 0;
    }
    //init child board to parent then make change
    //for(int i = 0;i < 64;i++)
    //  child->board[i] = parent->board[i];
    memcpy(child->board, parent->board, 64);
    child->board[destRow * 8 + destCol] = child->board[sourceRow * 8 + sourceCol];
    child->board[sourceRow * 8 + sourceCol] = 0;
}

此代码释放内存

void killChildren(struct boardState* parent)
{
    struct boardStateList* temp[7];

    temp[0] = parent->children;
        while(temp[0])
        {
            temp[1] = temp[0]->element->children;
            while(temp[1])
            {
                temp[2] = temp[1]->element->children;
                while(temp[2])
                {
                    temp[3] = temp[2]->element->children;
                    while(temp[3])
                    {
                        temp[4] = temp[3]->element->children;
                        while(temp[4])
                        {
                            temp[5] = temp[4]->element->children;
                            while(temp[5])

                            {
                                free(temp[5]->element);
                                temp[6] = temp[5];
                                temp[5] = temp[5]->next;
                                free(temp[6]);
                            }

                            free(temp[4]->element);
                            temp[6] = temp[4];
                            temp[4] = temp[4]->next;
                            free(temp[6]);
                        }
                        free(temp[3]->element);
                        temp[6] = temp[3];
                        temp[3] = temp[3]->next;
                        free(temp[6]);
                    }


                    free(temp[2]->element);
                    temp[6] = temp[2];
                    temp[2] = temp[2]->next;
                    free(temp[6]);
                }
                free(temp[1]->element);
                temp[6] = temp[1];
                temp[1] = temp[1]->next;
                free(temp[6]);
            }
            free(temp[0]->element); //aiutils.c:742
            temp[6] = temp[0];
            temp[0] = temp[0]->next;
            free(temp[6]);
        }


}

当我尝试读取其中一个输出板时,它部分出现乱码,然后程序段错误。这是 valgrind 的输出

==3013== Memcheck, a memory error detector
==3013== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3013== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==3013== Command: ./chess white
==3013== Parent PID: 2898
==3013== 
--3013-- 
--3013-- Valgrind options:
--3013--    --leak-check=full
--3013--    --show-leak-kinds=all
--3013--    --track-origins=yes
--3013--    --verbose
--3013--    --log-file=valgrind-out.txt
--3013-- Contents of /proc/version:
--3013--   Linux version 4.14.123-111.109.amzn2.x86_64 (mockbuild@ip-10-0-1-12) (gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)) #1 SMP Mon Jun 10 19:37:57 UTC 2019
--3013-- 
--3013-- Arch and hwcaps: AMD64, LittleEndian, amd64-cx16-lzcnt-rdtscp-sse3-avx-avx2-bmi
--3013-- Page sizes: currently 4096, max supported 4096
--3013-- Valgrind library directory: /usr/lib64/valgrind
--3013-- Reading syms from /home/ec2-user/chess-ai/chess
--3013-- Reading syms from /usr/lib64/ld-2.26.so
--3013-- Reading syms from /usr/lib64/valgrind/memcheck-amd64-linux
--3013--    object doesn't have a symbol table
--3013--    object doesn't have a dynamic symbol table
--3013-- Scheduler: using generic scheduler lock implementation.
--3013-- Reading suppressions file: /usr/lib64/valgrind/default.supp
==3013== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-3013-by-ec2-user-on-ip-172-31-14-154.ec2.internal
==3013== embedded gdbserver: writing to   /tmp/vgdb-pipe-to-vgdb-from-3013-by-ec2-user-on-ip-172-31-14-154.ec2.internal
==3013== embedded gdbserver: shared mem   /tmp/vgdb-pipe-shared-mem-vgdb-3013-by-ec2-user-on-ip-172-31-14-154.ec2.internal
==3013== 
==3013== TO CONTROL THIS PROCESS USING vgdb (which you probably
==3013== don't want to do, unless you know exactly what you're doing,
==3013== or are doing some strange experiment):
==3013==   /usr/lib64/valgrind/../../bin/vgdb --pid=3013 ...command...
==3013== 
==3013== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==3013==   /path/to/gdb ./chess
==3013== and then give GDB the following command
==3013==   target remote | /usr/lib64/valgrind/../../bin/vgdb --pid=3013
==3013== --pid is optional if only one valgrind process is running
==3013== 
--3013-- REDIR: 0x401cd20 (ld-linux-x86-64.so.2:strlen) redirected to 0x5805bed1 (???)
--3013-- REDIR: 0x401cb00 (ld-linux-x86-64.so.2:index) redirected to 0x5805beeb (???)
--3013-- Reading syms from /usr/lib64/valgrind/vgpreload_core-amd64-linux.so
--3013-- Reading syms from /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so
==3013== WARNING: new redirection conflicts with existing -- ignoring it
--3013--     old: 0x0401cd20 (strlen              ) R-> (0000.0) 0x5805bed1 ???
--3013--     new: 0x0401cd20 (strlen              ) R-> (2007.0) 0x04c2fc50 strlen
--3013-- REDIR: 0x401ad90 (ld-linux-x86-64.so.2:strcmp) redirected to 0x4c30d80 (strcmp)
--3013-- REDIR: 0x401d260 (ld-linux-x86-64.so.2:mempcpy) redirected to 0x4c34390 (mempcpy)
--3013-- Reading syms from /usr/lib64/libc-2.26.so
--3013-- REDIR: 0x4ebf9b0 (libc.so.6:memmove) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebebe0 (libc.so.6:strncpy) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebfc90 (libc.so.6:strcasecmp) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebe630 (libc.so.6:strcat) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebec10 (libc.so.6:rindex) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ec1260 (libc.so.6:rawmemchr) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebfb20 (libc.so.6:mempcpy) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebf950 (libc.so.6:bcmp) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebeba0 (libc.so.6:strncmp) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebe6a0 (libc.so.6:strcmp) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebfa80 (libc.so.6:memset) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ed94c0 (libc.so.6:wcschr) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebeb40 (libc.so.6:strnlen) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebe710 (libc.so.6:strcspn) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebfce0 (libc.so.6:strncasecmp) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebe6e0 (libc.so.6:strcpy) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebfe20 (libc.so.6:memcpy@@GLIBC_2.14) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebec40 (libc.so.6:strpbrk) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebe660 (libc.so.6:index) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebeb10 (libc.so.6:strlen) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ec5b10 (libc.so.6:memrchr) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebfd30 (libc.so.6:strcasecmp_l) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebf920 (libc.so.6:memchr) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4eda280 (libc.so.6:wcslen) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebeef0 (libc.so.6:strspn) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebfc60 (libc.so.6:stpncpy) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebfc30 (libc.so.6:stpcpy) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ec1290 (libc.so.6:strchrnul) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4ebfd80 (libc.so.6:strncasecmp_l) redirected to 0x4a2771e (_vgnU_ifunc_wrapper)
--3013-- REDIR: 0x4f8ebf0 (libc.so.6:__strrchr_avx2) redirected to 0x4c2f5d0 (rindex)
--3013-- REDIR: 0x4ebaf00 (libc.so.6:malloc) redirected to 0x4c2cb0d (malloc)
--3013-- REDIR: 0x4f8edc0 (libc.so.6:__strlen_avx2) redirected to 0x4c2fb90 (strlen)
--3013-- REDIR: 0x4f8ea00 (libc.so.6:__strchrnul_avx2) redirected to 0x4c33ec0 (strchrnul)
--3013-- REDIR: 0x4f8f2e0 (libc.so.6:__mempcpy_avx_unaligned_erms) redirected to 0x4c33fd0 (mempcpy)
--3013-- REDIR: 0x4ebb590 (libc.so.6:free) redirected to 0x4c2dcba (free)
==3013== Invalid read of size 1
==3013==    at 0x404062: prinmtBoard (chessutils.c:254)
==3013==    by 0x403625: initDataSet (aiutils.c:303)
==3013==    by 0x4006EB: main (main.c:38)
==3013==  Address 0x51ef524 is 4 bytes inside a block of size 88 free'd
==3013==    at 0x4C2DD28: free (vg_replace_malloc.c:530)
==3013==    by 0x402604: killChildren (aiutils.c:742)
==3013==    by 0x403615: initDataSet (aiutils.c:301)
==3013==    by 0x4006EB: main (main.c:38)
==3013==  Block was alloc'd at
==3013==    at 0x4C2CB7B: malloc (vg_replace_malloc.c:299)
==3013==    by 0x400988: addChildBoard (aiutils.c:657)
==3013==    by 0x400DA2: findPossiblePawnMoves (aiutils.c:472)
==3013==    by 0x402F6B: findPossibleMoves (aiutils.c:434)
==3013==    by 0x402F6B: initDataSet (aiutils.c:189)
==3013==    by 0x4006EB: main (main.c:38)
==3013== 
==3013== 
==3013== Process terminating with default action of signal 1 (SIGHUP)
==3013==    at 0x4F1F892: write (in /usr/lib64/libc-2.26.so)
==3013==    by 0x4EB1D7C: _IO_file_write@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==3013==    by 0x4EB105E: new_do_write (in /usr/lib64/libc-2.26.so)
==3013==    by 0x4EB2F68: _IO_do_write@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==3013==    by 0x4EB3011: _IO_file_underflow@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.so)
==3013==    by 0x4EB41E1: _IO_default_uflow (in /usr/lib64/libc-2.26.so)
==3013==    by 0x4E9503C: _IO_vfscanf (in /usr/lib64/libc-2.26.so)
==3013==    by 0x4EA3957: __isoc99_scanf (in /usr/lib64/libc-2.26.so)
==3013==    by 0x4026D3: nextMove (aiutils.c:24)
==3013==    by 0x4006F6: main (main.c:42)
==3013== 
==3013== HEAP SUMMARY:
==3013==     in use at exit: 104 bytes in 2 blocks
==3013==   total heap usage: 232,874,525 allocs, 232,874,523 frees, 12,109,477,208 bytes allocated
==3013== 
==3013== Searching for pointers to 2 not-freed blocks
==3013== Checked 69,400 bytes
==3013== 
==3013== 16 bytes in 1 blocks are still reachable in loss record 1 of 2
==3013==    at 0x4C2CB7B: malloc (vg_replace_malloc.c:299)
==3013==    by 0x402670: nextMove (aiutils.c:15)
==3013==    by 0x4006F6: main (main.c:42)
==3013== 
==3013== 88 bytes in 1 blocks are definitely lost in loss record 2 of 2
==3013==    at 0x4C2CB7B: malloc (vg_replace_malloc.c:299)
==3013==    by 0x4006DD: main (main.c:37)
==3013== 
==3013== LEAK SUMMARY:
==3013==    definitely lost: 88 bytes in 1 blocks
==3013==    indirectly lost: 0 bytes in 0 blocks
==3013==      possibly lost: 0 bytes in 0 blocks
==3013==    still reachable: 16 bytes in 1 blocks
==3013==         suppressed: 0 bytes in 0 blocks
==3013== 
==3013== ERROR SUMMARY: 65 errors from 2 contexts (suppressed: 0 from 0)
==3013== 
==3013== 64 errors in context 1 of 2:
==3013== Invalid read of size 1
==3013==    at 0x404062: printBoard (chessutils.c:254)
==3013==    by 0x403625: initDataSet (aiutils.c:303)
==3013==    by 0x4006EB: main (main.c:38)
==3013==  Address 0x51ef524 is 4 bytes inside a block of size 88 free'd
==3013==    at 0x4C2DD28: free (vg_replace_malloc.c:530)
==3013==    by 0x402604: killChildren (aiutils.c:742)
==3013==    by 0x403615: initDataSet (aiutils.c:301)
==3013==    by 0x4006EB: main (main.c:38)
==3013==  Block was alloc'd at
==3013==    at 0x4C2CB7B: malloc (vg_replace_malloc.c:299)
==3013==    by 0x400988: addChildBoard (aiutils.c:657)
==3013==    by 0x400DA2: findPossiblePawnMoves (aiutils.c:472)
==3013==    by 0x402F6B: findPossibleMoves (aiutils.c:434)
==3013==    by 0x402F6B: initDataSet (aiutils.c:189)
==3013==    by 0x4006EB: main (main.c:38)
==3013== 
==3013== ERROR SUMMARY: 65 errors from 2 contexts (suppressed: 0 from 0)

标签: cpointersmemory

解决方案


该代码对阅读 x) 没有吸引力,但我将向您展示如何使用 valgrind 的输出来自己查找问题;)

==3013== Invalid read of size 1
==3013==    at 0x404062: prinmtBoard (chessutils.c:254)
==3013==    by 0x403625: initDataSet (aiutils.c:303)
==3013==    by 0x4006EB: main (main.c:38)
==3013==  Address 0x51ef524 is 4 bytes inside a block of size 88 free'd
==3013==    at 0x4C2DD28: free (vg_replace_malloc.c:530)
==3013==    by 0x402604: killChildren (aiutils.c:742)
==3013==    by 0x403615: initDataSet (aiutils.c:301)
==3013==    by 0x4006EB: main (main.c:38)
==3013==  Block was alloc'd at
==3013==    at 0x4C2CB7B: malloc (vg_replace_malloc.c:299)
==3013==    by 0x400988: addChildBoard (aiutils.c:657)
==3013==    by 0x400DA2: findPossiblePawnMoves (aiutils.c:472)
==3013==    by 0x402F6B: findPossibleMoves (aiutils.c:434)
==3013==    by 0x402F6B: initDataSet (aiutils.c:189)
==3013==    by 0x4006EB: main (main.c:38)

这里有 1 个内存错误的 3 个详细信息:

  • 该错误是大小为 1 的无效读取,因此您尝试访问(不再)在您的内存中的 1 字节的内存区域。(发生在 chessutils.c:254 的第 254 行)
  • 地址 xxx 是一个 88 块释放的 4 个字节。这意味着您尝试访问的内存曾经是您的,但不再是因为您刚刚释放了它,在 aiutils.c 的第 742 行
  • 您释放然后使用的内存块被分配在文件 aiutils 的第 657 行。

这通常应该足以跟踪您的代码过程并找到错误,找到 malloc,释放它的地方,然后是您仍然使用不再属于您的内存的任何地方;)

如果在此之后您仍然崩溃,但错误发生了变化,那么您可能取得了进展。不要忘记一个错误总是可以被另一个错误隐藏,在内存调试方面更是如此。

祝你好运o/


推荐阅读