首页 > 其他分享 >c语言学习笔记5

c语言学习笔记5

时间:2023-07-29 13:12:09浏览次数:41  
标签:文件 语言 函数 笔记 学习 内存 数组 分配 指针

内存空间
在C语言中,内存空间可以被划分为以下几个部分:
1. 栈(Stack):这部分内存由编译器自动分配和释放,用于存放函数的参数值,局部变量等。其操作方式类似于数据结构中的堆栈,先进后出。
2. 堆(Heap):堆是用于动态内存分配的。与栈不同,堆的分配和释放必须由程序员自己操作。在C语言中,使用malloc,calloc,realloc等函数进行分配,使用free函数进行释放。如果程序员不正确地管理堆,可能会导致内存泄漏或其他问题。
3. 静态/全局区:这部分内存存放全局变量和静态变量。全局变量的生命周期是整个程序的运行期间,而静态变量则是在函数或代码块执行完之后仍然存在,直到程序结束。
4. 常量区:如名字所示,这部分内存主要用于存储常量,例如字符串常量。
5. 代码区:这部分内存用于存储程序的二进制代码。

以下是这些内存区域的一种典型分布方式:
高地址
|------------------|
|                  |
|     堆(Heap)    |
|                  |
|------------------|
|                  |
|     未分配区域     |
|                  |
|------------------|
|                  |
|     栈(Stack)   |
|                  |
|------------------|
|                  |
|静态/全局区(Static/Global)|
|                  |
|------------------|
|常量区(Constants)  |
|------------------|
|  代码区(Code)     |
低地址
值得注意的是,栈和堆在内存中的增长方向是相反的:栈从高地址向低地址增长,而堆从低地址向高地址增长。当堆和栈的增长方向相遇时,会发生堆栈溢出错误。


C语言中内存分配主要有如下几种:
1.栈(stack):
- 由编译器自动分配释放
- 适合存储函数参数、局部变量等
- 生命周期与作用域相关
- 速度快,但空间有限
2.堆(heap):
- 由malloc/free等函数手动管理
- 适合存储需要长期存在的变量
- 生命周期由程序员决定
- 空间较多但速度相对较慢
3.静态存储区(static storage duration): 
- 在整个程序执行期间存在
- 生命周期是从程序开始到结束
- 适合存储需要在函数间共享的全局变量
4.常量区(constant region):
- 存储字符串和其他常量
- 在编译时分配,程序运行期间不改变
区别主要在:

- 空间:栈空间有限但速度快,堆空间多但相对慢;静态存储区和常量区的空间由常量和全局变量决定。

- 分配方式:栈由编译器自动分配,堆由malloc等函数手动申请;静态区和常量区在编译时分配。

- 生命周期:栈的变量在函数调用结束后消失;堆的变量由程序员决定;静态区和常量区存在整个程序执行期间。

总的来说,C语言提供了不同的内存分配方式,程序员可以根据实际需要灵活选择。它们各有优缺点,搭配使用可以提高效率。

动态内存分配

为什么存在动态内存分配
在C语言中存在动态内存分配主要是为了:
1. 实现可变长度的数组。C语言中的数组长度是在编译时固定的,如果需要实现可变长度的数组,就需要动态内存分配。
2. 解决内存不足。在编译时无法预测程序运行时需要的内存大小,有时需要根据运行时条件申请内存。
3.实现数据结构。C语言中的数组长度固定,如果需要实现一些数据结构如链表、队列、栈等,就需要使用动态内存分配。
具体的动态内存分配方式有:
- malloc():从heap空间中分配可用内存。
- calloc():与malloc()类似,但会将内存初始化为0。
- realloc():重新分配内存,可以增大或缩小已分配内存的大小。
- free():释放已分配的heap内存。
使用这些函数可以实现可变长度的数组、链表等数据结构,灵活地管理程序内存。

如:
int *arr = malloc(n * sizeof(int));  // 分配n个int类型的元素
arr[0] = 1;
arr[1] = 2;
...

free(arr);  // 释放内存
所以,总的来说,在C语言中存在动态内存分配是为了实现可变长度的数组、解决内存不足以及实现数据结构等目的。可以提供程序更多的灵活性。
动态内存函数的介绍

malloc

void* malloc (size_t size);
分配size字节 的内存块,返回指向块开头的指针。
新分配的内存块的内容未初始化,保留不确定的值。
如果大小为零,则返回值取决于特定的库实现(它可能是也可能不是空指针),但返回的指针不应被取消引用。
成功时,返回一个指向函数分配的内存块的指针。
该指针的类型始终为void*,可以将其转换为所需的数据指针类型以便可取消引用。
如果函数未能分配所请求的内存块,则返回空指针。


以下是malloc和free函数的使用示例:
#include <stdio.h> 
#include <stdlib.h>
int main() {
    int *p;    
    // 分配10个int大小的内存 
    p = (int *)malloc(10 * sizeof(int));    
    if (p == NULL) {
        printf("Error allocating memory\n");
        exit(1); 
    }   
    // 使用分配的内存 
    p[0] = 10; 
    p[1] = 20;   
    // 打印 
    printf("p[0] = %d p[1] = %d\n", p[0], p[1]);
    // 释放分配的内存
    free(p);    
    return 0;
}
malloc函数用于从堆内存中分配指定大小的内存。基本语法是:
ptr = malloc(size);`
这里ptr是一个指针变量,size是要分配的内存大小,单位是字节。
free函数用于释放使用malloc分配的内存。基本语法是:
free(ptr);
ptr是使用malloc分配的指针变量。
内存分配和释放是C语言中一个很重要的内容,malloc和free函数用于管理程序的动态内存。

free

void free (void* ptr);
如果ptr没有指向用上述函数分配的内存块,则会导致未定义的行为。
如果ptr是空指针,则该函数不执行任何操作。
请注意,此函数不会更改ptr本身的值,因此它仍然指向相同的(现在无效)位置。

calloc

void* calloc (size_t num, size_t size);
分配数组并将其清零初始化
为num 个元素的数组分配一块内存,每个元素的长度为size字节,并将其所有位初始化为零。
有效结果是分配零初始化的(num*size)字节内存块。
如果大小为零,则返回值取决于特定的库实现(它可能是也可能不是空指针),但返回的指针不应被取消引用。

成功时,返回一个指向函数分配的内存块的指针。
该指针的类型始终为void*,可以将其转换为所需的数据指针类型以便可取消引用。
如果函数未能分配所请求的内存块,则返回空指针。

realloc

void* realloc (void* ptr, size_t size);
重新分配内存块
更改ptr 指向的内存块的大小。
size调整之后新大小。
该函数可以将内存块移动到新位置(其地址由函数返回)。
即使该块被移动到新位置,内存块的内容也会保留到新大小和旧大小中较小的一个。如果新的大小更大,则新分配的部分的值是不确定的。
如果ptr是空指针,该函数的行为类似于分配内存,分配一个大小为字节的新块并返回指向其开头的指针。

指向重新分配的内存块的指针,可以与ptr 相同,也可以是新位置。
该指针的类型为void*,可以将其转换为所需的数据指针类型,以便可取消引用。


realloc函数有以下几点需要注意:
1. realloc可能会改变内存布局。因此需要把指向原内存块的指针更新为realloc返回的值
2. 如果realloc失败,原内存块仍然有效。
3. 如果size为0,realloc会释放内存块。
4. 如果ptr为NULL,realloc等价于malloc。
5. 建议使用临时变量保存realloc的返回值,不要直接使用result = realloc(ptr, size)这样的写法。
例如:
void *temp = realloc(ptr, size);
if(temp != NULL){ 
    ptr = temp;
}
6. 如果realloc失败,原内存块仍有效。应该检查realloc的返回值,并用原指针释放内存。
例如:
void *temp = realloc(ptr, size);
if(temp != NULL){
    ptr = temp;
}else{
    free(ptr);
    ptr = NULL;
}
所以总的来说,主要要检查realloc的返回值,更新指针,检查realloc失败后的处理。
常见的动态内存错误
C语言中常见的动态内存错误有:
1. 内存泄漏(Memory leak):因未正确释放动态分配的内存,导致内存无法回收。
2. 使用未初始化的指针:指向未知内存地址的悬空指针,可能导致非法访问。
3. 数组下标越界:数组下标超出数组大小,导致非法访问内存。
4. 野指针:已释放的内存指针,或指向不存在内存的指针。使用野指针可能导致非法访问。
5. 双重释放(Double free): 重复释放同一块内存,可能导致程序Crash。
6. 使用free的非heap内存: 不应使用free释放非malloc分配的内存,可能导致Crash。
7. 内存碎片(Memory fragmentation): 长时间未释放内存,造成可用内存块变小、不连续。
主要的解决方法是:
1. 使用完动态内存后尽快释放。
2. 释放内存前检查指针是否有效。
3. 分配完内存后初始化指针,释放内存后置空指针。
4. 及时检查malloc/realloc的返回值。
5. 利用工具如valgrind检查内存泄漏与非法访问。
总的来说,C语言中由于缺少自动内存管理,容易产生各种动态内存错误,需要开发者仔细检查和处理。
柔性数组
柔性数组(flexible array)是C99中引入的一个特性,它可以让结构体的最后一个元素是一个未知大小的数组。
具体来说,柔性数组有以下几个特点:
1. 柔性数组必须是结构体中的最后一个元素。
2. 柔性数组的类型是未完全定义的,通常声明为元素类型后跟一个空方括号,如int arr[]。
3. sizeof返回的结构体大小不包括柔性数组的大小。
4. 分配结构体变量时,必须手动为柔性数组额外分配空间。
5. 通过结构体指针可以访问柔性数组元素。
示例:
struct s {
  int size;
  int arr[]; // flexible array 
};
struct s *ps = malloc(sizeof(struct s) + 10 * sizeof(int)); // 额外分配数组空间
ps->size = 10; 
ps->arr[0] = 1; // 访问柔性数组元素
柔性数组的好处是可以让结构体直接管理可变大小的数组,不需要额外的指针,使用更方便。但需要注意手动分配和释放内存。


柔性数组(flexible array)是C99中新增的一个特性,它允许在结构体的最后一个成员为未知大小的数组,这样可以很方便地定义可变长度的结构体。
柔性数组的主要优点有:
- 节省空间。与定义固定大小数组相比,柔性数组只会根据实际需要动态分配内存,避免内存浪费。
- 编程方便。可以直接通过结构体指针访问柔性数组成员,而不需要单独为数组另外分配内存。
- 兼容旧代码。与传统的结构体定义兼容,只是在最后添加了柔性数组。
- 可读性好。直接通过结构体定义就能表明这是一个可变长度的结构体。
- 减少指针运算。访问柔性数组元素时不需要计算偏移量。
- 接口一致。柔性数组的长度无需特殊处理,访问方法与普通数组一致。
总之,柔性数组很好地解决了在结构体中如何定义可变长度数组的问题,增加了结构体的灵活性,非常适合用于描述长度不固定的数据结构。

文件操作

什么是文件
硬盘上的文件就是文件。
但在程序设计中,我们一般谈的文件有两种:程序文件,数据文件。
程序文件:包括源程序文件(后缀为.c),目标文件(windows环境后缀.obj),可执行程序(windows环境后缀.exe)
数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
在以前各章所处理的数据输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。
文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如:c:\code\test.txt
问了方便起见,文件标识常被称为文件名。
文件类型
根据数据的组织形式,数据文件被称为文本文件或二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换,以ASCII字符的形式存储的文件就是文本文件。
字符一律用ASCII码的形式存储,数值型数据既可以用ASCII码的形式存储,也可以用二进制形式存储。
文件缓冲区
在C语言中,文件操作时会用到文件缓冲区。
主要有以下几点:
1. 每次读取或写入文件时,实际上是和文件缓冲区交互,文件缓冲区存在内存中。
2. 文件缓冲区默认大小为 8KB(8192 字节),当缓冲区填满时,会自动将缓冲区内容写到磁盘文件。
3. 通过fflush()或fclose()可以强制将文件缓冲区内容写入磁盘文件。
4. 设置缓冲区大小:
   - setvbuf(FILE *stream, char *buf, int mode, size_t size);
   - mode: _IOFBF (完全缓冲)、 _IOLBF (行缓冲)、_IONBF(不使用缓冲)

举个例子:
#include <stdio.h>
int main() {
   FILE *fp;
   fp = fopen("test.txt", "w");
   setvbuf(fp, NULL, _IOFBF, 1024); // 设置为1KB缓冲区 
   fprintf(fp, "Hello");
   // 此时并不会立即写入文件,而是存入缓冲区
   fflush(fp);          // 强制将缓冲区内容写入文件  
   fclose(fp);
}
文件指针
C语言中,文件指针(file pointer)用来标识一个已打开的文件。
主要特点如下:
- 每个文件指针都是一个FILE类型的指针。
- 使用fopen()函数打开文件后会返回一个文件指针,用于后续读取/写入该文件。
- 使用文件指针调用文件操作函数,如fread()、 fwrite()、fgets()、fputs() 等。
- 使用fclose()关闭文件指针。
- 标准输入/输出/错误也被视为文件,对应的文件指针是:

stdin  - 标准输入(通常是键盘)
stdout - 标准输出(通常是屏幕)
stderr -  标准错误输出(通常也是屏幕)


例子:
#include <stdio.h>
int main() {
   FILE *fp;  
   fp = fopen("test.txt", "r");  // 打开一个文件,返回其文件指针
   fscanf(fp, "%d", &n);         // 使用文件指针读取文件
   fclose(fp);                   // 关闭文件指针    
}

主要文件操作函数:
- fopen() - 打开文件 
- fclose() - 关闭文件
- fread() -  从文件中读取
- fwrite() - 写入文件
- fscanf() - 与fprintf()类似,用于文件输入
- fprintf() - 用于文件输出
文件的打开和关闭
C语言中,文件主要使用fopen()和fclose()来打开和关闭。
打开文件的方法是:
FILE *fopen(const char *filename, const char *mode);

- filename - 要打开的文件的名称
- mode - 打开模式(如 "r" 只读,"w" 写入,"a" 追加等)

fopen()成功会返回一个FILE类型的文件指针,否则返回NULL。

使用完文件后需要关闭,关闭文件的方法是:
int fclose(FILE *fp);


- fp - 要关闭的文件的文件指针
fclose()成功会返回0,失败则返回EOF。
完整的打开和关闭文件的代码是:
FILE *fp;
fp = fopen("filename.txt", "r");
if (fp == NULL) {
    printf("Cannot open file\n");
    return 0; 
}
// 读/写文件 ...
fclose(fp);

除了直接关闭外,还可以使用文件缓冲区来提高效率,程序结束时自动关闭:
FILE *fp = fopen("filename.txt", "r");
// 读/写文件 ...
// 程序结束时自动释放fp
希望能帮助到您!具体可参考stdio.h头文件了解相关函数。


fopen()函数的模式字符串主要有以下几种:
1. r - 以只读方式打开文件。文件指针将会放在文件的开头。这是默认模式。
2. r+ - 打开一个文件用于读写。文件指针将会放在文件的开头。
3. w - 打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 
4. w+ - 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
5. a - 打开一个文件用于追加。文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于追加。
6. a+ - 打开一个文件用于读写。文件指针将会放在文件的结尾。如果该文件不存在,创建新文件。
7. b - 二进制模式。加上这个标志,文件将以二进制模式打开。这是默认模式。
8. t - 文本模式。加上这个标志,文件将以文本模式打开。
这些模式可以组合使用。例如:"w+b" 表示:以二进制读写模式打开文件。如果该文件不存在则创建。
总的来说,主要分为以下几种:
- 只读:r 
- 读写:r+, w+ ,a+
- 只写:w, a
- 二进制:b(默认)
- 文本:t
希望能提供参考!详细可参考stdio.h头文件介绍。
文件的顺序读写

文件

scanf/printf	是针对标准输入流/标准输出流的 格式化输入输出语句
fscanf/fprintf	是针对所有输入流的/所有输出流的 格式化输入输出语句
sscanf/sprintf	sscanf是从字符串中读取格式化的数据/sprintf是把格式化数据输出成(存储到)字符串
文件结束的判定
牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。而是应用于当文件读取结束的时候,判断是读取失败结果,还是遇到文件尾结束。
1文本文件读取是否结束,判断返回值是否为EOF(fgetc)或则NULL(fgets)
例如:fgetc判断是否为EOF
	fgets判断返回值是否为NULL
2二进制文件的读取结果判断,判断返回值是否小于实际要读的个数。
例如:fread判断返回值是否小于实际要读的个数。

程序环境和预处理

程序的翻译环境执行环境
在ANSIC的任何一种实现中,存在两个不同的环境。
第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。第二种是执行环境,它用于实际执行代码。
预定义符号介绍
C语言中主要的预定义符号有:

1. __LINE__ : 表示当前行号,用于帮助调试。

2. __FILE__ : 表示当前输入的文件名称,用于调试。

3. __DATE__ : 表示代码编译的日期。

4. __TIME__ : 表示代码编译的时间。

5. NULL : 定义为0,表示空指针常量。

6. true/false : 分别定义为1和0,表示布尔常量。

7. sizeof : 返回类型或表达式所占用的存储空间(以字节为单位)。

这些预定义符号的主要作用是:
1. 方便调试:如__LINE__, __FILE__可以告知当前错误所在的文件和行号。
2. 提供常量:如NULL, true/false等常见常量。
3. 计算类型大小:sizeof可以用于计算不同类型的大小。
4. 记录编译时间:__DATE__ 和 __TIME__可以记录代码编译的日期和时间。
5. 符合C标准:__STDC__ 表示是否符合C标准。
总的来说,这些预定义符号为C语言提供了更多有效的功能和信息,方便了程序的编写和调试。
预处理指令#define
#define在C语言中是一个预处理指令,它用来定义符号常量。

语法:#define 标识符 替换值

例如:
#define PI 3.14
#define WIDTH 80


在后续代码中可以这样使用:
float area = PI * radius * radius;
printf("The width is: %d", WIDTH);
在预处理阶段,#define指令会用替换值替换标识符,最终的代码会变成:
float area = 3.14 * radius * radius;  
printf("The width is: 80");

#define定义的常量具有以下特点:
1. 常量名称不占用内存空间
2. 常量名称替换后的值在整个代码中是一致的
3. 常量名称在预处理阶段被替换,编译时只剩下替换值
4. 常量名称建议使用全部大写
5. 常量名称相当于文本替换,不占用内存

#define的主要用途是:

1. 定义常量,替换 tedius 的魔数
2. 提高代码的可读性和可维护性
3. 方便修改,只需要修改#define就可以全局生效

所以,#define是C语言中一个非常有用的预处理指令。


在C语言中,#define可以定义两种类型的宏:
1. 参数宏
2. 普通宏
### 普通宏
语法:#define 标识符 替换值
示例:
#define PI 3.14
#define MAX 100
这种宏在使用时,完整的标识符会被其替换值直接替换。
### 参数宏

语法:#define 标识符(参数列表) 替换值
示例:
#define square(x)  x*x
#define sum(x,y) x+y


这种宏可以带参数,在使用时会替换参数。
调用参数宏:
int a = 3;
int b = 4;
int c = square(a); // c becomes 9
int d = sum(a, b); // d becomes 7


总之:
- 普通宏只是简单文本替换,其替换值在定义时就是固定的。
- 参数宏具有更强大的功能,可以根据调用时传入的不同参数产生不同的结果。
参数宏更加适合于定义通用性较强的函数。

但是宏存在缺点:
- 缺乏语法校验,定义或调用时可能出错,不会得到编译期报错
- 代码不易调试
- 参数出现多次会多次展开
所以还是尽量使用函数,只有在性能要求很高时才使用宏。
宏和函数的对比
C 语言中的宏和函数有以下主要对比:
1. 宏利用文本替换实现,函数需要编译后生成机器码运行。因此宏比函数具有更高效率。
2. 宏的参数直接作用于被替换后的文本,不进行类型检查,函数的参数进行Type Checking。 所以宏出错时比较难排查。
3. 宏无法传递参数,因为直接替换文本。函数可以有参数,并支持默认参数。
4. 宏无法定义局部变量。函数可以定义局部变量。
5. 宏不支持递归调用。函数支持递归调用。
6. 宏只能完成宏替换规则中能实现的功能。函数更加通用。
7. 宏在预编译阶段被替换,函数在编译阶段生成机器码。
所以总的来说:
- 如果需要高效率,优先考虑宏。
- 如果需要类型安全、局部变量等特性,优先考虑函数。
- 如果存在复杂逻辑,尽量使用函数,避免宏。
宏是由预处理器处理的,而函数需要编译器才能生成。所以习惯上说,尽量使用函数代替宏,除非真的需要高效率。

标签:文件,语言,函数,笔记,学习,内存,数组,分配,指针
From: https://www.cnblogs.com/zhangyu520/p/17589659.html

相关文章

  • day3c++学习
    1内存分区模型C++程序在执行时,将内存大方向划分为4个区域代码区:存放函数体的二进制代码,由操作系统进行管理的全局区:存放全局变量和静态变量以及常量栈区:由编译器自动分配释放,存放函数的参数值,局部变量等堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收......
  • 线性基学习笔记
    线性基简介线性基是一种擅长处理异或问题的数据结构.设值域为\([1,N]\),就可以用一个长度为$⌈\log_2{N}⌉$的数组来描述一个线性基。特别地,线性基第\(i\)位上的数二进制下最高位也为第\(i\)位。一个线性基满足,对于它所表示的所有数的集合\(S\),\(S\)中任意多个数异或所得的......
  • C++ Primer Plus学习笔记
    仅限main函数,如果没有返回语句,编译器会加隐含的返回语句:return0;WIN1064位系统中,sizeof(int)==sizeof(long)==4.C++17之后,新增byte数据类型,在标头<cstddef>中定义,取值范围[0-255],初始化:std::byteb{42};char取值范围[-128,127]原始字符串R"(string)"R"+*(......
  • 解决(几乎)任何机器学习问题(1、建立你的工作环境)
    原作者:AbhishekThakur原文:GitHub-abhishekkrthakur/approachingalmost:Approaching(Almost)AnyMachineLearningProblem1、建立你的工作环境在我们开始编码之前,在你的机器上设置好一切是非常重要的。在本书中,我们将使用Ubuntu18.04和Python3.7.6。如果你是Win......
  • Meta-Transformer 多模态学习的统一框架
    Meta-Transformer是一个用于多模态学习的新框架,用来处理和关联来自多种模态的信息,如自然语言、图像、点云、音频、视频、时间序列和表格数据,虽然各种数据之间存在固有的差距,但是Meta-Transformer利用冻结编码器从共享标记空间的输入数据中提取高级语义特征,不需要配对的多模态训练......
  • 【Linux】Kali Linux 安全学习笔记(1) - Docker Kali 部署与安装软件
    由于最近要做安全方面的工作,经网友们的推荐选定了kalilinux作为实施平台。但vm直装的方式太过麻烦了,本次kalilinux将采用docker镜像的方式进行部署使用。直接使用run运行命令启动rolling镜像,若镜像不存在,docker会自动进行checkout到本地,如下图:dockerrun-itkal......
  • openGauss学习笔记-24 openGauss 简单数据管理-模式匹配操作符
    openGauss学习笔记-24openGauss简单数据管理-模式匹配操作符数据库提供了三种独立的实现模式匹配的方法:SQLLIKE操作符、SIMILARTO操作符和POSIX-风格的正则表达式。除了这些基本的操作符外,还有一些函数可用于提取或替换匹配子串并在匹配位置分离一个串。24.1LIKE描述:判断字......
  • 『学习笔记』fhq-treap
    啥是平衡树这边建议去这里。分裂一般指的是按值分裂,意思就是以树上的BST(二叉搜索树)的值为关键值分裂。一般会给一个关键值\(val\),把一棵平衡树分裂成BST值\(\leqval\)和\(>val\)的两部分。主要思想从根开始递归,递归到某一节点,如果当前根节点的BST值小于等于你给的那......
  • 【笔记】构造题
    听说多做构造题长脑子,至少能让我从机械性的考试里清醒一点吧递归子问题剔除问题边缘例题......
  • Unity学习
    Unity学习1常用快捷键alt+鼠标左键:以某个物体为中心旋转视角鼠标左键+w/s/a/d:视角移动F:相机聚焦物体Q/W/E/R/T/Y:左上角工具栏工具2文件资源2.1工程目录Assets目录:主要存放资源文件,该文件中的内容会在unity项目栏中显示。2.2文件类型FBX文件:3D模型文件,其中包括了......