OSTEP:系统API的调用

第十四章:插叙:内存操作API

  1. 执行null文件后并没有提示或报错,代码如下

    1
    2
    3
    4
    5
    6
    int main()
    {
    int *pt = NULL;
    free(pt); // 释放空指针
    return 0;
    }
  2. 在执行完gdb null后的run后,提示了以下信息

    log
    1
    2
    3
    (gdb) run
    Starting program: /home/halc/code/cpp/null
    [Inferior 1 (process 9285) exited normally]
  3. 在使用valgrind检查后,可以得到以下输出信息

    log
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ==9579== Memcheck, a memory error detector
    ==9579== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==9579== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==9579== Command: ./null
    ==9579==
    ==9579==
    ==9579== HEAP SUMMARY:
    ==9579== in use at exit: 0 bytes in 0 blocks
    ==9579== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
    ==9579==
    ==9579== All heap blocks were freed -- no leaks are possible
    ==9579==
    ==9579== For lists of detected and suppressed errors, rerun with: -s
    ==9579== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

    根据C/C++ Preferencefree()的介绍如下

    If is a null pointer, the function does nothing. ptr
    当指针是空指针的时候,啥都不会发生,因此也理所当然的没有发生任何的内存泄露

  4. 首先使用malloc()函数对内存空间进行分配,但是不使用free()对内存进行释放

    1
    2
    3
    4
    5
    6
    int main()
    {
    int *pt = (int *)malloc(sizeof(int));
    // free(pt);
    return 0;
    }

    使用gdb对可执行文件进行调试如下

    gdb log
    1
    2
    Starting program: /home/halc/code/cpp/null 
    [Inferior 1 (process 9978) exited normally]

    使用valgrind对可执行文件调试入如下

    valgrind log
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    $ valgrind --leak-check=yes ./null
    ==9930== Memcheck, a memory error detector
    ==9930== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==9930== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==9930== Command: ./null
    ==9930==
    ==9930==
    ==9930== HEAP SUMMARY:
    ==9930== in use at exit: 4 bytes in 1 blocks
    ==9930== total heap usage: 1 allocs, 0 frees, 4 bytes allocated
    ==9930==
    ==9930== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
    ==9930== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==9930== by 0x10915E: main (null.cpp:6)
    ==9930==
    ==9930== LEAK SUMMARY:
    ==9930== definitely lost: 4 bytes in 1 blocks
    ==9930== indirectly lost: 0 bytes in 0 blocks
    ==9930== possibly lost: 0 bytes in 0 blocks
    ==9930== still reachable: 0 bytes in 0 blocks
    ==9930== suppressed: 0 bytes in 0 blocks
    ==9930==
    ==9930== For lists of detected and suppressed errors, rerun with: -s
    ==9930== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

    这个时候通过valgrind工具即可检查出,在堆空间中有definitely lost(确定的内存泄露)4 bytes in 1 blocks,正好对应了代码中sizeof(int)的大小和数量

  5. 首先使用malloc()函数分配100个int的空间给指针data,然后在data[100]的位置赋值

    1
    2
    3
    4
    5
    6
    7
    int main()
    {
    int *data = (int *)malloc(sizeof(int) * 100);
    data[100] = 0;
    free(data);
    return 0;
    }

    程序直接运行的时候不会报错,没有任何提示。但是使用valgrind进行检查的时候有如下日志

    log
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    $ valgrind --leak-check=yes ./null
    ==10500== Memcheck, a memory error detector
    ==10500== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==10500== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==10500== Command: ./null
    ==10500==
    ==10500== Invalid write of size 4
    ==10500== at 0x10918D: main (null.cpp:7)
    ==10500== Address 0x4a4c1d0 is 0 bytes after a block of size 400 alloc'd
    ==10500== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==10500== by 0x10917E: main (null.cpp:6)
    ==10500==
    ==10500==
    ==10500== HEAP SUMMARY:
    ==10500== in use at exit: 0 bytes in 0 blocks
    ==10500== total heap usage: 1 allocs, 1 frees, 400 bytes allocated
    ==10500==
    ==10500== All heap blocks were freed -- no leaks are possible
    ==10500==
    ==10500== For lists of detected and suppressed errors, rerun with: -s
    ==10500== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

    可以看到valgrind成功检测出一个无效的写入,在内存分配之后,只有data[0] - data[99]是可以正常写入的,data[100]并不存在,因此出现了无效写入的错误

  6. 用和第五题相似的方法创建一个数组,然后通过free()函数对内存释放,然后对已经释放的空间进行读取

    1
    2
    3
    4
    5
    6
    7
    int main()
    {
    int *data = (int *)malloc(sizeof(int) * 100);
    free(data);
    printf("%d\n", data[0]);
    return 0;
    }

    在本地电脑上直接运行的时候输出了0,使用valgrind工具进行检查的时候输出了如下日志

    log
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    ==10682== Memcheck, a memory error detector
    ==10682== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==10682== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==10682== Command: ./null
    ==10682==
    ==10682== Invalid read of size 4
    ==10682== at 0x1091B3: main (null.cpp:8)
    ==10682== Address 0x4a4c040 is 0 bytes inside a block of size 400 free'd
    ==10682== at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==10682== by 0x1091AE: main (null.cpp:7)
    ==10682== Block was alloc'd at
    ==10682== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==10682== by 0x10919E: main (null.cpp:6)
    ==10682==
    0
    ==10682==
    ==10682== HEAP SUMMARY:
    ==10682== in use at exit: 0 bytes in 0 blocks
    ==10682== total heap usage: 2 allocs, 2 frees, 1,424 bytes allocated
    ==10682==
    ==10682== All heap blocks were freed -- no leaks are possible
    ==10682==
    ==10682== For lists of detected and suppressed errors, rerun with: -s
    ==10682== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

    可以看见检测出了无效的读写Invalid read of size 4

  7. data数组中的data[50]进行内存释放,然后输出data[0]

    1
    2
    3
    4
    5
    6
    7
    8
    int main()
    {
    int *data = (int *)malloc(sizeof(int) * 100);
    // free(data);
    free(data + 50);
    printf("%d\n", data[0]);
    return 0;
    }

    在直接运行的时候会直接报错

    1
    2
    3
    $ ./null
    free(): invalid pointer
    [1] 10986 abort ./null

    不需要用valgrind也可以检查出此处有free()的问题

    根据 c-free-invalid-pointer 的回答,可以知道

    When you have in fact allocated a block of memory, you can only free it from the pointer returned by. That is to say, only from the beginning of the block.

    当我们分配了一块内存的时候,我们只能从返回的指针开始对这块内存进行释放,也就是说我们只能从内存块的开头对某一处内存进行释放

  8. 通过realloc()函数实现一个类似vector的操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int main()
    {
    int *pt = (int *)malloc(sizeof(int)); // 给指针pt分配1个int大小空间的内存
    *pt = 10; // 对对应内存位置进行赋值
    pt = (int *)realloc(pt, sizeof(int) * 2); // 将原本的变量扩容为2个int大小空间
    pt[1] = 20; // 对新扩大的内存位置进行赋值
    printf("%d, %d\n", pt[0], pt[1]); // 输出pt对应的两个内存位置的结果
    free(pt); // 释放堆内存中的pt
    return 0;
    }

    程序编译可以成功通过,使用valgrind也正常通过,没有无效读写错误

    通过这种操作实现的vector可以在访问的时候直接通过index进行访问,时间复杂度为O(1),并且他并不需要和链表一样创建一个指向next的指针,不过在向后添加内容的时候依旧需要O(n)的时间复杂度来向后添加元素。

    顺便记录一个Tips, 在C中是允许void *类型进行任意转换的,因此即使没有(int *)也不会出现报错,而在C++中对类型转换的限制更多,并不允许直接进行这样的操作,必须要进行类型转换(通过static_cast转换运算符)才能分配空间给对应的指针。

  9. 偷懒,懒得写


OSTEP:系统API的调用
https://halc.top/p/264bf58c
作者
HalcyonAzure
发布于
2022年4月10日
许可协议