Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

C语言封面

1. 数据的二进制

进制 规则 使用数字
二进制 逢 2 进 1 0-1
八进制 逢 8 进 1 0-7
十进制 逢 10 进 1 0-9
十六进制 逢 16 进 1 0-9、A-F

2. 进制转换

(1)其他进制转二进制

  • 八进制转二进制:三位一体,一位八进制对应三位二进制。
  • 十进制转二进制:短除法(除 2 取余)。
  • 十六进制转二进制:四位一体,一位十六进制对应四位二进制。

(2)其他进制转八进制

  • 二进制转八进制:三位一体。
  • 十进制转八进制:短除法(除 8 取余)。
  • 十六进制转八进制:先转二进制,再转八进制。

(3)其他进制转十进制

各个位上的数字乘以对应进制的位数次方(从右为 0 开始)后相加。

(4)其他进制转十六进制

  • 二进制转十六进制:四位一体。
  • 十进制转十六进制:短除法(除 16 取余)。
  • 八进制转十六进制:先转二进制,再转十六进制。

3. 程序中的数据分类

分类 说明 包含类型
基本数据类型 C 语言设计之初所规定的类型 整型、单精度浮点型、双精度浮点型、字符型
构造数据类型 C 语言编程过程中为了方便编程而规定的类型 数组、指针、结构体、共用体、枚举

4. 存储单位换算

单位 换算
1 Byte 8 bit(比特位,即二进制位数)
1 KB 1024 Byte(字节)
1 MB 1024 KB
1 GB 1024 MB
1 TB 1024 GB

5. while循环

注意:char类型循环输入时,需要清空输入缓冲区 –getchar接收换行
什么时候清空缓冲区:1、结束

6. 字符数组

1. 字符数据是什么?

字符数组是一个字符型的一维数组。

1
char ch[5] = {'h', 'e', 'l', 'l', 'o'};

如果作为字符串使用,需要以 '\0' 结尾:

1
2
3
char ch[6] = {'h', 'e', 'l', 'l', 'o', '\0'};
char ch[6] = "hello";
char ch[] = "hello";

2. 字符串的本质

字符串本质上是一个以 '\0' 结尾的字符型一维数组。

3. 字符串的操作

  1. 使用数组形式操作字符串:循环打印。
  2. 使用字符串格式操作字符串:
    • %s:自带循环,从指定地址开始输出字符,直到 '\0' 结束。
    • '\0':字符串结束符。系统在定义字符串时自动添加;定义字符数组时,长度要提前预留 '\0' 的位置。

4. 字符串常用函数

(1)输入

  • gets:输入一个字符串,可以包含空格;即将过时,可能有警告。
  • scanf:输入一个字符串,不能接收空格;空格在 scanf 中表示结束当前格式输入,不作为有效字符。
    (2)字符串长度
  • strlen:获取字符串实际长度,遇到 '\0' 结束,不计算 '\0'。需要 #include <string.h>。
  • sizeof:获取数组空间大小,默认包含 '\0' 的位置。
    (3)字符串复制
1
strcpy(接收方, 提供方);

会把提供方结尾的 '\0' 也复制过来。需要 #include <string.h>。

(4)字符串拼接

1
strcat(接收方, 提供方);

会把接收方结尾的 '\0' 覆盖掉。需要 #include <string.h>。
(5)字符串比较

1
strcmp(s1, s2);

返回值:

  • 0:相等
  • 非 0:不相等,通常正数表示 s1 > s2,负数表示 s1 < s2
    需要 #include <string.h>。

7. 指针

7.1 指针的定义

格式:

1
指针指向的空间类型 *变量名;

指针指向的空间类型:指针变量中存放谁的地址,就称为指针指向谁。

指针的大小:

  • 64 位操作系统:指针大小固定为 8 字节。
  • 32 位操作系统:指针大小固定为 4 字节。

7.2 解指针

格式:

1
*变量名;

含义:通过地址,操作指针指向空间的内容。

7.3 指针指向的空间类型

  • 全局变量:变量的值不赋值默认为 0。
  • 局部变量:变量的值不赋值默认为随机值。

7.4 野指针

野指针:没有明确指向的指针,应避免出现。

1
int *p; // 假设 p 是局部变量

操作 *p,会操作一块未知空间,非常危险。

  • 在 Linux 中有保护机制,会默认指向 0 地址,该地址会被保护起来,不能被操作。
  • 若在 MCU 中没保护,操作的地址未知,非常危险。

避免野指针出现:

一般定义指针时,如果暂时不知道存放谁的地址,直接让指针指向 NULL(0 号地址)。

1
int *p = NULL; // NULL 是 0 号地址,计算机预留,所有人都不能操作

假设不小心操作 *p,比如:

1
*p = 200;

会报段错误。

7.5 指针的偏移

指针的加减:以指针指向的空间大小为单位进行加减。

7.6 指针和数组

  • 数组指针:指向数组的指针。
  • 指针数组:数组中的元素都是指针。
(1)数组指针

指针指向一维数组:

1
2
int num[6] = {1, 2, 3, 4, 5, 6};
int *p = num; // C 语言规定,单独数组名是数组首元素地址,num 等价于 &num[0]

p + 1:以 int 为单位。

指针指向二维数组:

1
2
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*p)[3] = a; // a 是一个元素地址,又等于 &a[0]
  • 数组名:a
  • 数组中的元素:a[0]、a[1],每个元素类型都是 int [3]
  • p 里面存放数组的首元素地址,p 指向首元素
  • p 指针指向的空间类型:int [3]
  • p 的类型:int (*)[3]

取元素:

1
2
3
4
a[i][j]
*(*(p + i) + j)
p[i][j]
*(*(a + i) + j)
(2)指针数组

定义数组:

1
元素类型 数组名[元素个数];

示例:

1
int *ptr[10] = {NULL};

例子:

1
char *s[3] = {"hello", "xyz", "abc"};
  • sizeof(s):64 位下为 24 字节。
  • s[0]:char *,常量区 "hello" 的地址值。
  • s[1]:char *,常量区 "xyz" 的地址值。
  • s[2]:char *,常量区 "abc" 的地址值。

7.7 C 语言的 5 大区

区域 说明
栈区 存放局部变量
堆区 需要程序员用 malloc 等函数手动申请,用完之后用 free 函数释放
全局区/静态区 存放全局变量或者静态变量
代码区 存放二进制代码
常量区 存放常量

示例:

1
char *p = "hello"; // p 是指针,p 里面存放地址,"hello" 会被自动存放到常量区

7.8 const

const:修饰变量,使其只读。

位置:定义变量的时候。

方式 写法 效果
方式 1 const int num = 12; num 只读,num = 13; 报错
方式 2 int num = 100; const int *p = &num; const 修饰 *p,不能通过 *p 改变 num,*p = 200; 报错
方式 3 int num = 100; int const *p = &num; 同方式 2
方式 4 int a = 100, b = 200; int * const p = &a; const 修饰 p,p 的指向不能改,p = &b; 报错
方式 5 int a = 100, b = 200; const int * const p = &a; 第一个 const 修饰 *p,*p = 300; 报错;第二个 const 修饰 p,p = &b; 报错

const 在库函数中的应用:

1
2
3
4
size_t strlen(const char *s);
strcpy(char *s1, const char *s2);
strcat(char *s1, const char *s2);
strcmp(const char *s1, const char *s2);

7.9 拓展:数组名与数组首地址

1
int num[6] = {11, 22, 33, 44, 55, 66};
  • num:表示首元素地址,等于 &num[0](特殊规定)。
  • &num:表示整个数组的首地址。

用 %p 打印:

1
2
3
num
&num[0]
&num

三个的数值一样,但是做指针偏移的时候,偏移量不一样:

  • num + 1:以数组的元素为单位,等于 &num[0] + 1。
  • &num + 1:以整个数组为单位。

定义指针的格式:

1
int num[6] = {11, 22, 33, 44, 55, 66};

方式 1:

1
int *p = num; // num == &num[0]

p + 1:以 int 为单位。

方式 2:

1
int (*p)[6] = &num; // &num 整个数组的首地址

p + 1:以 int [6] 为单位。


8. 函数

8.1 函数的意义

封装代码,减少重复。

8.2 自定义子函数从定义到执行的过程

阶段 说明
函数定义 函数真正实现功能的过程
函数声明 告知编译器该函数存在
函数调用 函数真正被执行

8.3 函数的定义

格式:

1
2
3
4
5
返回值类型 函数名(形参类型 形参名, ...)
{
函数体;
return 返回值;
}

注意事项:

  • 返回值类型和返回值必须一致。
  • 如果没有返回值,返回值类型写 void。
  • return 后面什么也不写,甚至 return 也可以不写。
  • 返回值最多只能返回 1 个数。

8.4 形参和实参的关系

  1. 形参的起始值来自于实参,即参数传递的过程。
  2. 实参的个数和位置必须和形参一一对应,传参是按照对应位置传递。
  3. 形参名和实参名可以一样,也可以不一样。
  4. 形参和实参各自占用不同的内存空间。形参在子函数中发生改变时,实参不变。

8.5 参数传递方式

方式 说明
值传递 形参的改变不会引起实参的改变,形参和实参占用不同的存储空间
地址传递 通过地址改变指针指向空间的值
数组传参 数组名是首元素地址;通过地址修改数组中的值,属于地址传递

8.6 static 的使用

类型 生命周期 作用范围
局部变量 从定义处开始,到当前函数结束 从定义处开始,到当前函数结束
全局变量 从定义处开始,到当前 .c 结束 从定义处开始,到当前 .c 结束
static 修饰局部变量 延长生命周期到整个 .c 结束 作用范围不变

补充说明:

  • static 修饰局部变量,延长生命周期,但作用范围不变。
  • 一般返回值为数组时,数组前面要加 static。
  • 如果子函数被多次调用,被 static 修饰的变量只定义一次。

8.7 函数的递归操作

函数自身调用自身的一种操作。

注意:一定会存在递归的结束条件。

8.8 函数、指针和数组

(1)指针函数

指针函数:返回值为指针的函数。

格式:

1
返回类型 *函数名(形参列表);
(2)函数指针

函数指针:定义指针,指针指向函数。

格式:

1
指针指向的空间类型 (*变量名)(形参列表);

示例:

1
2
3
4
5
6
7
float Avg_Fun(float *p, int n);

// Avg_Fun 的类型:float (float *, int)
float (*w)(float *, int) = &Avg_Fun;

// C 语言规定,函数名也是一个地址
float (*w)(float *, int) = Avg_Fun;
(3)函数指针数组

当多个函数指针的类型一样时,可以将它们封装成数组。

示例:

1
2
3
4
5
int Max_Fun(int a, int b);
int Min_Fun(int a, int b);

int (*p1)(int a, int b) = Max_Fun;
int (*p2)(int a, int b) = Min_Fun;

由于 p1 和 p2 的类型一样,因此可以将它们封装成数组:

1
int (*ptr[2])(int a, int b) = {Max_Fun, Min_Fun};

说明:

  • ptr[0]:类型是 int (*)(int, int),赋值为 Max_Fun
  • ptr[1]:类型是 int (*)(int, int),赋值为 Min_Fun
  • 操作 ptr[0] 就是操作 Max_Fun
  • 操作 ptr[1] 就是操作 Min_Fun

调用等价关系:

1
2
Max_Fun(24, 9); // 等价于 ptr[0](24, 9);
Min_Fun(24, 9); // 等价于 ptr[1](24, 9);

9. 结构体

9.1 结构体是什么?

把具有不同类型的相同属性特征的事物归为一类。

9.2 结构体的意义

需求 使用类型
保存多个不同数据类型的数据 结构体
保存单个数据类型的数据 基本数据类型
保存多个具有相同数据类型的数据 数组

9.3 结构体的表现形式

(1)定义自定义类型:struct
1
2
3
struct 类型名 {
成员变量;
};
(2)声明时是否占用内存空间?

声明时不占用内存空间。

9.4 结构体的空间大小

(1)自动计算

使用 sizeof,项目中直接使用。

(2)手动计算
  1. 先找到成员变量中基本数据类型占用最大的成员。
  2. 每一个成员变量的起始位置从该成员类型大小的整数倍开始计算,不够补齐;第一个成员变量默认从 0 开始计算。
  3. 最终大小要是最大成员变量的整数倍,不够需要补齐。

9.5 结构体应用

(1)结构体变量
  1. 定义方式:
    • 在声明结构体类型的同时,进行变量的定义。
    • 先声明结构体类型,再定义结构体变量。
      格式:
    1
    结构体类型 标识符 = 初始值;
  2. 类型重定义:
    • 宏定义:#define,本质是替换。
    • 类型重定义:typedef。
  3. 初始化:
    • 在定义变量的同时进行初始化:使用 {}。
    • 先定义变量,再进行初始化:使用 . 运算指定成员变量进行初始化。
    • 必须在函数内部进行初始化。
  4. 应用:
    • 输入输出:结构体变量、成员变量。
    • 结构体数组:获取结构体的成员变量,例如 stus[i].id。
    • 结构体指针:获取结构体的成员变量,例如 结构体指针->成员变量、p->id,表示成员变量的空间。

10. 预处理命令及模块化

10.1 预处理命令

1. 预处理命令是什么

处理在编译之前需要完成的内容。

2. 查看预处理后的文件
1
gcc -E a.c -o a.i
3. 常见预处理命令

(1)包含:#include

写法 查找优先级
#include <file.h> 优先从系统库目录中查找,其次在项目结构目录中查找
#include "file" 优先从项目结构目录中查找,其次在系统库目录中查找

(2)宏定义:#define

  • 本质:完全替换。
  • 不带参:
    1
    #define S 5
  • 带参:
    1
    2
    #define MAX(a,b) (a)>(b)?(a):(b)
    #define div(a,b) a/b

    注意:div 的参数是否分别加括号,结果可能不同。

(3)条件编译:#if / #endif

  • #if / #elif / #else / #endif:代码执行。
    1
    2
    3
    4
    #define N 1
    #if N == 1
    // 代码……
    #endif
    如果 N 为 0,比较后 #if 与 #endif 中间的内容会消失。
  • #ifdef / #endif:如果定义……
  • #ifndef / #define / #endif:如果未定义,则定义。
  • 作用:防止重复定义,常用于 .h 文件。

10.2 模块化

1. 模块化是什么

以项目级文件对程序进行分类管理。

2. 项目级文件系统
目录 说明 文件/产物
include 声明文件 .h 文件
lib 第三方库文件
output 编译后的可执行文件 a.out
src 源码文件 .c 文件
3. 模块化开发的作用
  • 方便代码移植
  • 保护源代码
4. 如何进行模块化开发

按功能模块进行文件划分:

  • 源码文件:.c 文件
    • 功能模块的具体实现
    • 一般是受保护的文件,不对外
  • 声明文件:.h 文件
    • 功能实现所需要的资源
    • 对外暴露的内容:功能函数的声明
5. 关键字 static / extern
关键字 作用
extern 修饰函数或全局变量的声明,表示该函数或全局变量是借用外部文件的内容
static 修饰局部变量:提高局部变量的生命周期,但是不提高其作用域。
修饰全局变量或函数的定义:表示该全局变量或函数私有化,不允许外部文件借用
6. 项目级程序编译

终端位置在项目目录下:

1
gcc src/*.c -o output/main -I include
7. 在 .h 文件中使用 #include 出现报错

CSDN 上有解决办法。

11. 链表

11.1 链表是什么?

存储方式对比
存储方式 地址特点 典型结构 内存区域 管理方式
线性存储 地址连续 数组 栈区 自动开辟,自动释放
链式存储 地址不连续 链表 堆区 手动开辟,手动释放

11.2 链表的组成

  • 链表由一个个节点组成。
  • 节点是一个具有保存数据和下一个节点地址的空间。
  • 节点定义:
    1
    2
    3
    4
    struct node {
    数据域;
    指针域;
    };
    示例:
    1
    2
    3
    4
    typedef struct node {
    int data; // 数据域
    struct node *next; // 指针域
    } Node;

11.3 常用函数

函数名 功能 头文件 函数原型 参数 返回值
malloc 开辟空间 #include <stdlib.h> void *malloc(size_t size); size_t size:需要开辟的空间大小 void *:开辟好的空间地址,需要强转为需要的空间类型
memset 空间初始化 #include <string.h> void *memset(void *s, int c, size_t n); void *s:要初始化的空间首地址
int c:初始化内容,一般写 0
size_t n:要初始化的空间大小
void *:初始化后的空间首地址(等于要初始化的空间首地址)
bzero 空间初始化 #include <string.h> void bzero(void *s, size_t n); void *s:要初始化的空间首地址
size_t n:要初始化的空间大小
无
free 释放空间 #include <stdlib.h> void free(void *ptr); void *ptr:要释放的空间地址 无

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdlib.h>
#include <string.h>

int main() {
// 开辟一个 Node 大小的空间
Node *p = (Node *)malloc(sizeof(Node));
if (p == NULL) {
return -1;
}

// 初始化空间为 0
memset(p, 0, sizeof(Node));
// 或者使用 bzero(p, sizeof(Node));

// 使用 ...

// 释放空间
free(p);
p = NULL;

return 0;
}

11.4 单链表操作

1. 创建结点
  • 声明节点类型(.h 文件)
  • 根据节点类型,开辟节点空间(malloc)
  • 初始化节点空间
  • 返回开辟好的节点地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdlib.h>
#include <string.h>

typedef struct node {
int data;
struct node *next;
} Node;

Node *create_node(int data) {
Node *new_node = (Node *)malloc(sizeof(Node));
if (new_node == NULL) {
return NULL;
}
memset(new_node, 0, sizeof(Node));
new_node->data = data;
new_node->next = NULL;
return new_node;
}
2. 新增节点

(1)尾插法

1
2
3
4
5
6
7
8
9
10
11
void append_node(Node *head, int data) {
Node *new_node = create_node(data);
if (new_node == NULL) {
return;
}
Node *p = head;
while (p->next != NULL) {
p = p->next;
}
p->next = new_node;
}

(2)头插法

1
2
3
4
5
6
7
8
void insert_head(Node **head, int data) {
Node *new_node = create_node(data);
if (new_node == NULL) {
return;
}
new_node->next = *head;
*head = new_node;
}
3. 遍历链表
1
2
3
4
5
6
7
8
9
#include <stdio.h>
void traverse(Node *head) {
Node *p = head;
while (p != NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
4. 删除节点

按值删除节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void delete_node(Node **head, int data) {
Node *p = *head;
Node *prev = NULL;

while (p != NULL && p->data != data) {
prev = p;
p = p->next;
}

if (p == NULL) {
return; // 未找到
}

if (prev == NULL) {
*head = p->next;
} else {
prev->next = p->next;
}

free(p);
p = NULL;
}
5. 节点排序

以冒泡排序为例,交换数据域:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void sort_list(Node *head) {
if (head == NULL || head->next == NULL) {
return;
}

Node *p, *q;
for (p = head; p->next != NULL; p = p->next) {
for (q = head; q->next != NULL; q = q->next) {
if (q->data > q->next->data) {
int temp = q->data;
q->data = q->next->data;
q->next->data = temp;
}
}
}
}

11.5 循环链表

  • 循环链表是一种首尾相连的链表。
  • 尾节点的 next 指针不再为 NULL,而是指向头节点。
    结构定义与单链表相同,只是操作时需要注意尾节点指向头节点。
1
2
3
4
5
6
7
8
// 创建循环链表节点
Node *create_circular_node(int data) {
Node *new_node = create_node(data);
if (new_node != NULL) {
new_node->next = new_node; // 自己指向自己,形成单节点循环
}
return new_node;
}

11.6 双向链表

  • 双向链表每个节点有两个指针域:一个指向前驱,一个指向后继。
  • 可以双向遍历,操作更灵活。
    结构定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct dnode {
int data;
struct dnode *prev;
struct dnode *next;
} DNode;

DNode *create_dnode(int data) {
DNode *new_node = (DNode *)malloc(sizeof(DNode));
if (new_node == NULL) {
return NULL;
}
memset(new_node, 0, sizeof(DNode));
new_node->data = data;
new_node->prev = NULL;
new_node->next = NULL;
return new_node;
}

12. 文件

12.1 文件存储形式

  • 二进制形式存储
  • 字符(ASCII)形式存储

12.2 文件操作的目的

将程序中的数据永久保存在磁盘中,以文件形式存储。

12.3 文件操作的方式

方式 特点 说明
基于缓冲区的文件操作 数据安全,速度慢 标准输入缓冲区 stdin、标准输出缓冲区 stdout、标准错误缓冲区 stderr
基于非缓冲区的文件操作 数据不安全,速度快 直接操作磁盘

12.4 文件操作的步骤

  1. 打开文件:建立程序与文件之间的通道
  2. 读写数据:操作数据到程序或文件中,读写以程序为参照
  3. 关闭文件:关闭文件通道

12.5 常用函数

函数概览
函数 功能 头文件
fopen 打开文件 <stdio.h>
fclose 关闭文件 <stdio.h>
perror 打印错误信息 <stdio.h>
fputc 写一个字符 <stdio.h>
fgetc 读一个字符 <stdio.h>
fputs 写一个字符串 <stdio.h>
fgets 读一个字符串 <stdio.h>
fwrite 写一块数据 <stdio.h>
fread 读一块数据 <stdio.h>
fprintf 格式化写 <stdio.h>
fscanf 格式化读 <stdio.h>
rewind 光标移到首位 <stdio.h>
fseek 光标移到指定位置 <stdio.h>
ftell 获取光标位置 <stdio.h>
feof 判断是否到文件末尾 <stdio.h>
详细说明与示例
1. fopen
  • 功能:打开文件
  • 原型:FILE *fopen(const char *pathname, const char *mode);
  • 参数:
    • pathname:路径/文件名,默认当前目录
    • mode:操作模式
      • "r":只读,文件不存在则打开失败
      • "w":只写,创建新文件,若原文件存在则覆盖(清空写入)
      • "a":追加写入,在光标位置追加,默认打开时光标在首位
      • "r+"、"w+"、"a+":读写能力,保留原特性
  • 返回值:FILE * 文件流指针,失败返回 NULL
    示例:
1
2
3
4
5
FILE *fp = fopen("test.txt", "w");
if (fp == NULL) {
perror("fopen");
return -1;
}
2. perror
  • 功能:打印函数执行结果(失败或成功信息)
  • 原型:void perror(const char *s);
  • 参数:s:函数名称或提示信息
  • 返回值:无
    示例:
1
perror("fopen");
3. fclose
  • 功能:关闭文件
  • 原型:int fclose(FILE *stream);
  • 参数:stream:打开的文件流指针
  • 返回值:0 成功,-1 失败
    示例:
1
fclose(fp);
4. fputc
  • 功能:写出一个字符到文件
  • 原型:int fputc(int c, FILE *stream);
  • 参数:c:写出的字符;stream:文件流指针或 stdout
  • 返回值:写出字符的 int 类型值,-1 失败
    示例:
1
2
fputc('A', fp);
fputc('B', stdout);
5. fgetc
  • 功能:从文件读取一个字符到程序
  • 原型:int fgetc(FILE *stream);
  • 参数:stream:文件流指针或 stdin
  • 返回值:读取字符的 int 类型值,-1 失败
    示例:
1
2
3
4
int ch = fgetc(fp);
if (ch != EOF) {
printf("%c", ch);
}
6. rewind
  • 功能:移动光标到首位
  • 原型:void rewind(FILE *stream);
  • 参数:stream:文件流指针
  • 返回值:无
    示例:
1
rewind(fp);
7. fseek
  • 功能:移动光标到指定位置
  • 原型:int fseek(FILE *stream, long offset, int whence);
  • 参数:
    • stream:文件流指针
    • offset:光标偏移量(正数向右,负数向左)
    • whence:光标参考位置
      • SEEK_SET:首位(0)
      • SEEK_CUR:当前(1)
      • SEEK_END:末尾(2)
  • 返回值:0 成功,-1 失败
    示例:
1
2
fseek(fp, 0, SEEK_END); // 移动到文件末尾
long size = ftell(fp); // 获取文件大小
8. fputs
  • 功能:写出一个字符串到文件
  • 原型:int fputs(const char *s, FILE *stream);
  • 参数:s:写出的字符串;stream:文件流指针
  • 返回值:写出字符串的字节数,-1 失败
    示例:
1
fputs("Hello World\n", fp);
9. fgets
  • 功能:从文件读取一个字符串到程序
  • 原型:char *fgets(char *s, int size, FILE *stream);
  • 参数:
    • s:保存读取字符串的首地址(数组)
    • size:保存的最大长度
    • stream:文件流指针
  • 返回值:保存读取字符串的首地址,失败返回 NULL
    示例:
1
2
3
char buf[100];
fgets(buf, sizeof(buf), fp);
printf("%s", buf);
10. fwrite
  • 功能:写出一个块数据到文件(以二进制形式写出)
  • 原型:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
  • 参数:
    • ptr:任意类型的块数据首地址
    • size:块大小
    • nmemb:块数量
    • stream:文件流指针
  • 返回值:写出的数据字节数,-1 失败
    示例:
1
2
int arr[3] = {1, 2, 3};
fwrite(arr, sizeof(int), 3, fp);
11. fread
  • 功能:读取一个块数据到程序
  • 原型:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • 参数:
    • ptr:任意类型的块数据首地址
    • size:块大小
    • nmemb:块数量
    • stream:文件流指针
  • 返回值:读取的数据字节数,-1 失败
    示例:
1
2
int arr[3];
fread(arr, sizeof(int), 3, fp);
12. fprintf
  • 功能:格式化写出
  • 原型:int fprintf(FILE *stream, const char *format, ...);
  • 参数:
    • stream:文件流指针
    • format:格式字符串
    • ...:可变参数,输出列表
  • 返回值:写出数据的字节数
    示例:
1
fprintf(fp, "%d %s\n", 100, "hello");
13. fscanf
  • 功能:格式化读取
  • 原型:int fscanf(FILE *stream, const char *format, ...);
  • 参数:
    • stream:文件流指针
    • format:格式字符串
    • ...:可变参数,地址列表
  • 返回值:读取数据的字节数
    示例:
1
2
3
int num;
char str[50];
fscanf(fp, "%d %s", &num, str);
14. feof
  • 功能:判断光标位置是否在末尾
  • 原型:int feof(FILE *stream);
  • 参数:stream:文件流指针
  • 返回值:文件指针指向文件结束位置返回 1,否则返回 0
    示例:
1
2
3
4
5
6
while (!feof(fp)) {
int ch = fgetc(fp);
if (ch != EOF) {
putchar(ch);
}
}
15. ftell
  • 功能:获取光标位置(距离首位的字节数)
  • 原型:long ftell(FILE *stream);
  • 参数:stream:文件流指针
  • 返回值:光标距离首位的字节数(文件大小)
    示例:
1
2
3
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
printf("文件大小:%ld 字节\n", size);

12.6 综合示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdio.h>
#include <string.h>
int main() {
// 打开文件
FILE *fp = fopen("demo.txt", "w+");
if (fp == NULL) {
perror("fopen");
return -1;
}

// 写入数据
fputs("Hello, World!\n", fp);
fprintf(fp, "数字:%d\n", 123);
int arr[] = {1, 2, 3};
fwrite(arr, sizeof(int), 3, fp);

// 移动光标到开头
rewind(fp);

// 读取字符串
char buf[100];
fgets(buf, sizeof(buf), fp);
printf("读取字符串:%s", buf);

// 读取格式化数据
int num;
fscanf(fp, "数字:%d\n", &num);
printf("读取数字:%d\n", num);

// 读取块数据
int arr2[3];
fread(arr2, sizeof(int), 3, fp);
printf("读取数组:%d %d %d\n", arr2[0], arr2[1], arr2[2]);

// 获取文件大小
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
printf("文件大小:%ld 字节\n", size);

// 关闭文件
fclose(fp);

return 0;
}

14. 待补充

评论