APUE读书笔记(三)

5 标准I/O库

流和FILE对象

流的定向决定了读写字符是单字节(字节定向)还是多字节(宽定向)。
设置流的定向的模式(不改变已定向流):

#include <stdio.h>
#include <wchar.h>
int fwide(FILE *fp, int mode);
//返回值:流为宽定向则返回正值,字节定向则返回负值,未定向则返回0
/*
mode为负则指定字节定向;
mode为正则指定宽定向;
mode为0则不作设置而返回当前流定向状态
*/

标准输入、标准输出和标准错误

标准输入、标准输出和标准错误的FILE流指针分别为stdinstdoutstderr

缓冲

标准I/O提供:

  1. 全缓冲;
  2. 行缓冲;
  3. 不带缓冲。

冲洗是缓冲区的写操作。
不指向交互式设备的标准输入和标准输出是全缓冲(反之是行缓冲),标准错误不带缓冲。
更改缓冲类型:

#include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf);//buf为NULL则不带缓冲(否则缓冲区长度为BUFSIZE)
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);//mode为_IOFBF或_IOLBF且buf为NULL则选择合适长度系统缓冲区,为_IONBF且buf为NULL则无缓冲
//返回值:成功则返回0,出错则返回非0
/*
mode:
_IOFBF为全缓冲;
_IOLBF为行缓冲;
_IONBF为不带缓冲
*/

强制冲洗一个流(使所有未写的数据被传送至内核):

#include <stdio.h>
int fflush(FILE *fp);
//返回值:成功则返回0,出错则返回EOF

打开流

打开标准I/O流:

#include <stdio.h>
FILE *fopen(const char *restrict pathname, const char *restrict type);
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);//将打开流指定为fp(已定向则先清除)
FILE *fdopen(int fd, const char *type);
//返回值:成功则返回文件指针,出错则返回NULL
/*
type:
"r"或"rb"即读打开;
"w"或"wb"即把文件截断至0或写打开;
"a"或"ab"即追加(在文件尾写打开或写创建);
"r+"或"r+b"或"rb+"即读写打开;
"w+"或"w+b"或"wb+"即把文件截断至0或读写打开;
"a+"或"a+b"或"ab+"即在文件尾读写打开或读写创建
*/

关闭打开的流(关闭前冲洗缓冲区):

#include <stdio.h>
int fclose(FILE *fp);
//返回值:成功则返回0,出错则返回EOF

进程正常终止时会关闭所有打开的标准I/O流(先会冲洗缓冲区)。

读和写流

打开流后3种非格式化I/O:

  1. 每次一字符的I/O;
  2. 每次一行的I/O;
  3. 直接I/O。

一次读一字符:

#include <stdio.h>
int getc(FILE *fp);//可被实现为宏
int fgetc(FILE *fp);
int getchar(void);//等同于getc(stdin)
//返回值:若成功则返回下一字符,若已到达文件尾或出错则返回EOF

流的出错相关操作:

#include <stdio.h>
int ferror(FILE *fp);//检测出错
int feof(FILE *fp);//检测文件结束
//返回值:条件为真则返回非0,否则返回0

void clearerr(FILE *fp);//清除出错和文件结束标志

将字符压到流中:

#include <stdio.h>
int ungetc(int c, FILE *fp);
//返回值:成功则返回c,出错则返回EOF

一次写一字符:

#include <stdio.h>
int putc(int c, FILE *fp);//可被实现为宏
int fputc(int c, FILE *fp);
int putchar(int c);//等同于putc(c, stdout)
//返回值:成功则返回c,出错则返回EOF

每次一行I/O

每次读一行:

#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);//读到下一个换行符为止(读进去),不超过n-1个字符,缓冲区以null字节结尾
char *gets(char *buf);//与stdin相连,避免使用(不安全,缓冲区可能溢出)
//返回值:成功则返回buf,已达到文件尾或出错则返回NULL

每次写一行:

#include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp);//以null字节分割字符串但不将其写出(与换行符无关)
int puts(const char *str);//与stdout相连,不推荐使用(不是不安全,尾部会加个换行符)
//返回值:成功则非负值,出错则返回EOF

标准I/O效率

标准I/O库与直接调用readwrite相比不慢很多。

二进制I/O

二进制读写操作:

#include <stdio.h>
size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
//返回值:读或写的对象数

二进制I/O可移植性较差。

定位流

三种方法定位标准I/O流:

  1. 假定文件的位置可以存放在长整型中:
    #include <stdio.h>
    long ftell(FILE *fp);//得到当前位置
    //返回值:成功则返回当前文件位置指示,出错则返回-1L
    int fseek(FILE *fp, long offset, int whence);//设置位置
    //返回值:成功则返回0,出错则返回-1
    void rewind(FILE *fp);//回绕
  2. 使用off_t
    #include <stdio.h>
    off_t ftello(FILE *fp);//得到当前位置
    //返回值:成功则返回当前文件位置,出错则返回(off_t)-1
    int fseeko(FILE *fp, off_t offset, int whence);//设置位置
    //返回值:成功则返回0,出错则返回-1
  3. 由ISO C引入(可移植性好):
    #include <stdio.h>
    int fgetpos(FILE *restrict fp, fpos_t *restrict pos);//得到当前位置
    int fsetpos(FILE *fp, const fpos_t *pos);//设置位置
    //返回值:成功则返回0,出错则返回

格式化I/O

  1. 格式化写:
    #include <stdio.h>
    int printf(const char *restrict format, ...);//与stdout相连
    int fprintf(FILE *restrict fp, const char *restrict format, ...);
    int dprintf(int fd, const char *restrict format, ...);
    //返回值:成功则返回输出字符数,输出错误则返回负值
    int sprintf(char *restrict buf, const char *restrict format, ...);//不安全(缓冲区可能溢出)
    //返回值:成功则返回存入数组的字符数,编码出错则返回负值
    int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);
    //返回值:缓冲区足够大则返回将要存入数组的字符数,编码出错则返回负值
    使用arg的版本:
    #include <stdarg.h>
    #include <stdio.h>
    int vprintf(const char *restrict format, va_list arg);//与stdout相连
    int vfprintf(FILE *restrict fp, const char *restrict format, va_list arg);
    int vdprintf(int fd, const char *restrict format, va_list arg);
    //返回值:成功则返回输出字符数,输出错误则返回负值
    int vsprintf(char *restrict buf, const char *restrict format, va_list arg);//不安全(缓冲区可能溢出)
    //返回值:成功则返回存入数组的字符数,编码出错则返回负值
    int vsnprintf(char *restrict buf, size_t n, const char *restrict format, va_list arg);
    //返回值:缓冲区足够大则返回将要存入数组的字符数,编码出错则返回负值
  2. 格式化读:
    #include <stdio.h>
    int scanf(const char *restrict format, ...);//与stdin相连
    int fscanf(FILE *restrict fp, const char *restrict format, ...);
    int sscanf(const char *restrict buf, const char *restrict format, ...);
    //返回值:赋值的输入项数,输入出错或任一转换前已到达文件尾则返回EOF
    使用arg的版本:
    #include <stdarg.h>
    #include <stdio.h>
    int vscanf(const char *restrict format, va_list arg);//与stdin相连
    int vfscanf(FILE *restrict fp, const char *restrict format, va_list arg);
    int vsscanf(const char *restrict buf, const char *restrict format, va_list arg);
    //返回值:指定的输入项数,输入出错或任一转换前已到达文件尾则返回EOF

函数fileno

将流指针转换为文件描述符:

#include <stdio.h>
int fileno(FILE *fp);
//返回值:与该流相关联的文件描述符

临时文件

ISO C提供的关于临时文件的函数:

#include <stdio.h>
char *tmpnam(char *ptr);//仅生成文件名并存放在ptr中,ptr为NULL则其存在静态区(会复用)中
//返回值:指向唯一路径名的指针

FILE *tmpfile(void);//生成文件名并创建它,其为二进制文件(wb+),关闭文件或程序结束时自动删除(先unlink)
//返回值:成功则返回文件指针,出错则返回NULL

SUS也定义了关于临时文件的函数:

#include <stdlib.h>
char *mkdtemp(char *template);//创建目录(有用户读写执行权限,不自动删除)
//返回值:成功则返回指向目录名的指针,出错则返回NULL

int mkstemp(char *template);//创建文件(不自动删除)
//返回值:成功则返回文件描述符,出错则返回-1

内存流

创建内存流:

#include <stdio.h>
FILE *fmemopen(void *restrict buf, size_t size, const char *restrict type);//流关闭时缓冲区释放,会自动添加null字节
//返回值:成功则返回流指针,错误则返回NULL

其他创建内存流的两个函数:

#include <stdio.h>
FILE *open_memstream(char **bufp, size_t *sizep);
#include <wchar.h>
FILE *open_wmemstream(wchar_t **bufp, size_t *sizep);
//返回值:成功则返回流指针,出错则返回NULL
//只能写打开,无法指定自己的缓冲区(但可以访问其地址和大小),流关闭时缓冲区释放,可以对缓冲区扩容
全部评论

相关推荐

bg双非本科,方向是嵌入式。这次秋招一共拿到了&nbsp;8&nbsp;个&nbsp;offer,最高年包&nbsp;40w,中间也有一段在海康的实习经历,还有几次国家级竞赛。写这篇不是想证明什么,只是想把自己走过的这条路,尽量讲清楚一点,给同样背景的人一个参考。一、我一开始也很迷茫刚决定走嵌入式的时候,其实并没有一个特别清晰的规划。网上的信息很零散,有人说一定要懂底层,有人说项目更重要,也有人建议直接转方向。很多时候都是在怀疑:1.自己这种背景到底有没有机会2.现在学的东西到底有没有用3.是不是已经开始晚了这些问题,我当时一个都没答案。二、现在回头看,我主要做对了这几件事第一,方向尽早确定,但不把自己锁死。我比较早就确定了嵌入式这个大方向,但具体做哪一块,是在项目、竞赛和实习中慢慢调整的,而不是一开始就给自己下结论。第二,用项目和竞赛去“证明能力”,而不是堆技术名词。我不会刻意追求学得多全面,而是确保自己参与的每个项目,都能讲清楚:我负责了什么、遇到了什么问题、最后是怎么解决的。第三,尽早接触真实的工程环境。在海康实习的那段时间,对我触动挺大的。我开始意识到,企业更看重的是代码结构、逻辑清晰度,以及你能不能把事情说清楚,而不只是会不会某个知识点。第四,把秋招当成一个需要长期迭代的过程。简历不是一次写完的,面试表现也不是一次就到位的。我会在每次面试后复盘哪些问题没答好,再针对性补。三、我踩过的一些坑现在看也挺典型的:1.一开始在底层细节上纠结太久,投入产出比不高2.做过项目,但前期不会总结,导致面试表达吃亏3.早期有点害怕面试,准备不充分就去投这些弯路走过之后,才慢慢找到节奏。四、给和我背景相似的人一点建议如果你也是双非,准备走嵌入式,我觉得有几件事挺重要的:1.不用等“准备得差不多了”再投2.项目一定要能讲清楚,而不是做完就算3.不要只盯着技术,多关注表达和逻辑很多时候,差的不是能力,而是呈现方式。五、写在最后这篇总结不是标准答案,只是我个人的一次复盘。后面我会陆续把自己在嵌入式学习、竞赛、实习和秋招中的一些真实经验拆开来讲,希望能对后来的人有点帮助。如果你正好也在这条路上,希望你能少走一点弯路。
x_y_z1:蹲个后续
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务