> For the complete documentation index, see [llms.txt](https://labspc.gitbook.io/cnippets/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://labspc.gitbook.io/cnippets/chap8.-wen-jian-du-xie-yu-nei-cun-guan-li/8.4-cun-chu-guan-li-han-shu.md).

# 7.1 存储管理函数

**Dynamic memory management**

**学习步骤：**

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700502074617-eb009560-9cef-44f8-ae04-ff886e88c6ff.png" alt="" width="200">

2023-11-23 周四

2024-03-24 Upload

## 7.1 malloc() / free() <a href="#r1hxn" id="r1hxn"></a>

`malloc()` 函数是 C 语言中用于动态分配内存的函数之一，它允许在程序运行时按需请求指定大小的内存空间。这个函数位于 `<stdlib.h>` 头文件中。

#### `malloc()` 函数的基本用法： <a href="#f92110d3" id="f92110d3"></a>

```
#include <stdlib.h>

void *malloc(size_t size);
```

* 参数： `size` 参数表示要分配的内存大小，以字节为单位。返回类型是 `void*`，即指向分配内存起始位置的指针。
* 返回值： 如果内存分配成功，返回指向分配内存的指针；如果分配失败，则返回 `NULL`。

#### 使用示例： <a href="#a0c1aa89" id="a0c1aa89"></a>

```c
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr;
    int n = 5;

    // 分配内存空间给指定大小的整型数组
    ptr = (int *)malloc(n * sizeof(int));

    if (ptr == NULL) {
        printf("内存分配失败！\n");
        return 1;
    }

    // 使用分配的内存空间
    for (int i = 0; i < n; ++i) {
        ptr[i] = i; // 对分配的内存进行写入
        printf("%d ", ptr[i]); // 读取并输出分配的内存内容
    }

    // 释放动态分配的内存
    free(ptr);
    ptr = NULL; // 建议将指针置为 NULL，以避免悬空指针

    return 0;
}
```

#### 注意事项： <a href="#id-1c7fa641" id="id-1c7fa641"></a>

1. 检查分配是否成功： 始终检查 `malloc()` 返回的指针是否为 `NULL`，以确保内存分配成功。
2. 释放动态分配的内存： 使用 `free()` 函数释放通过 `malloc()` 分配的内存，避免内存泄漏。
3. 类型转换： 在 C++ 中，不需要显式类型转换 `malloc()` 返回的指针，而在 C 中需要进行显式类型转换。

`malloc()` 是动态内存分配中最基本和常用的函数之一，但在使用时需要小心管理内存，避免内存泄漏和指针操作错误。随后的 C 标准引入了更多安全和方便的内存管理函数，如 `calloc()` 和 `realloc()`，它们可以更好地满足不同的需求。

当你使用 `malloc()` 分配了内存后，你可以将这块内存用于存储数据或进行其他操作。以下是一个示例，演示了如何正确分配内存并使用指针进行操作：

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    int *ptr = (int *)malloc(10 * sizeof(int)); // 分配足够存储 10 个整数的内存空间
    if (ptr == NULL) {
        printf("malloc failed: Unable to allocate memory\n");
        return 1;
    }

    // 将数组 arr 的内容复制到动态分配的内存中
    memcpy(ptr, arr, 10 * sizeof(int));

    // 打印动态分配内存中的内容
    for (int i = 0; i < 10; ++i) {
        printf("%d ", ptr[i]);
    }
    printf("\n");

    // 释放动态分配的内存
    free(ptr);
    ptr = NULL; // 将指针置为 NULL，避免悬空指针

    return 0;
}
```

int \*ptr = (int \*)malloc(10 \* sizeof(int));: 使用 malloc() 函数分配了足够存储 10 个整数的内存空间，并将分配得到的地址赋值给了 ptr 指针。这行代码中的 (int \*) 是一种类型转换，将 malloc() 返回的 void\* 类型指针强制转换为 int\* 类型指针。

在这个示例中，我使用 `malloc()` 分配了足够存储 10 个整数的内存空间，并用 `memcpy()` 将数组 `arr` 的内容复制到了动态分配的内存中。然后通过循环打印了动态分配内存中的内容，最后使用 `free()` 释放了分配的内存。

这个例子展示了如何正确分配和使用动态内存。记得在不再需要动态分配的内存时使用 `free()` 函数来释放它，以免出现内存泄漏问题。

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700780406882-35e498b0-a2ad-47f0-91f5-91fd5c414d86.png" alt="" width="431">

分配给一块空间，用，用完没有还，。 用去图书馆借书的例子理解， 借了，看完了，没还，别人也借不了。 然后我一直不还， 对图书馆来说，相当于书丢了，这就是“内存泄漏”。

开辟过程：

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700751553534-97d3af15-300d-47fa-aa7d-e83baeb81f9d.png" alt="" width="423">

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700779747695-e119a580-be53-4401-a615-fe0a6efcf414.png" alt="" width="819">

```c
// 将数组 arr 的内容复制到动态分配的内存中
    memcpy(ptr, arr, 10 * sizeof(int));

    // 打印动态分配内存中的内容
    for (int i = 0; i < 10; ++i) {
        printf("%d ", ptr[i]);
    }
    printf("\n");

    // 释放动态分配的内存
    free(ptr);
    ptr = NULL; // 将指针置为 NULL，避免悬空指针
```

这段代码的作用是演示了动态内存的使用过程：

1. 内存复制： 使用 `memcpy()` 函数将数组 `arr` 中的内容复制到了动态分配的内存空间中，这样就把静态数组 `arr` 中的数据复制到了动态分配的内存中的位置 `ptr`。
2. 打印动态分配内存中的内容： 通过循环遍历动态分配内存中的内容，并使用 `printf()` 函数输出每个元素的值。
3. 释放动态分配的内存： 最后使用 `free()` 函数释放了动态分配的内存。这一步非常重要，因为在不再需要动态分配内存时，应该及时释放它，以免造成内存泄漏。
4. 将指针置为 NULL： 将指针 `ptr` 置为 `NULL` 是一种良好的习惯，可以避免出现悬空指针。悬空指针是指已经释放的内存却没有将指针设置为 `NULL`，这样的指针可能会导致程序错误的操作。

这段代码是一个简单的示例，展示了动态内存分配、使用和释放的基本流程。在实际编程中，使用动态内存时需要确保适当地分配和释放内存，避免内存泄漏和悬空指针的问题。

生成随机字符串： 这个例子对malloc的运用就很到位

<https://cplusplus.com/reference/cstdlib/malloc/>

```c
/* malloc example: random string generator*/
#include <stdio.h>      /* printf, scanf, NULL */
#include <stdlib.h>     /* malloc, free, rand */

int main ()
{
  int i,n;
  char * buffer;

  printf ("How long do you want the string? ");
  scanf ("%d", &i);

  buffer = (char*) malloc (i+1);
  if (buffer==NULL) exit (1);

  for (n=0; n<i; n++)
    buffer[n]=rand()%26+'a';
  buffer[i]='\0';

  printf ("Random string: %s\n",buffer);
  free (buffer);

  return 0;
}
```

`rand()%26+'a'` 是一个生成随机小写字母的表达式。让我解释一下：

* `rand()` 函数返回一个介于 0 和 `RAND_MAX` 之间的随机整数值。`RAND_MAX` 是 `<stdlib.h>` 头文件中定义的一个常量，表示 `rand()` 函数能够生成的最大随机数值。
* `rand()%26` 使用取余操作符 `%` 将 `rand()` 生成的随机整数限制在 0 到 25 之间。因为 `% 26` 可以得到 0 到 25 的余数。
* `'a'` 是字符 'a' 的 ASCII 值。在 ASCII 表中，小写字母 'a' 对应的 ASCII 值为 97。

所以，`rand()%26+'a'` 的含义是：生成一个 0 到 25 的随机整数，然后加上 ASCII 值 97（即小写字母 'a' 的 ASCII 值），得到的结果是一个随机小写字母的 ASCII 值。这个值会被存储在 `buffer[n]` 中，实现了生成随机小写字母的目的。

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700780249757-c6e8648a-ed87-47a1-9727-f15d79c888cb.png" alt="" width="479">

这段代码是一个简单的示例，用于生成随机字符串。

1. `printf ("How long do you want the string? ");`: 打印提示消息，询问用户希望生成的随机字符串的长度。
2. `scanf ("%d", &i);`: 从用户输入中读取一个整数，存储到变量 `i` 中，表示用户期望生成的字符串长度。
3. `buffer = (char*) malloc (i+1);`: 使用 `malloc()` 函数分配了 `i+1` 大小的内存空间，存储字符的缓冲区。为了存储字符串结束符 `\0`，分配的大小比用户输入的长度多了一个字符位置。`buffer` 指针指向这块分配的内存空间。
4. `if (buffer==NULL) exit (1);`: 检查 `malloc()` 是否成功分配了内存。如果 `buffer` 是 `NULL`，意味着分配失败，程序将以错误码 1 退出。
5. `for (n=0; n<i; n++) buffer[n]=rand()%26+'a';`: 使用循环生成随机字符并存储在 `buffer` 指向的内存中。这里利用 `rand()` 函数生成一个 0 到 25 的随机数，加上字符 'a'，以便生成小写字母（ASCII 值从 97 到 122）。
6. `buffer[i]='\0';`: 在生成随机字符后，手动添加字符串结束符 `\0`，确保生成的内容构成一个以 `\0` 结尾的字符串。
7. `printf ("Random string: %s\n",buffer);`: 使用 `%s` 格式化符号将生成的随机字符串打印到控制台上。
8. `free (buffer);`: 使用 `free()` 函数释放了动态分配的内存，以免造成内存泄漏。

这个程序演示了如何动态分配内存，并在内存中生成随机字符串。在实际使用时，为了确保安全，应该考虑更多边界情况和错误处理，比如输入检查、内存分配失败时的应对等。

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700803892730-155348c3-dfe2-4a30-b144-2a346666a839.png" alt="" width="738">

## 7.2 calloc <a href="#yevyd" id="yevyd"></a>

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700837496797-4f3d09dd-e7bf-44af-b30e-e9058077ca86.png" alt="" width="728">

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700837679983-e9724f57-d05a-46e7-9cac-1946c1d075bf.png" alt="" width="379">

`sizeof(int)` 就是表示整型 4

`p==NULL`表示 是否是悬空指针， 有没有申请成功，失败就结束 。`return 1` 。

```c
#include <stdio.h>
#include <stdlib.h>

int main() {
    int n;
    int *ptr;

    printf("Enter the number of elements: ");
    scanf("%d", &n);

    // 使用 calloc 分配 n 个整数大小的内存空间
    ptr = (int *)calloc(n, sizeof(int));

    if (ptr == NULL) {
        printf("Memory allocation failed\n");
        return 1;
    }

    printf("Memory allocated successfully\n");

    // 打印分配的内存空间中的内容（此时应全为零）
    printf("Content of allocated memory:\n");
    for (int i = 0; i < n; ++i) {
        printf("%d ", ptr[i]);
    }
    printf("\n");

    // 释放动态分配的内存
    free(ptr);
    ptr = NULL;

    return 0;
}
```

这个示例演示了使用 calloc() 函数分配了 n 个整数大小的内存空间，然后打印了分配的内存中的内容。由于 calloc() 分配的内存会初始化为零，所以在打印内容之前，所有的元素都应该是零。

这段代码是一个基本的示例，展示了 calloc() 函数的用法。在实际使用时，可以根据需求修改分配的内存空间的类型和大小，并根据需要进行更复杂的操作。记得在不再需要动态分配的内存时，使用 free() 函数释放它，避免内存泄漏问题。

```
// 打印分配的内存空间中的内容（此时应全为零）
    printf("Content of allocated memory:\n");
    for (int i = 0; i < n; ++i) {
        printf("%d ", ptr[i]);
    }
    printf("\n");
```

让我解释下每行代码的作用：

1. printf("Content of allocated memory:\n");: 打印消息，说明即将输出分配的内存空间的内容。
2. for (int i = 0; i < n; ++i) { printf("%d ", ptr\[i]); }: 这个 for 循环遍历了动态分配的内存空间，依次输出每个元素的值。ptr\[i] 表示指针 ptr 指向的动态分配内存空间中第 i 个元素的值。
3. printf("\n");: 在 for 循环结束后，打印一个换行符，将输出的内容放在新的一行。

整个代码块的目的是，利用循环遍历打印了动态分配的内存空间中的内容，展示了 calloc() 函数初始化分配内存为零的特性。

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700839889447-49555a33-c471-4ed3-9b8a-efb8b79c415d.png" alt="" width="462">

calloc 函数的典型用法， 就这样记住就行。

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700839708804-003f0791-d358-4c0b-a209-47b49861205e.png" alt="" width="278">

```c
#include <stdio.h>
#include <string.h>

int main() {
    char str[20] = "Hello, World!";
    
    printf("Before memset: %s\n", str);

    // 将 str 的前 5 个字符（包括 '\0' 结尾符）设置为 '*'
    memset(str, '*', 5);

    printf("After memset: %s\n", str);

    return 0;
}
```

#### ![](https://cdn.nlark.com/yuque/0/2023/png/1171985/1700839914039-8a34d5ed-d4c3-4ddf-a4c4-b78983361ea5.png) <a href="#pefmg" id="pefmg"></a>

#### 注意事项： <a href="#nyscy" id="nyscy"></a>

* memset() 常用于将数组或其他内存块的一部分或全部内容设置为特定的值，比如清空数组、初始化某些数据结构等。
* 由于 value 参数是以整数形式给出的，因此将会被转换为 unsigned char 类型，所以可以用任何 unsigned char 类型表示的值来填充内存块，如 0、'A'、'\*' 等。
* 尽管 memset() 函数返回 void\* 类型的指针，但它实际上没有返回值。

## 7.3 realloc <a href="#gnonn" id="gnonn"></a>

#### realloc() 函数原型： <a href="#dvbif" id="dvbif"></a>

```
cCopy code
void *realloc(void *ptr, size_t size);
```

* ptr：指向之前由 malloc()、calloc() 或 realloc() 返回的内存块的指针。如果 ptr 是 NULL，则 realloc() 的行为类似于 malloc(size)。
* size：需要重新分配的内存块的新大小（以字节为单位）。

\ <br>

```c
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr;

    // 分配初始内存空间
    ptr = (int *)malloc(5 * sizeof(int));
    if (ptr == NULL) {
        printf("Memory allocation failed\n");
        return 1;
    }

    printf("Memory allocated successfully\n");

    // 扩大内存空间
    ptr = (int *)realloc(ptr, 10 * sizeof(int)); // 10 * sizeof(int) >> 增加 50字节
    if (ptr == NULL) {
        printf("Memory reallocation failed\n");
        return 1;
    }

    printf("Memory reallocated successfully\n");

    free(ptr); // 释放内存

    return 0;
}
```

当在代码中加入数组时，我们可以利用 `realloc()` 函数动态调整数组的大小。以下是一个示例，展示了如何动态增加数组的长度：

```
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr = NULL; // 初始化指针为 NULL，表示未分配内存

    int initialSize = 5;
    int newSize = 10;

    // 初始分配内存
    arr = (int *)malloc(initialSize * sizeof(int));
    if (arr == NULL) {
        printf("Memory allocation failed\n");
        return 1;
    }

    printf("Memory allocated successfully\n");

    // 假设在此之前已经对 arr 进行了赋值操作...

    // 重新分配更大的内存空间
    arr = (int *)realloc(arr, newSize * sizeof(int));
    if (arr == NULL) {
        printf("Memory reallocation failed\n");
        return 1;
    }

    printf("Memory reallocated successfully\n");

    // 使用新的内存空间...

    free(arr); // 释放内存

    return 0;
}
```

这个示例展示了如何在初始分配了一定大小的内存后，使用 `realloc()` 函数动态地调整数组的大小。在实际使用中，你可以根据需求动态改变数组的大小，使其适应不同的场景和需求。值得注意的是，`realloc()` 可能会涉及内存的重新分配和数据的拷贝，所以在使用时需要注意内存分配失败的情况，并确保操作前后正确管理内存。

`realloc` 在申请空间的时候，

情况一，覆盖到别人的空间： 情况一比较复杂

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700841317971-e1fb910d-82cf-451f-ad01-6281273ad70c.png" alt="" width="516">

怎么办？

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700841538080-3b30a1ee-ff8c-4207-864a-c8db75559efc.png" alt="" width="659">

找一块新的满足要求的空间， 第一步讲原来的值复制到新的空间去。 多的扩容的空间留着不用，返回的时候，直接返回新的地址。

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700841840285-2a2154c4-0a2f-4e07-8f06-792d988827aa.png" alt="" width="349">

可以看到这里，realloc 没有使用 p来进行接收，这就是因为，如果内存申请失败，p指向申请的这块地址的时候，申请失败，指不过来， 那天之前的地址，可能会“失忆”，有风险。

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700841995498-678a2e00-b31b-4107-829a-5941273a8f44.png" alt="" width="321">

这就ok了，做一个中间转换，

情况二，后面没人用，直接增加。情况二是简单的。

<img src="https://cdn.nlark.com/yuque/0/2023/png/1171985/1700841383640-db1d8eb5-ecf7-4439-bbbe-d727f3814c28.png" alt="" width="565">
