mirror of
https://github.com/Aexiar/c.git
synced 2024-10-22 14:05:45 +02:00
c
This commit is contained in:
parent
9e74cb5bf5
commit
46f65186a0
@ -2241,21 +2241,3 @@ int main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
# 第三章:共用体
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 第四章:typedef
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,28 +1,9 @@
|
||||
# 第一章:概述
|
||||
# 第一章:共用体
|
||||
|
||||
## 1.1 介绍
|
||||
|
||||
* [库函数](/C标准库参考手册.pdf)并非 C 语言本身的组成部分,而是 C 语言编译系统为方便用户使用而提供的公共函数。
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> 不同的编译系统提供的函数数量和函数名、函数功能都不尽相同, 使用时要小心,必要时需要查一下库函数手册。
|
||||
|
||||
* C 标准库的头文件,如下所示:
|
||||
|
||||
![](./assets/1.png)
|
||||
|
||||
## 1.2 常见的头文件和库函数
|
||||
|
||||
* 我们完全没必要记住所有的 C 语言头文件和库函数(也记不住),只需要熟悉常用的头文件和库函数。
|
||||
|
||||
| 头文件 | 功能说明 | 常用函数和宏 |
|
||||
| :------------ | :--------------------------------------------------------- | :----------------------------------------------------------- |
|
||||
| **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` |
|
||||
# 第二章:typedef(⭐)
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
@ -1,85 +1,28 @@
|
||||
`malloc` 是 C 语言中用于动态分配内存的函数,所分配的内存不会随着程序结束而自动释放,除非操作系统负责回收。这意味着,在程序运行期间,使用 `malloc` 分配的内存如果不手动释放(即使用 `free()` 函数释放),程序本身并不会自动回收这些内存。
|
||||
# 第一章:概述
|
||||
|
||||
### 具体情况如下:
|
||||
## 1.1 介绍
|
||||
|
||||
1. **程序运行期间:**
|
||||
在程序运行过程中,使用 `malloc` 分配的内存必须由程序显式地调用 `free()` 来释放。如果不这样做,就会造成**内存泄漏**,即这些内存永远不会再被程序使用,直到程序结束。
|
||||
* [库函数](/C标准库参考手册.pdf)并非 C 语言本身的组成部分,而是 C 语言编译系统为方便用户使用而提供的公共函数。
|
||||
|
||||
2. **程序结束后:**
|
||||
当程序完全结束后,操作系统通常会收回该程序所占用的所有资源,包括 `malloc` 分配的内存。因此,程序终止后,尽管没有调用 `free()`,操作系统仍然会回收这些动态分配的内存。
|
||||
> [!CAUTION]
|
||||
>
|
||||
> 不同的编译系统提供的函数数量和函数名、函数功能都不尽相同, 使用时要小心,必要时需要查一下库函数手册。
|
||||
|
||||
3. **为什么还是要手动释放?**
|
||||
尽管程序终止后内存会被操作系统回收,但如果你的程序是一个长时间运行的应用(如服务器、后台服务),持续的内存泄漏将会导致内存的不可用部分不断增多,最终导致系统内存耗尽。因此,为了保证程序的健壮性和高效性,在程序运行中及时手动释放内存是非常重要的。
|
||||
* C 标准库的头文件,如下所示:
|
||||
|
||||
### 例子:
|
||||
![](./assets/1.png)
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
## 1.2 常见的头文件和库函数
|
||||
|
||||
int main() {
|
||||
int *ptr = (int *)malloc(10 * sizeof(int)); // 动态分配内存
|
||||
* 我们完全没必要记住所有的 C 语言头文件和库函数(也记不住),只需要熟悉常用的头文件和库函数。
|
||||
|
||||
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` 分配内存后,在合适的时机释放这些内存,以保证程序的稳定性和高效性。
|
||||
| 头文件 | 功能说明 | 常用函数和宏 |
|
||||
| :------------ | :--------------------------------------------------------- | :----------------------------------------------------------- |
|
||||
| **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` |
|
@ -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` 分配内存后,在合适的时机释放这些内存,以保证程序的稳定性和高效性。
|
0
docs/notes/02_c-leap/11_xdx/index.md
Normal file
0
docs/notes/02_c-leap/11_xdx/index.md
Normal file
Loading…
Reference in New Issue
Block a user