Uinx\Linux系统编程第九章
I/O库函数
I/O库函数与系统调用
系统调用函数:open()、read()、write()、lseek()、close()
I/O库函数:fopen()、fread()、fwrite()、fseek()、fclose()
相同点:
1、 目的:I/O库函数和系统调用都用于进行输入/输出操作,以便程序能够与外部设备、文件系统或网络通信。
2、 提供抽象:它们都提供了一种抽象层,使开发者能够更容易地进行I/O操作,而无需深入了解底层的硬件或操作系统细节。
3、 可移植性:它们提供了跨平台的接口,允许开发者编写可移植的代码,而不必担心不同操作系统的细节。
不同点:
1、层次:I/O库函数通常是高级抽象,构建在底层系统调用之上,为程序员提供更友好的接口。系统调用则直接与操作系统内核交互。
2、性能:由于I/O库函数包含了额外的抽象层,通常会比系统调用慢一些,因为它们需要更多的处理和管理。
3、控制权:系统调用提供了更多的控制权,因为它们直接与操作系统内核通信,可以执行更底层的操作。I/O库函数则更侧重于方便性和易用性。
I/O库函数的算法
fread算法
fread()
是C语言中标准I/O库函数之一,用于从文件中读取数据。它的主要作用是从指定的文件流中读取数据块,并将这些数据块存储到内存中的缓冲区。fread() 的原型如下:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
参数说明:
ptr:
一个指向用于存储读取数据的缓冲区的指针。
size:
每个数据项的字节数。
count:
要读取的数据项的数量。
stream:
指向文件流的指针,表示从哪个文件中读取数据。
函数返回值:
fread()
返回成功读取的数据项数量,通常与count
相等。如果发生错误或到达文件末尾,它可能返回一个小于count
的值。
I/O库模式
字符模式I/O
int fgetc( File *fp);
int unget( int c ; FILE *fp);
int fputc( int c , FILE *fp);
实现对文本的字符读写,fgetc返回的是整数,(其中fgetc实现按字符读操作,而ungetc和fputc则是按字符写入操作)
行模式I/O
char *fgets(char *buf , int size_z , FILE *fp);
int fputs(char *buf , FILE *fp);
fgets()函数从指定的流 fp 读取一行,并把它存储在 buf 所指向的字符串内。结束条件:当读取 (size_z-1) 个字符时,或者读取到换行符时,或者到达文件末尾。
格式化的I/O
格式化输入
scanf(char *FMT , &items);
fscanf(FILE *fp , char *FMT , &items);
scanf()从标准输入 stdin 读取格式化输入;fscanf()从流 fp 读取格式化输入;
格式化输出
printf(char *FMT , &items);
fprintf(FILE *fp , char *FMT , &items);
其它的I/O数据库函数
- fseek() ftell() rewind() :更改文件流中的读/写字节位置;
- feof() ferr() fileno() :测试文件流状态;
- fdopen() :用文件描述符打开文件流;
- freopen() :以新名称重新打开现有流;
- setbuf() setvbuf() :设置缓冲方案;
- popen() :创建管道,复刻子进程来调用sh。
文件流缓冲
-
无缓冲:从非缓冲流中写入或者读取的字符将马上被单独传送到文件中去或者从文件中传输出来。如stder函数,所有输出都会立即发出。
-
行缓冲:遇到换行符时,以块的方式输出(逐行输出)。
-
全缓冲:写入全缓冲流或从中读取的字符以块的形式传输。一般的文件流缓冲方式。
在fopen()创建文件流之后,在对它进行任何操作之前,用户均可发出一个
int setvbuf(FILE *stream, char *buf, int mode, int size)
模式必须为以下三者之一:
- _IOFBF/(F=full)全缓冲
- _IOLBF/(L=line)行缓冲
- _IONBF 无缓冲
chatgpt的苏格拉底式提问
- fread()函数
- I/O库函数的字符模式
在命令行出现的问题及解决
在库链接时出现了问题:
原因是库的名字要使用lib开头,改为libstaticlib.a。
改好后又出现了下面问题:
询问chatgpt后解释说:
最后加上hello.c就好了
但是在链接动态库的时候又出现了问题:
经询问chatgpt,它告诉我库的尾缀只能是.a或者.so,不能是.dll。改了之后就好了。(最好是在一个目录下都编译好了以后再放到其它目录)
gdb调试:
概括gdb调试问题:
- s,n都是单步调试,但是遇到c语言本身的库函数,s会使调试进入到跟这个函数相关的很多步里去,但是n就会执行完这一行的库函数,相对来说n要方便,但是在进入到自己写的函数里调试的时候最好在第一步使用s。
- until可以让你执行完一个循环里的代码
- finish可以完成一整个函数。
- p 如果要看xxx的值,需要用p xxx。
- b可以很多类型的断点,如b 10 ; b if i=100; b main.
- c是继续执行直到下一个断点
- clear line/name:清除行/函数名前面的断点
- watch c:每当c的值改变的时候,显示它的新值和旧值