编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • C语言入门精通

    • 入门教程

    • 综合案例

    • 专栏博客

    • 标准集库

      • C语言标准集库
      • 01.assert.h
      • 02.ctype.h
      • 03.errno.h
      • 04.float.h
      • 05.inttypes.h
      • 06.iso646.h
      • 07.limits.h
      • 08.locale.h
      • 09.math.h
      • 10.signal.h
      • 11.stdarg.h
      • 12.stdbool.h
      • 13.stddef.h
      • 14.stdint.h
      • 15.stdio.h
        • 标准 I/O 函数
        • 文件操作函数
        • 字符串操作函数
        • tmpfile()
        • tmpnam()
        • fflush()
        • setvbuf()
        • setbuf()
        • ungetc()
        • perror()
        • 可变参数操作函数
      • 16.stdlib.h
      • 17.string.h
      • 18.time.h
      • 19.wchar.h
      • 20.wctype.h
  • Cpp入门到精通

  • Java入门精通

  • Go入门到精通

  • JavaScript入门

  • CodeX
  • C语言入门精通
  • 标准集库
杨充
2025-03-26
目录

15.stdio.h

# stdio.h

stdio.h是 C 语言的标准 I/O 库,用于读取和写入文件,也用于控制台的输入和输出。

# 标准 I/O 函数

以下函数用于控制台的输入和输出。

  • printf():输出到控制台,详见《基本语法》一章。
  • scanf():从控制台读取输入,详见《I/O 函数》一章。
  • getchar():从控制台读取一个字符,详见《I/O 函数》一章。
  • putchar():向控制台写入一个字符,详见《I/O 函数》一章。
  • gets():从控制台读取整行输入(已废除),详见《I/O 函数》一章。
  • puts():向控制台写入一个字符串,详见《I/O 函数》一章。

# 文件操作函数

以下函数用于文件操作,详见《文件操作》一章。

  • fopen():打开文件。
  • fclose():关闭文件。
  • freopen():打开一个新文件,关联一个已经打开的文件指针。
  • fprintf():输出到文件。
  • fscanf():从文件读取数据。
  • getc():从文件读取一个字符。
  • fgetc():从文件读取一个字符。
  • putc():向文件写入一个字符。
  • fputc():向文件写入一个字符。
  • fgets():从文件读取整行。
  • fputs():向文件写入字符串。
  • fread():从文件读取二进制数据。
  • fwrite():向文件写入二进制数据。
  • fseek():将文件内部指针移到指定位置。
  • ftell():获取文件内部指针的当前位置。
  • rewind():将文件内部指针重置到文件开始处。
  • fgetpos():获取文件内部指针的当前位置。
  • fsetpos():设置文件内部指针的当前位置。
  • feof():判断文件内部指针是否指向文件结尾。
  • ferror():返回文件错误指示器的状态。
  • clearerr():重置文件错误指示器。
  • remove():删除文件。
  • rename():文件改名,以及移动文件。

# 字符串操作函数

以下函数用于操作字符串,详见《字符串操作》一章。

  • sscanf():从字符串读取数据,详见《I/O 函数》一章。
  • sprintf():输出到字符串。
  • snprintf():输出到字符串的更安全版本,指定了输出字符串的数量。

# tmpfile()

tmpfile()函数创建一个临时文件,该文件只在程序运行期间存在,除非手动关闭它。它的原型如下。

FILE* tmpfile(void);
1

tmpfile()返回一个文件指针,可以用于访问该函数创建的临时文件。如果创建失败,返回一个空指针 NULL。

FILE* tempptr;
tempptr = tmpfile();
1
2

调用close()方法关闭临时文件后,该文件将被自动删除。

tmpfile()有两个缺点。一是无法知道临时文件的文件名,二是无法让该文件成为永久文件。

# tmpnam()

tmpname()函数为临时文件生成一个名字,确保不会与其他文件重名。它的原型如下。

char* tmpname(char* s);
1

它的参数是一个字符串变量,tmpnam()会把临时文件的文件名复制到这个变量里面,并返回指向该字符串变量的指针。如果生成文件名失败,tmpnam()返回空指针 NULL。

char filename[L_tmpnam];

if (tmpnam(filename) != NULL)
  // 输出诸如 /tmp/filew9PMuZ 的文件名
  printf("%s\n", filename);
else
  printf("Something wrong!\n");
1
2
3
4
5
6
7

上面示例中,L_tmpnam是stdio.h定义的一个宏,指定了临时文件的文件名长度。

tmpname()的参数也可以是一个空指针 NULL,同样返回指向文件名字符串的指针。

char* filename;
filename = tmpnam(NULL);
1
2

上面示例中,变量filename就是tmpnam()生成的文件名。

该函数只是生成一个文件名,稍后可以使用fopen()打开该文件并使用它。

# fflush()

fflush()用于清空缓存区。它接受一个文件指针作为参数,将缓存区内容写入该文件。

fflush(fp);
1

如果不需要保存缓存区内容,则可以传入空指针 NULL。

fflush(NULL);
1

如果清空成功,fflush()返回0,否则返回 EOF。

注意,fflush()一般只用来清空输出缓存区(比如写文件)。如果使用它来清空输入缓存区(比如读文件),属于未定义行为。

fflush()的一个用途是不等回车键,就强迫输出缓存区。大多数系统都是行缓存,这意味着只有遇到回车键(或者缓存区满了,或者文件读到结尾),缓存区的内容才会输出,fflush()可以不等回车键,立即输出。

for (int i = 9; i >= 0; i--) {
  printf("\r%d", i);
  fflush(stdout);
  sleep(1);
}
1
2
3
4
5

上面示例是一个倒计时效果,\r是回车键,表示每轮循环都会回到当前行的行首,等于删除上一轮循环的输出。fflush(stdout)表示立即将缓存输出到显示器,这一行是必需的,否则由于上一行的输出没有回车键,不会触发缓存输出,屏幕上不会显示任何内容,只会等到程序运行结束再一次性输出。

# setvbuf()

setvbuf()函数用于定义某个字节流应该如何缓存。它可以接受四个参数。

int setvbuf(FILE* stream, char* buffer, int mode, size_t size)
1

第一个参数stream是文件流。

第二个参数buffer是缓存区的地址。

第三个参数mode指定缓存的行为模式,它是下面三个宏之一,这些宏都定义在stdio.h。

  • _IOFBF:满缓存。当缓存为空时,才从流读入数据;当缓存满了,才向流写入数据。一般情况下,这是默认设置。
  • _IOLBF:行缓存。每次从流读入一行数据,或向流写入一行数据,即以行为单位读写缓存。
  • _IONBF:无缓存。不使用缓存区,直接读写设备。

第四个参数size指定缓存区的大小。较大的缓存区提供更好的性能,而较小的缓存区可以节省空间。stdio.h提供了一个宏BUFSIZ,表示系统默认的缓存区大小。

它的意义在于,使得用户可以在打开一个文件之前,定义自己的文件缓冲区,而不必使用fopen()函数打开文件时设定的默认缓冲区。

char buffer[N];

setvbuf(stream, buffer, _IOFBF, N);
1
2
3

上面示例设置文件流stream的缓存区从地址buffer开始,大小为N,模式为_IOFBF。

setvbuf()的第二个参数可以为空指针 NULL。这样的话,setvbuf()会自己创建一个缓存区。

注意,setvbuf()的调用必须在对文件流执行任何操作之前。

如果调用成功,setvbuf()的返回值为0,否则返回非零值。

下面的例子是将缓存区调整为行缓存。

FILE *fp;
char lineBuf[1024];

fp = fopen("somefile.txt", "r");
setvbuf(fp, lineBuf, _IOLBF, 1024);
1
2
3
4
5

# setbuf()

setbuf()是setvbuf()的早期版本,可以视为后者的简化版本,也用来定义某个字节流的缓存区。

void setbuf(FILE* stream, char* buffer);
1

它的第一个参数stream是文件流,第二个参数buffer是缓存区的地址。

它总是可以改写成setvbuf()。

char buffer[BUFSIZ];

setbuf(stream, buffer);

// 等同于
setvbuf(stream, buffer, _IOFBF, BUFSIZ);
1
2
3
4
5
6

上面示例中,BUFSIZ是stdio.h定义的宏,表示系统默认的缓存区大小。

setbuf()函数没有返回值。

setbuf()的第二个参数如果设置为 NULL,表示不进行缓存。

setbuf(stdout, NULL);

// 等同于
setvbuf(stdout, NULL, _IONBF, 0);
1
2
3
4

# ungetc()

ungetc()将从缓存里面读取的上一个字符,重新放回缓存,下一个读取缓存的操作会从这个字符开始。有些操作需要了解下一个字符是什么,再决定应该怎么处理,这时这个函数就很有用。

它的原型如下。

int ungetc(int c, FILE *stream);
1

它的第一个参数是一个字符变量,第二个参数是一个打开的文件流。它的返回值是放回缓存的那个字符,操作失败时,返回 EOF。

int ch = fgetc(fp);

if (isdigit(ch)) {
  ch = fgetc(fp);
}

ungetc(ch, fp);
1
2
3
4
5
6
7

上面示例中,如果读取的字符不是数字,就将其放回缓存。

# perror()

perror()用于在 stderr 的错误信息之前,添加一个自定义字符串。

void perror(const char *s);
1

该函数的参数就是在报错信息前添加的字符串。它没有返回值。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>

int main(void) {
  int x = -1;

  errno = 0;
  float y = sqrt(x);
  if (errno != 0) {
    perror("sqrt error");
    exit(EXIT_FAILURE);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

上面示例中,求-1的平方根,导致报错。头文件errno.h提供宏errno,只要上一步操作出错,这个宏就会设置成非零值。perror()用来在报错信息前,加上sqrt error的自定义字符串。

执行上面的程序,就会得到下面的报错信息。

$ gcc test.c -lm
$ ./a.out
sqrt error: Numerical argument out of domain
1
2
3

# 可变参数操作函数

(1)输出函数

下面是printf()的变体函数,用于按照给定格式,输出函数的可变参数列表(va_list)。

  • vprintf():按照给定格式,输出到控制台,默认是显示器。
  • vfprintf():按照给定格式,输出到文件。
  • vsprintf():按照给定格式,输出到字符串。
  • vsnprintf():按照给定格式,输出到字符串的安全版本。

它们的原型如下,基本与对应的printf()系列函数一致,除了最后一个参数是可变参数对象。

#include <stdio.h>
#include <stdarg.h>
    
int vprintf(
  const char * restrict format,
  va_list arg
);

int vfprintf(
  FILE * restrict stream,
  const char * restrict format,
  va_list arg
);
    
int vsprintf(
  char * restrict s,
  const char * restrict format,
  va_list arg
);
    
int vsnprintf(
  char * restrict s,
  size_t n,
  const char * restrict format,
  va_list arg
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

它们的返回值都为输出的字符数,如果出错,返回负值。

vsprintf()和vsnprintf()的第一个参数可以为 NULL,用来查看多少个字符会被写入。

下面是一个例子。

int logger(char *format, ...) {
  va_list va;
  va_start(va, format);
  int result = vprintf(format, va);
  va_end(va);

  printf("\n");

  return result;
}

// 输出 x = 12 and y = 3.20
logger("x = %d and y = %.2f", x, y);
1
2
3
4
5
6
7
8
9
10
11
12
13

(2)输入函数

下面是scanf()的变体函数,用于按照给定格式,输入可变参数列表 (va_list)。

  • vscanf():按照给定格式,从控制台读取(默认为键盘)。
  • vfscanf():按照给定格式,从文件读取。
  • vsscanf():按照给定格式,从字符串读取。

它们的原型如下,跟对应的scanf()函数基本一致,除了最后一个参数是可变参数对象。

#include <stdio.h>
#include <stdarg.h>
    
int vscanf(
  const char * restrict format,
  va_list arg
);
    
int vfscanf(
  FILE * restrict stream,
  const char * restrict format,
  va_list arg
);
    
int vsscanf(
  const char * restrict s,
  const char * restrict format,
  va_list arg
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

它们返回成功读取的项数,遇到文件结尾或错误,则返回 EOF。

下面是一个例子。

int error_check_scanf(int expected_count, char *format, ...) {
  va_list va;

  va_start(va, format);
  int count = vscanf(format, va);
  va_end(va);

  assert(count == expected_count);

  return count;
}

error_check_scanf(3, "%d, %d/%f", &a, &b, &c);
1
2
3
4
5
6
7
8
9
10
11
12
13
上次更新: 2026/06/10, 11:13:41
14.stdint.h
16.stdlib.h

← 14.stdint.h 16.stdlib.h→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式