C语言文件操作与宏定义常见问题解析

文件

文本文件

(ASCII文件)

二进制文件

(二进制文件)

文件操作

注意事项

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

为什么 fclose 必须调用?

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

fgets 和 fscanf 的区别

  • fputs 专门写字符串
  • fprintf 专门写格式化数据

文件定位操作

  • fseek(fp, offset, whence)
  • fseek(fp,-3,SEEK_END); 表示移动到文件末尾的第3个字节

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

  • 计算文件的大小使用

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

文件mode列表

位置指针

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

  • 判文件结束函数 feof()

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

读取输入返回值

预处理

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

  • 防止重复包含

#ifndef TEST_H
#define TEST_H

// 内容

#endif

宏函数

#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
  • 注意:如果在程序编译/运行中链接了库文件,系统会到指定的目录下去查找库文件, 一般查找顺序如下:
  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)将某个数据二进制位(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)

位端

  • 位端 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 在低地址处到底存了什么,从而判断出系统的字节序。