include guards

防止 .h 文件重复编译两次。

使用

#ifndef EGG_H // 就是if not define 
#define EGG_H // 接上句,如果没有定义,就定义,如果定义了就跳过这句
 
#endif // 直到这一句

通过这三条预处理器指令,确保头文件中的内容只编译一次。

更简单的方式

现代编译器使用更为简单的方式,在头文件第一行写上:

#pragma once // 这个头文件只包含一次

函数定义与函数原型

所谓函数原型,就是在 .h 这种文件中定义的,后面是 ; 而非 {} 的函数。

而函数定义就是我们日常写的一些方法了。

简要地说,函数原型不关注如何实现一个函数的,它只是说明这个函数需要什么,返回什么。

而函数定义就关注这个函数是如何实现的。

函数原型出现的原因

解决编译顺序问题。

也就是向前声明

c 语言的内存管理

主要就用到三个方法,mallocreallocfree

malloc

分配内存。

int *arr = (int*)malloc(10 * sizeof(int));
// malloc需要根据参数开辟空间,以字节为单位的,所有需要用到sizeof,这个返回类型需要的字节数量,然后malloc返回的是一个指针
// 这就需要转换了,把这个指针变成int类型的指针,前面就加上int*

realloc

重新分配内存。

// 先分配一个小数组
int *arr = (int*)malloc(5 * sizeof(int));
// 需要更多空间!
int *new_arr = (int*)realloc(arr, 10 * sizeof(int));
// 原本内存不够了,就分配一个新的内存,接受两个参数,第一个是原本的指针,第二个是新开辟的空间的大小
// 第二个参数是扩展到的位置,而不是原本的增加了,不是append,而是new capacity

free

有 malloc,就要有 free,分配了内存就要关闭。

int *ptr = (int*)malloc(5 * sizeof(int));
 
free(ptr);
ptr = NULL // 好习惯,释放完后将指针设置为NULL,避免误用

内存泄露

如果分配了空间,但是没有 free 就会造成内存泄露。

这个内存指的就是 ram,虽说 ram 断点就丢失,并且当程序完全退出后,操作系统会收回该程序占用的所有内存空间。

但是问题会出在程序运行期间。

长期运行的程序可能会因为内存耗尽而关闭。

其实 win 10 就有这个问题,win 10 的桌面窗口管理器就有内存泄漏问题,运行时间久了,可能占 10 几个 G 的 ram,电脑变得非常卡。

struct

只能包含数据字段,不能包含函数。

并且每次都要定义,都要加上 struct 才行,即便是已经定义好的 struct。

比如:

struct Student{}; // struct定义完需要加上分号,表示语句结束
void getStudent(struct Student student);

而 cpp 中的 struct 是支持包含数据与方法的。

c 需要通过一些高级技巧,例如函数指针,才能模拟方法,模拟类。

fgets 使用

函数原型

char *fgets(char *str, int n, FILE *stream);
  • str:指向字符数组的指针,用于存储读取的字符串。这个就是缓冲区 char buffer[100]
  • n:最大读取字符数(包括结尾的空字符 \0)。
  • stream:文件流(如 stdin 或文件指针)。

返回指向字符数组的指针。

stream

流,是数据输入输出的抽象对象,用于统一接口处理各种数据源。

主要类型:

  1. 标准流
    1. stdin:标准输入,键盘
    2. stdout:标准输出,屏幕
    3. stderr:标准错误,屏幕
  2. 文件流
    1. 通过 fopen() 打开文件返回的指针
  3. 其他流
    1. 字符串流
    2. 网络套接字流

strtol

将字符串转换为长整型。

函数原型

#include <stdlib.h>
long strtol(const char *str, char **endptr, int base);
  • str:要转换的字符串
  • endptr:指向剩余未转换部分的指针(可设为 NULL),比如 "1234abc",这个指针最后就指向 "abc" 了。
  • base:进制(2-36,0表示自动检测)

返回转换后的长整型值。

失败了返回 0

数组退化

c 语言中数组参数会退化成为指针,所以数组的所有其他属性,长度什么的就全部丢失了。

缓冲区

缓冲区(Buffer) 是内存中的一块临时存储区域,用于在数据从一个地方传输到另一个地方时暂存数据。

在C语言输入输出中,主要有两种缓冲区:

  1. 输入缓冲区:存储从键盘等输入设备读取的数据
  2. 输出缓冲区:存储要显示到屏幕等输出设备的数据

使用 c 语言的读取操作,经常会出现缓冲区问题,比如上一个 gets 的结束后输入 \n 也就是按下 enter,被下一个 gets 直接读取了,因为这个被放进缓冲区了,没有清空,下一个 gets 直接读取,导致程序出错。

scanf 的模式匹配机制

scanf 本质上是一个模式匹配函数,它根据格式字符串中的格式说明符来解析输入。