第17章 指针的高级用法

作者: 橡树人 | 来源:发表于2020-03-09 10:56 被阅读0次

    英文原版:P413

    本章主要内容:

    • 17.1节介绍动态内存分配的基础知识;
    • 17.2节介绍如何动态分配字符串,这要比普通的字符数组更灵活;
    • 17.3节如何动态分配数组;
    • 17.4节介绍当动态分配的内存块不再使用时,如何释放该内存块;
    • 17.5节重点讨论链表,这是最基础的链式数据结构;
    • 17.6节介绍指向指针的指针的概念;
    • 17.7节介绍指向函数的指针,这是一个非常有用的概念,比如C语言中的一些功能强大的库函数都使用函数指针作为参数,比如给任意数组排序的库函数qsort
    • 17.8节介绍受限指针特征;
    • 17.9节介绍灵活的数组成员特征;

    17.1节 动态存储分配

    C语言的数据结构通常都是大小的固定的,比如当程序被编译后,一个数组的元素个数就是固定的。

    • 由于在编写程序时需要选择这些数据结构的大小,所以固定大小的数据结构可能会出现问题;
    • 如果不修改程序且重新编译程序的话,就不可能改变这些数据结构的大小。

    考虑在16.3节中的inventory程序,允许用户向数据库中添加零件。数据库是用大小为100的数组存储的。为了增大数据库的容量,我们可以增加数组的大小,重新编译该程序。但是,无论我们使数组变得有多大,总是有可能出现数组被填满。

    幸运的是,C语言支持动态存储分配,即在程序执行时分配内存。

    使用动态存储分配,我们可以设计按需增长和缩小的数据结构。

    虽然动态存储分配可用于所有数据类型,但是动态存储分配被常用于字符串、数组、结构体等。

    由于可将动态分配结构体链接起来形成链表、树及其他数据结构,所以我们重点关注动态分配结构体。

    17.1.1小节 内存分配函数

    为了动态分配存储,我们需要调用在<stdlib.h>中声明的三个内存分配函数之一:

    • malloc:分配一个内存块,但不初始化该内存块;
    • calloc:分配一个内存块,并清空该内存块;
    • realloc:重新定义之前已分配的内存块的大小;

    注:

    • malloc函数是最经常使用的;
    • malloc函数比calloc函数更有效率;
    • 3个内存分配函数的返回值是void *类型;

    当我们调用某个内存分配函数来请求一块内存时,该函数是不知道我们计划在该内存块里存储的数据类型的,所以内存分配函数不能返回一个指向普通数据类型(比如int或者char)的指针,而是返回一个void *类型的值。

    void *类型

    • void *类型的值是一个泛型指针,本质上是一个内存地址;

    17.1.2小节 空指针

    当调用内存分配函数时,经常有一种可能:该函数不能分配一块足够大的内存来满足我们的请求。
    如果这种情形发生了,则该函数返回一个空指针。

    • 空指针是一个指针,该指针不指向任何对象;
    • 空指针是一个特殊的值,用于区分有效指针和无效指针;

    编程提示:

    • 只要函数的返回值是一个指针变量,则需要检查该返回值是否是空指针;
    • 测试内存分配函数的返回值,及如果该返回值是空指针,该采取什么样的措施,都是程序员的职责;
    • 尝试通过一个空指针去访问内存的效果是未定义的:程序可能崩溃或者出现不符合预期的行为;

    空指针是用宏NULL来表示的,定义在6个头文件里:<locale.h><stddef.h><stdio.h><stdlib.h><time.h>

    程序中只要包含任何一个头文件,就能保证编译器可识别NULL
    任何使用内存分配函数的程序都会包含头文件<stdlib.h>,当然会使NULL可用的。

    • 任何非空指针的测试都为true;
    • 只有空指针的测试为false;

    例1 测试malloc的返回值是否为NULL

    p = malloc(10000);
    if (p == NULL) {
      /* 分配失败;应采取的措施*/
    }
    

    相关文章

      网友评论

        本文标题:第17章 指针的高级用法

        本文链接:https://www.haomeiwen.com/subject/afludhtx.html