以下是对笔记中 Linux 文件操作(标准 I/O 为主) 知识点的系统总结,包含核心概念梳理、补充细节,并以表格整理标准 I/O 函数的参数与功能:
一、Linux 应用开发核心方向
笔记围绕 Linux 下的软件开发 展开,核心方向包括:
文件操作 | 对 Linux 各类文件(块设备、字符设备等)进行读写控制 | 文件类型、标准 I/O/文件 I/O 函数 |
多任务 | 通过进程、线程实现程序并行/并发执行 | 进程创建(fork)、线程管理等 |
网络编程 | 实现网络数据传输、通信 | Socket 编程、TCP/UDP 协议等 |
网络服务器构建 | 开发支持网络请求的服务程序 | HTTP 协议、套接字监听/响应逻辑 |
数据库 | 数据持久化存储与管理 | MySQL、SQLite 等数据库操作 |
二、Linux 文件类型(7 种)
Linux 中“一切皆文件”,通过 ls -l 可查看文件类型(首字符标识 ):
b | 块设备文件 | 存储设备(硬盘、U盘 ) | /dev/sda(硬盘分区 ) |
c | 字符设备文件 | 输入输出设备(键盘、显示器 ) | /dev/tty(终端 )、/dev/usb |
d | 目录文件 | 存储文件/子目录的“容器” | 任意文件夹(如 /home ) |
– | 普通文件 | 存储文本、二进制、图片等数据 | .c 源码、.txt 文档、可执行文件 |
l | 软链接文件(符号链接 ) | 快捷方式,指向其他文件/目录的路径 | ln -s 原文件 软链接名 创建 |
s | 套接字文件 | 进程间网络通信(本地/网络套接字 ) | /var/run/mysqld/mysqld.sock |
p | 管道文件 | 进程间通信(匿名/命名管道 ) | mkfifo 管道名 创建 |
三、文件操作核心流程
无论标准 I/O 还是文件 I/O,均遵循 “打开 → 读写 → 关闭” 三步:
打开 | 关联文件与程序,获取操作句柄 | fopen(返回 FILE* 流指针 ) | open(返回文件描述符 fd ) |
读写 | 按字符、行、数据块读写内容 | fgetc/fputc、fgets/fputs | read/write(按字节流读写 ) |
关闭 | 释放资源,确保数据落盘 | fclose | close(关闭文件描述符 ) |
Linux fopen 函数打开模式(mode)说明表格,包含每种模式的含义、文件存在/不存在时的行为:
r | 只读(read only) | 打开文件,流指针指向文件开头 | 报错(返回 NULL) | 读取配置文件、日志文件等 |
r+ | 读写(read + write) | 打开文件,流指针指向文件开头 | 报错(返回 NULL) | 读取并修改现有文件内容 |
w | 只写(write only,清空文件) | 清空文件内容,流指针指向开头 | 创建新文件 | 覆盖写入全新内容(如日志重置) |
w+ | 读写(write + read,清空文件) | 清空文件内容,流指针指向开头 | 创建新文件 | 覆盖写入并需要读取的场景 |
a | 追加写(append only) | 流指针移到文件末尾(保留内容) | 创建新文件 | 日志追加、消息队列写入 |
a+ | 追加读写(append + read) | 读:指针在开头;写:指针在末尾 | 创建新文件 | 追加内容同时需要回读的场景 |
补充说明
模式区分细节:
- r/r+ 严格依赖文件已存在,否则直接失败(返回 NULL,需检查 errno 确认错误 )。
- w/w+ 会强制清空现有文件(即使文件有内容 ),适合“覆盖写入”场景。
- a/a+ 的写操作始终在文件末尾(无论指针如何移动 ),但读操作可自由调整指针位置(如 fseek )。
实践建议:
- 打开文件后务必检查返回值(if (fp == NULL) { perror("fopen"); exit(1); } )。
- 不同模式的指针行为需注意,尤其是 a+ 模式(读和写的指针位置独立 )。
跨平台差异:
Windows 系统下,fopen 模式需额外区分文本模式("rt"/"wt" )和二进制模式("rb"/"wb" ),但 Linux 下无此严格区分(默认二进制模式 )。
通过表格可直观对比各模式的行为差异,开发时根据需求(读、写、追加、覆盖 )选择对应模式即可。
四、标准 I/O 函数总结(核心函数参数 & 功能表)
标准 I/O 由 C 标准库(stdio.h)提供,基于 文件流(FILE*) 操作,核心函数如下:
1. 文件打开:fopen
FILE *fopen(const char *pathname, const char *mode);
功能 | 打开文件并返回 文件流指针(FILE*),失败返回 NULL。 |
参数 | – pathname:文件路径(如 "/tmp/test.txt")- mode:打开模式(见下表) |
返回值 | 成功返回 FILE* 流指针,失败返回 NULL(需检查 errno 定位错误)。 |
需要检查返回值以判断文件是否成功打开:
FILE *fp=fopen("./1.txt","w+");
FILE *fp1=fp;
if(fp==NULL)
{
printf("fopen error\\n");
return -1;
}
2. 文件关闭:fclose
int fclose(FILE *stream);
功能 | 关闭文件流,刷新缓冲区,释放资源。 |
参数 | stream:fopen 返回的文件流指针(FILE*)。 |
返回值 | 成功返回 0,失败返回 EOF(需检查 ferror(stream) 确认错误)。 |
对文件执行fopen后别忘了执行fclose关闭打开的文件:
3. 字符读写:fgetc / fputc
- 读字符:fgetcint fgetc(FILE *stream);
功能 | 从文件流 stream 读取一个字符(ASCII 码),流指针后移 1 字节。 |
参数 | stream:文件流指针(FILE*)。 |
返回值 | 成功返回字符的 ASCII 码(int 类型,如 'A' 对应 65);失败/到文件末尾返回 EOF。 |
代码:
#include<stdio.h>
int main(void)
{
FILE *fp=fopen("./1.txt","w+");
FILE *fp1=fp;
if(fp==NULL)
{
printf("fopen error\\n");
return -1;
}
fputc('h',fp);
fputc('e',fp);
fputc('l',fp);
fputc('l',fp);
fputc('o',fp);
fputc(' ',fp);
fputc('w',fp);
fputc('o',fp);
fputc('r',fp);
fputc('l',fp);
fputc('d',fp);
int ret=fgetc(fp1);
/*if(ret==EOF)
{
printf("end of file or error\\n");
}*/
while(ret!=EOF)
{
printf("%c",ret);
ret=fgetc(fp1);
}
fclose(fp);
printf("fopen successful\\n");
return 0;
}
- 写字符:fputcint fputc(int c, FILE *stream);
功能 | 向文件流 stream 写入一个字符(c 的 ASCII 码),流指针后移 1 字节。 |
参数 | – c:要写入的字符(如 'A' 或直接传 ASCII 码 65)- stream:文件流指针 |
返回值 | 成功返回写入字符的 ASCII 码;失败返回 EOF。 |
代码:逐个字符读取:
#include<stdio.h>
int main(void)
{
FILE *fp=fopen("1.txt","r");
int ret=fgetc(fp);
/*if(ret==EOF)
{
printf("end of file or error\\n");
}*/
while(ret!=EOF)
{
printf("%c",ret);
ret=fgetc(fp);
}
fclose(fp);
printf("fopen successful\\n");
return 0;
}
4. 行读写:fgets / fputs
- 读一行:fgetschar *fgets(char *s, int size, FILE *stream);
功能 | 从文件流 stream 读取一行数据(最多读 size-1 个字符,遇 \\n 或文件末尾停止 ),自动在 s 末尾加 \\0。 |
参数 | – s:存储数据的缓冲区(字符数组,如 char buf[1024] )- size:缓冲区大小(需 >1,预留 \\0 空间 )- stream:文件流指针 |
返回值 | 成功返回 s 缓冲区地址;失败/到文件末尾返回 NULL。 |
代码:
#include<stdio.h>
int main(int argc, const char *argv[])
{
FILE *PGS=fopen("./4.txt","r");
if(PGS==NULL)
{
printf("fopen error\\n");
return -1;
}
char s[100]={0};
while(1)
{
char *p=fgets(s,sizeof(s),PGS);
if(p==NULL)
{
break;
}
printf("%s\\n",p);
}
fclose(PGS);
return 0;
}
- 写一行:fputsint fputs(const char *s, FILE *stream);
功能 | 向文件流 stream 写入字符串(s 内容,不自动加 \\n,需手动添加 )。 |
参数 | – s:要写入的字符串(以 \\0 结尾,如 "hello" )- stream:文件流指针 |
返回值 | 成功返回非负整数(通常是写入字符数 );失败返回 EOF。 |
代码:
#include<stdio.h>
int main(int argc , const char *argv[])
{
FILE *FP=fopen("./4.txt","w");
if(FP==NULL)
{
printf("fopen error\\n");
return -1;
}
char s[100]={"china!\\n"};
int ret=fputs(s,FP);
if(ret==EOF)
{
printf("fputs error!\\n");
return -1;
}
return 0;
}
5. 数据块读写:fread / fwrite(补充)
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能 | 按数据块读写文件(适合二进制文件,如结构体、图片 )。 |
参数 | – ptr:缓冲区地址(读时存数据,写时取数据 )- size:单个数据块大小(字节 )- nmemb:数据块数量- stream:文件流指针 |
返回值 | 实际读写的数据块数量(size_t );失败或到末尾可能小于 nmemb。 |
五、补充细节 & 易错点
文件流指针移动:
读写操作会改变流指针位置(如 fgetc 后指针后移 1 字符 ),同一程序中“写后直接读”需用 fseek 重置指针(如 fseek(stream, 0, SEEK_SET) 回到文件开头 )。
缓冲区机制:
标准 I/O 带用户态缓冲区,数据先写入缓冲区,再由系统决定何时刷入磁盘。需立即落盘时,调用 fflush(stream) 手动刷新。
命令行参数:argc / argv
主函数参数用于接收命令行输入:
int main(int argc, char *argv[]);
- argc:参数总个数(含程序名 )。
- argv:参数数组,argv[0] 是程序路径/名称,argv[1] 开始是用户输入参数。
六,主函数传参的使用方法
以下是结合笔记内容,对 Linux C 主函数传参(argc/argv) 的总结与补充,用清晰的逻辑和示例说明用法:
1、主函数传参基础概念
在 Linux C 程序中,主函数 main 支持通过命令行传递参数,形式为:
int main(int argc, char *argv[]);
argc | 参数个数(argument count):包含程序名在内的所有命令行参数总数。 |
argv | 参数数组(argument vector):字符串数组,存储每个命令行参数的内容。 |
2、传参规则与示例
以笔记中示例 ./a.out aaa bbb 为例:
./a.out aaa bbb | 3 | argv[0] = "./a.out" argv[1] = "aaa" argv[2] = "bbb" | – argv[0] 固定为程序名/路径 – argv[1] 开始是用户传入的参数 |
3、传参方法与场景
1. 基础用法:命令行直接传参
编译生成可执行文件(如 a.out )后,在终端通过以下方式传参:
./a.out 参数1 参数2 参数3 ...
- 示例代码(遍历参数):#include <stdio.h>
int main(int argc, char *argv[]) {
printf("参数总数 argc = %d\\n", argc);
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\\n", i, argv[i]);
}
return 0;
} - 运行输出(输入 ./a.out aaa bbb ):参数总数 argc = 3
argv[0] = ./a.out
argv[1] = aaa
argv[2] = bbb
4、关键细节补充
argv 数组的结束标志:
argv[argc] 固定为 NULL,可用于遍历参数时的终止判断:
for (int i = 0; argv[i] != NULL; i++) {
printf("argv[%d] = %s\\n", i, argv[i]);
}
参数的本质是字符串:
argv 存储的是字符串,如需数值运算(如 ./app 10 20 ),需用 atoi/atof 转换:
#include <stdlib.h>
int num1 = atoi(argv[1]); // "10" → 10
int num2 = atoi(argv[2]); // "20" → 20
空格与引号处理:
- 命令行参数以空格分隔,如需传递含空格的参数,用引号包裹:./app "hello world" # argv[1] = "hello world"(含空格)
- 特殊字符(如 $、* )需用反斜杠转义(\\)或引号包裹。
七、总结表格(标准 I/O 核心函数参数速查表)
fopen | FILE *fopen(const char *pathname, const char *mode); | pathname(路径)、mode(打开模式) | 打开文件并获取流指针 |
fclose | int fclose(FILE *stream); | stream(文件流指针) | 关闭文件、刷新缓冲区 |
fgetc | int fgetc(FILE *stream); | stream(文件流指针) | 读 1 个字符 |
fputc | int fputc(int c, FILE *stream); | c(字符)、stream(文件流指针) | 写 1 个字符 |
fgets | char *fgets(char *s, int size, FILE *stream); | s(缓冲区)、size(最大读入字符 )、stream(文件流指针) | 读 1 行数据 |
fputs | int fputs(const char *s, FILE *stream); | s(字符串)、stream(文件流指针) | 写 1 行字符串(不含 \\0 ) |
使用fopen ,fputc,fgetc,fclose实现拷贝的功能:
代码:
#include<stdio.h>
int main(int argc ,const char *argv[])
{
if(argc!=3)
{
printf("Usage : ./a.out <srcfile> <dstfile>\\n");
return -1;
}
FILE *fp=fopen(argv[1],"r");
if(fp==NULL)
{
printf("fopen error\\n");
return -1;
}
FILE *fcp=fopen(argv[2],"w");
if(fcp==NULL)
{
printf("fopen error\\n");
return -1;
}
int ret =fgetc(fp);
if(ret==EOF)
{
printf("end of file or error");
}
while(ret!=EOF)
{
fputc(ret,fcp);
ret=fgetc(fp);
}
fclose(fp);
fclose(fcp);
return 0;
}
使用fopen,fgets,fputs,fclose库函数实现字符串的拷贝:
代码
#include<stdio.h>
int main(int argc,const char *argv[])
{
if(argc!=3)
{
printf("Usage : copy <srcfile> <dstfile>\\n");
return -1;
}
FILE *FGT=fopen(argv[1],"r");
if(FGT==NULL)
{
printf("fopen error\\n");
return -1;
}
FILE *FPT=fopen(argv[2],"w");
if(FPT==NULL)
{
printf("fopen error\\n");
return -1;
}
char s[100]={0};
while(1)
{
char *fgt=fgets(s,sizeof(s),FGT);
if(fgt==NULL)
{
break;
}
int fpt=fputs(fgt,FPT);
if(fpt==EOF)
{
printf("fputs error\\n");
return -1;
}
}
fclose(FGT);
fclose(FPT);
return 0;
}
评论前必须登录!
注册