C语言学习(文件)
May 19. 2026

AI文摘
此内容由AI根据文章内容自动生成.
加载中。

文件

文本文件

(ASCII文件)

二进制文件

(二进制文件)

文件操作

操作函数
打开文件fopen
读取文件fscanf / fgets / fread
写入文件fprintf / fputs / fwrite
定位文件fseek / ftell / rewind
关闭文件fclose

注意事项

  • fputs 函数不能写入换行符
  • fscanf 读取时必须传地址

为什么 fclose 必须调用?

  • 要刷新缓冲区
  • 要释放文件资源

fgets 和 fscanf 的区别

对比项fgets()fscanf()
读取方式按行读取按格式读取
是否能读取空格可以%s 不可以
遇到空格是否停止不会%s 会停止
遇到换行是否停止默认会跳过空白字符
是否保留 \n会保留不会保留
是否自动补 \0%s
是否支持格式化读取不支持支持
是否适合读取整行文本非常适合一般
是否容易越界不容易(可限制长度)容易
安全性较低
常见用途读取整行字符串读取格式化数据
推荐场景日志、文本行数字、结构化数据
  • fputs 专门写字符串
  • fprintf 专门写格式化数据

文件定位操作

  • fseek(fp, offset, whence)
含义
SEEK_SET文件开头
SEEK_CUR当前位置
SEEK_END文件末尾
参数含义
fp文件指针
offset偏移量,单位是字节
origin从哪里开始移动
  • fseek(fp,-3,SEEK_END); 表示移动到文件末尾的第3个字节

  • ftell(fp) 追加模式下,打开文件时指针处于文件末尾,ftell返回文件长度

  • 计算文件的大小使用

fseek(fp,0,SEEK_END)
ftell(fp);

文件mode列表

模式作用
r只读
w只写(清空原文件)
a追加写
r+读写
w+读写(清空)
a+读写追加
rb二进制读
wb二进制写
ab二进制追加

位置指针

  • 文件指针:文件指针是一个指向文件位置的指针,用于记录当前文件操作的位置。

  • 判文件结束函数 feof()

文本文件可以使用EOF 作为文件结束标志;二进制文件则使用feof进行函数判断文件是否结束,如果返回值为非0,则文件结束,否则返回值为0。

读取输入返回值

函数家族函数名成功时的返回值失败 / 结束时的返回值
单字符读写fgetc / getc读到的字符 (ASCII码)EOF (-1)
fputc / putc写入的字符EOF (-1)
字符串读写fgets缓冲区指针 (str)NULL (空指针)
fputs非负整数EOF (-1)
格式化读写fscanf成功读取的变量个数 (≥0)EOF (-1)
fprintf成功写入的字符数 (>0)负值
二进制块读写fread实际读取的元素个数小于要求的个数 (含 0)
fwrite实际写入的元素个数小于要求的个数

预处理

  • 编译的流程:预处理 -> 编译 -> 汇编 -> 链接

  • 防止重复包含

#ifndef TEST_H
#define TEST_H

// 内容

#endif

宏函数

坑点错误写法问题示例实际展开问题原因正确写法
参数没加括号#define SQUARE(x) x*xSQUARE(1+2)1+2*1+2运算优先级错误#define SQUARE(x) ((x)*(x))
整体没加括号#define ADD(a,b) a+b10*ADD(1,2)10*1+2外部运算影响宏结果#define ADD(a,b) ((a)+(b))
参数被重复执行#define MAX(a,b) ((a)>(b)?(a):(b))MAX(i++,j++)i++ 可能执行多次宏本质是文本替换尽量用函数替代
宏后加分号#define NUM 10;if(x) NUM多出一个 ;预处理不是语句#define NUM 10
字符串相关问题#define STR helloprintf("%s", STR)不是字符串少了双引号#define STR "hello"
多行宏未加 \宏换行直接写编译报错宏提前结束预处理按行处理使用 \ 连接
宏与变量同名#define NUM 10
int NUM;
编译异常变量名被替换宏全局替换避免重名
宏不检查类型#define ADD(a,b) ((a)+(b))ADD("a","b")可能错误宏没有类型检查复杂逻辑用函数
优先级陷阱#define MUL(a,b) a*bMUL(1+2,3+4)1+2*3+4缺少括号#define MUL(a,b) ((a)*(b))
if 配合宏问题多条语句宏if(x) TEST(); else ...else 报错宏展开导致语法混乱do{ }while(0)
宏中定义多语句#define TEST a++;b++;if(x) TEST逻辑混乱宏不是代码块do{a++;b++;}while(0)
宏递归定义#define A B
#define B A
使用 A展开异常循环替换避免循环宏
空格导致问题#define ADD(a, b) a + b某些复杂拼接替换不符合预期文本替换细节规范书写
宏调试困难宏内部复杂逻辑打断点困难看不到真实代码编译前已替换复杂逻辑改函数
宏不能取地址#define NUM 10&NUM编译错误宏不是变量使用 const
宏没有作用域#define PI 3.14全局污染所有文件可见预处理全局替换减少全局宏
条件编译遗漏#ifdef DEBUG 未关闭代码混乱编译逻辑错误条件不完整配套 #endif
头文件重复包含没有 include guard重定义错误重复展开多次 include使用 #ifndef
宏与函数名冲突#define getchar()调用库函数异常被宏替换名称污染避免覆盖库名
位运算宏坑点#define BIT(x) 1<<xBIT(1+2)1<<1+2优先级问题#define BIT(x) (1<<(x))

#undef 有什么作用?

取消宏定义
使宏失效
方便重新定义
避免宏污染

库操作

静态库生成

gcc -c add.c sub.c
ar rcs libmylib.a add.o sub.o
# rcs 表示 r:插入/替换文件 c:创建库文件 s:生成索引(很重要)
gcc main.c -L. -lmylib -o app

动态库生成

gcc -fPIC -c add.c -o add.o
gcc -shared -o libmylib.so add.o sub.o
gcc main.c -L. -lmylib -o app
对比静态库 (.a)动态库 (.so)
链接时间编译时运行时
文件大小
是否依赖库
更新方式重新编译替换 .so
内存占用每个程序一份多程序共享
  • 注意:如果在程序编译/运行中链接了库文件,系统会到指定的目录下去查找库文件, 一般查找顺序如下:
  1. -L 指定的路径
  2. LD_LIBRARY_PATH
  3. /etc/ld.so.conf里的路径
  4. 默认系统路径: /lib /usr/lib
  • 建议直接将库文件放置在 默认系统路径 ,可确保编译和运行时系统可以正确 加载库文件。

手动加载库文件

dlfcn 库函数, 使用时需要链接 -ldl

  1. dlopen() 返回值:*handle, 失败返回NULL,可以使用dlerror() 获取错误信息
  2. dlsym() 返回值:*func,失败返回NULL,可以使用dlerror() 获取错误信息
  3. dlclose()
  • handle 句柄

位运算

  • 位运算是对 二进制位(bit) 进行操作的运算
  • 位运算符:& | ^ ~ << >>
运算符名称作用
&按位与两位都为1才为1
||按位或,有1就为1
^按位异或相同为0,不同为1
~按位取反0变1,1变0
<<左移二进制整体左移
>>右移二进制整体右移
  • 1)将某个数据二进制位(n1,n2…)清零 a &= ~(1<<n1)
  • 2)获取某个数据指定二进制位(n1,n2…)的数据 (a & (1 << n1)) >> n1 (a >> n) & 1
  • 3)将某个数据二进制位(n1,n2…)置位 a |= (1<<n1)
  • 4)将某个数据二进制位(n1,n2…)反转 a ^= (1<<n1)
目的操作符
置1|
清0& ~
取值>> &1
翻转^

位端

  • 位端 CPU存储多字节数据时:字节存放顺序不同
  1. 浮点型类型不能做位段;
  2. 位段不能跨越内存单元
  3. 位段二进制位数不能超过类型长度
  4. 位段是无法获取地址的。
  5. C语言允许定义无名位段,无名位段无法访问
  6. 无名位段设置长度为0, 强制下一个位段存储于下一个内存单元
  7. 不能定义位段数组。
  8. 位段数据要考虑主机字节序

内存地址与位权的高低

内存地址的高低:看十六进制数值的大小。比如 0x101 比 0x100 大,所以 0x101 是高地址,0x100 是低地址。 位权的高低:看二进制数的书写位置。左边是高位(权重最大),右边是低位(权重最小)。例如在 10000001 中,左边的 1 代表 128(高位),右边的 1 代表 1(低位)。

大小端模式 (Endianness)

这是指多字节数据(如 int)在内存中的存储顺序: 小端模式 (Little-Endian):“低存低”。数据的低位字节存放在内存的低地址处。目前主流的 x86/x64 电脑都是小端模式。 大端模式 (Big-Endian):“高存低”。数据的高位字节存放在内存的低地址处。符合人类阅读习惯,常用于网络传输和部分嵌入式设备。 判断方法:利用联合体(union)或指针强转,将整数 1 存入内存,然后读取其最低地址的第一个字节。如果读到 1 就是小端,读到 0 就是大端。

联合体 (Union)

核心特性:联合体内的所有成员共享同一块内存空间,并且都是从这块内存的起始位置(低地址)开始存放。 巧妙应用:正是因为它共享内存且从低地址对齐的特性,我们才能用一个单字节的 char 去“偷看”一个四字节 int 在低地址处到底存了什么,从而判断出系统的字节序。

> comment on / twitter
>
CC BY-NC-SA 4.0 2021-PRESENT © RYANUO