c
Some checks are pending
Deploy / build (push) Waiting to run
Deploy / Deploy (push) Blocked by required conditions

This commit is contained in:
许大仙 2024-10-11 16:40:12 +08:00
parent 9e74cb5bf5
commit 46f65186a0
6 changed files with 107 additions and 116 deletions

View File

@ -2241,21 +2241,3 @@ int main() {
} }
``` ```
# 第三章:共用体
# 第四章typedef

View File

@ -1,28 +1,9 @@
# 第一章:概述 # 第一章:共用体
## 1.1 介绍
* [库函数](/C标准库参考手册.pdf)并非 C 语言本身的组成部分,而是 C 语言编译系统为方便用户使用而提供的公共函数。
> [!CAUTION]
>
> 不同的编译系统提供的函数数量和函数名、函数功能都不尽相同, 使用时要小心,必要时需要查一下库函数手册。
* C 标准库的头文件,如下所示:
![](./assets/1.png)
## 1.2 常见的头文件和库函数
* 我们完全没必要记住所有的 C 语言头文件和库函数(也记不住),只需要熟悉常用的头文件和库函数。 # 第二章typedef
| 头文件 | 功能说明 | 常用函数和宏 |
| :------------ | :--------------------------------------------------------- | :----------------------------------------------------------- |
| **stdio.h** | 标准输入输出库 | `printf` `scanf` `fprintf``fscanf``fopen` `fclose``fgets` `fputs` |
| **stdlib.h** | 标准库,提供内存分配、程序控制、类型转换、随机数生成等功能 | `malloc` `free` `exit` `atoi` `atof``rand``srand` |
| **string.h** | 字符串处理库 | `strlen` `strcpy` `strncpy` `strcat` `strcmp``strstr` `memset` `memcpy` |
| **math.h** | 数学库 | `sin` `cos` `tan` `exp` `log` `sqrt` `pow` |
| **time.h** | 时间和日期库 | `time` `clock` `difftime` `mktime` `strftime` `localtime``gmtime` |
| **ctype.h** | 字符处理库 | `isalnum` `isalpha` `isdigit` `islower` `isupper` `tolower` `toupper` |
| **stdbool.h** | 布尔类型库 | `bool` `true` `false` |
| **assert.h** | 断言库 | `assert` |

View File

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -1,85 +1,28 @@
`malloc` 是 C 语言中用于动态分配内存的函数,所分配的内存不会随着程序结束而自动释放,除非操作系统负责回收。这意味着,在程序运行期间,使用 `malloc` 分配的内存如果不手动释放(即使用 `free()` 函数释放),程序本身并不会自动回收这些内存。 # 第一章:概述
### 具体情况如下: ## 1.1 介绍
1. **程序运行期间:** * [库函数](/C标准库参考手册.pdf)并非 C 语言本身的组成部分,而是 C 语言编译系统为方便用户使用而提供的公共函数。
在程序运行过程中,使用 `malloc` 分配的内存必须由程序显式地调用 `free()` 来释放。如果不这样做,就会造成**内存泄漏**,即这些内存永远不会再被程序使用,直到程序结束。
2. **程序结束后:** > [!CAUTION]
当程序完全结束后,操作系统通常会收回该程序所占用的所有资源,包括 `malloc` 分配的内存。因此,程序终止后,尽管没有调用 `free()`,操作系统仍然会回收这些动态分配的内存。 >
> 不同的编译系统提供的函数数量和函数名、函数功能都不尽相同, 使用时要小心,必要时需要查一下库函数手册。
3. **为什么还是要手动释放?** * C 标准库的头文件,如下所示:
尽管程序终止后内存会被操作系统回收,但如果你的程序是一个长时间运行的应用(如服务器、后台服务),持续的内存泄漏将会导致内存的不可用部分不断增多,最终导致系统内存耗尽。因此,为了保证程序的健壮性和高效性,在程序运行中及时手动释放内存是非常重要的。
### 例子: ![](./assets/1.png)
```c ## 1.2 常见的头文件和库函数
#include <stdio.h>
#include <stdlib.h>
int main() { * 我们完全没必要记住所有的 C 语言头文件和库函数(也记不住),只需要熟悉常用的头文件和库函数。
int *ptr = (int *)malloc(10 * sizeof(int)); // 动态分配内存
if (ptr == NULL) { | 头文件 | 功能说明 | 常用函数和宏 |
printf("内存分配失败\n"); | :------------ | :--------------------------------------------------------- | :----------------------------------------------------------- |
return 1; | **stdio.h** | 标准输入输出库 | `printf` `scanf` `fprintf``fscanf``fopen` `fclose``fgets` `fputs` |
} | **stdlib.h** | 标准库,提供内存分配、程序控制、类型转换、随机数生成等功能 | `malloc` `free` `exit` `atoi` `atof``rand``srand` |
| **string.h** | 字符串处理库 | `strlen` `strcpy` `strncpy` `strcat` `strcmp``strstr` `memset` `memcpy` |
// 使用分配的内存 | **math.h** | 数学库 | `sin` `cos` `tan` `exp` `log` `sqrt` `pow` |
for (int i = 0; i < 10; i++) { | **time.h** | 时间和日期库 | `time` `clock` `difftime` `mktime` `strftime` `localtime``gmtime` |
ptr[i] = i; | **ctype.h** | 字符处理库 | `isalnum` `isalpha` `isdigit` `islower` `isupper` `tolower` `toupper` |
printf("%d ", ptr[i]); | **stdbool.h** | 布尔类型库 | `bool` `true` `false` |
} | **assert.h** | 断言库 | `assert` |
printf("\n");
free(ptr); // 手动释放内存
return 0;
}
```
在这个例子中,`malloc` 分配了 10 个整数的内存,并在最后通过 `free()` 释放了这部分内存。如果没有调用 `free()`,那么在程序运行期间这部分内存将无法被重新使用,导致内存泄漏,但程序终止后,操作系统还是会回收它。
总结来说,尽管操作系统会在程序结束时回收内存,但在程序运行过程中,我们仍然需要显式地释放通过 `malloc` 分配的内存以防止内存泄漏。
是的,尽管操作系统会在程序终止时回收程序所占用的内存资源,**仍然建议手动释放通过 `malloc` 分配的内存**。有几个原因:
1. **良好的编程习惯**:及时释放动态分配的内存有助于养成良好的编程习惯。内存泄漏虽然在程序终止时会被操作系统回收,但在长时间运行的程序或嵌入式系统中,内存泄漏会逐渐耗尽可用内存,导致程序运行效率低下,甚至崩溃。
2. **动态库和插件环境**:如果你编写的是动态库或插件,这些代码可能在应用程序的生命周期内被多次加载和卸载。如果不手动释放内存,内存泄漏可能累积,导致更严重的问题。
3. **避免误用或未及时释放**:即使操作系统会回收内存,如果程序运行中频繁分配而不释放内存,可能会导致内存使用量过大,甚至超出系统资源,特别是在长时间运行的服务或应用中。这会使程序在运行过程中性能下降或出现异常。
4. **跨平台行为差异**:虽然大多数现代操作系统会在程序退出时自动回收内存,但在某些嵌入式系统或资源有限的环境中,可能不会有这样的机制。因此,手动释放内存是一种更为安全的选择。
总结来说,尽管操作系统最终会释放程序终止时的内存,手动释放动态分配的内存仍然是一个良好的编程实践,能够提高代码的健壮性和可维护性。
在 C 语言中,使用 `malloc` 动态分配内存后,需要使用 `free` 来释放内存,主要原因有以下几点:
### 1. **避免内存泄漏**
当使用 `malloc` 动态分配内存时,系统为程序分配了一块指定大小的内存,而这块内存不会在使用完毕后自动释放。只有通过调用 `free` 函数,操作系统才能将这块内存标记为可重新使用。如果不调用 `free`,内存将一直被程序占用,直到程序结束,这会导致**内存泄漏**。
内存泄漏的危害在于:
- **耗尽系统内存**:程序长期运行而不释放内存会逐渐消耗系统内存资源,最终可能导致系统或程序崩溃。
- **性能下降**:随着内存泄漏积累,系统内存资源减少,导致程序运行速度变慢或出现资源不足问题。
### 2. **提高程序的内存管理效率**
手动管理内存允许程序员根据需要灵活地分配和释放内存资源。通过及时释放不再使用的内存,可以有效地提高程序的内存利用效率,使系统能够将这些内存重新分配给其他任务。这对于长期运行的程序,尤其是服务器或后台进程,尤为重要。
### 3. **避免程序异常行为**
如果不及时释放内存,不仅会导致内存泄漏,还可能导致程序出现意外行为,例如:
- **内存碎片化**:当分配和释放内存的顺序不一致时,内存会变得零碎化,剩余的内存虽然总量足够,但可能无法满足某次较大的分配请求,从而导致分配失败。
- **内存访问问题**:未释放的内存在后续程序运行中可能仍被访问,如果程序继续使用这块已过期的内存,可能会出现不可预知的错误或崩溃。
### 4. **符合动态内存分配的对称性原则**
`malloc``free` 是一对相互配合的函数。`malloc` 用于申请内存,而 `free` 则用于归还这块内存。这种对称性使得内存管理变得可控和可维护,符合编程中资源管理的基本原则:**谁申请资源,谁负责释放资源**。
### 5. **防止其他潜在的内存管理问题**
- **悬空指针Dangling Pointer**:如果没有正确使用 `free` 释放内存,并继续使用释放前的指针,可能会导致悬空指针问题。调用 `free` 后通常也应将指针置为 `NULL`,以避免无效的指针引用。
- **内存重用问题**:在不释放内存的情况下重新分配内存,可能导致内存重叠或不可预期的行为。
### 总结
通过 `free` 释放动态分配的内存,可以避免内存泄漏、提高内存管理效率、减少异常行为以及确保程序的健壮性。总的来说,良好的内存管理实践要求在使用 `malloc` 分配内存后,在合适的时机释放这些内存,以保证程序的稳定性和高效性。

View File

@ -0,0 +1,85 @@
`malloc` 是 C 语言中用于动态分配内存的函数,所分配的内存不会随着程序结束而自动释放,除非操作系统负责回收。这意味着,在程序运行期间,使用 `malloc` 分配的内存如果不手动释放(即使用 `free()` 函数释放),程序本身并不会自动回收这些内存。
### 具体情况如下:
1. **程序运行期间:**
在程序运行过程中,使用 `malloc` 分配的内存必须由程序显式地调用 `free()` 来释放。如果不这样做,就会造成**内存泄漏**,即这些内存永远不会再被程序使用,直到程序结束。
2. **程序结束后:**
当程序完全结束后,操作系统通常会收回该程序所占用的所有资源,包括 `malloc` 分配的内存。因此,程序终止后,尽管没有调用 `free()`,操作系统仍然会回收这些动态分配的内存。
3. **为什么还是要手动释放?**
尽管程序终止后内存会被操作系统回收,但如果你的程序是一个长时间运行的应用(如服务器、后台服务),持续的内存泄漏将会导致内存的不可用部分不断增多,最终导致系统内存耗尽。因此,为了保证程序的健壮性和高效性,在程序运行中及时手动释放内存是非常重要的。
### 例子:
```c
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(10 * sizeof(int)); // 动态分配内存
if (ptr == NULL) {
printf("内存分配失败\n");
return 1;
}
// 使用分配的内存
for (int i = 0; i < 10; i++) {
ptr[i] = i;
printf("%d ", ptr[i]);
}
printf("\n");
free(ptr); // 手动释放内存
return 0;
}
```
在这个例子中,`malloc` 分配了 10 个整数的内存,并在最后通过 `free()` 释放了这部分内存。如果没有调用 `free()`,那么在程序运行期间这部分内存将无法被重新使用,导致内存泄漏,但程序终止后,操作系统还是会回收它。
总结来说,尽管操作系统会在程序结束时回收内存,但在程序运行过程中,我们仍然需要显式地释放通过 `malloc` 分配的内存以防止内存泄漏。
是的,尽管操作系统会在程序终止时回收程序所占用的内存资源,**仍然建议手动释放通过 `malloc` 分配的内存**。有几个原因:
1. **良好的编程习惯**:及时释放动态分配的内存有助于养成良好的编程习惯。内存泄漏虽然在程序终止时会被操作系统回收,但在长时间运行的程序或嵌入式系统中,内存泄漏会逐渐耗尽可用内存,导致程序运行效率低下,甚至崩溃。
2. **动态库和插件环境**:如果你编写的是动态库或插件,这些代码可能在应用程序的生命周期内被多次加载和卸载。如果不手动释放内存,内存泄漏可能累积,导致更严重的问题。
3. **避免误用或未及时释放**:即使操作系统会回收内存,如果程序运行中频繁分配而不释放内存,可能会导致内存使用量过大,甚至超出系统资源,特别是在长时间运行的服务或应用中。这会使程序在运行过程中性能下降或出现异常。
4. **跨平台行为差异**:虽然大多数现代操作系统会在程序退出时自动回收内存,但在某些嵌入式系统或资源有限的环境中,可能不会有这样的机制。因此,手动释放内存是一种更为安全的选择。
总结来说,尽管操作系统最终会释放程序终止时的内存,手动释放动态分配的内存仍然是一个良好的编程实践,能够提高代码的健壮性和可维护性。
在 C 语言中,使用 `malloc` 动态分配内存后,需要使用 `free` 来释放内存,主要原因有以下几点:
### 1. **避免内存泄漏**
当使用 `malloc` 动态分配内存时,系统为程序分配了一块指定大小的内存,而这块内存不会在使用完毕后自动释放。只有通过调用 `free` 函数,操作系统才能将这块内存标记为可重新使用。如果不调用 `free`,内存将一直被程序占用,直到程序结束,这会导致**内存泄漏**。
内存泄漏的危害在于:
- **耗尽系统内存**:程序长期运行而不释放内存会逐渐消耗系统内存资源,最终可能导致系统或程序崩溃。
- **性能下降**:随着内存泄漏积累,系统内存资源减少,导致程序运行速度变慢或出现资源不足问题。
### 2. **提高程序的内存管理效率**
手动管理内存允许程序员根据需要灵活地分配和释放内存资源。通过及时释放不再使用的内存,可以有效地提高程序的内存利用效率,使系统能够将这些内存重新分配给其他任务。这对于长期运行的程序,尤其是服务器或后台进程,尤为重要。
### 3. **避免程序异常行为**
如果不及时释放内存,不仅会导致内存泄漏,还可能导致程序出现意外行为,例如:
- **内存碎片化**:当分配和释放内存的顺序不一致时,内存会变得零碎化,剩余的内存虽然总量足够,但可能无法满足某次较大的分配请求,从而导致分配失败。
- **内存访问问题**:未释放的内存在后续程序运行中可能仍被访问,如果程序继续使用这块已过期的内存,可能会出现不可预知的错误或崩溃。
### 4. **符合动态内存分配的对称性原则**
`malloc``free` 是一对相互配合的函数。`malloc` 用于申请内存,而 `free` 则用于归还这块内存。这种对称性使得内存管理变得可控和可维护,符合编程中资源管理的基本原则:**谁申请资源,谁负责释放资源**。
### 5. **防止其他潜在的内存管理问题**
- **悬空指针Dangling Pointer**:如果没有正确使用 `free` 释放内存,并继续使用释放前的指针,可能会导致悬空指针问题。调用 `free` 后通常也应将指针置为 `NULL`,以避免无效的指针引用。
- **内存重用问题**:在不释放内存的情况下重新分配内存,可能导致内存重叠或不可预期的行为。
### 总结
通过 `free` 释放动态分配的内存,可以避免内存泄漏、提高内存管理效率、减少异常行为以及确保程序的健壮性。总的来说,良好的内存管理实践要求在使用 `malloc` 分配内存后,在合适的时机释放这些内存,以保证程序的稳定性和高效性。

View File