跳转到内容

C 编程/流 I/O

来自 Wikibooks,开放世界中的开放书籍
上一个:错误处理 C 编程 下一个:字符串

stdio.h 头文件声明了大量执行文件和设备(例如控制台)输入和输出的函数。它是 C 库中最早出现的头文件之一。它声明的函数比任何其他标准头文件都多,也需要更多的解释,因为函数背后有复杂的机制。

多年来,设备无关的输入输出模型得到了显著改进,但其成功却鲜为人知。FORTRAN II 在 1960 年代被吹捧为一种机器无关的语言,但实际上,在不进行任何更改的情况下,几乎不可能在体系结构之间迁移 FORTRAN 程序。在 FORTRAN II 中,您需要在 FORTRAN 代码中间的 FORTRAN 语句中直接指定要操作的设备。因此,在面向磁带的 IBM 7090 上,您会写 READ INPUT TAPE 5,而在其他机器上,您会写 READ CARD 来读取卡片图像。FORTRAN IV 有更通用的 READWRITE 语句,指定了逻辑单元号(LUN)而不是设备名称。设备无关 I/O 的时代到来了。

诸如打印机之类的外围设备仍然对所要求执行的操作有相当强的概念。随后,发明了外设交换实用程序来处理奇异的设备。当阴极射线管出现时,每家控制台制造商都独立地解决了诸如控制台光标移动之类的难题,从而带来了更多麻烦。

Unix 就是在这种环境下诞生的。Unix 的开发者肯·汤普森和丹尼斯·里奇应得赞誉,他们在操作系统中融入了无数巧妙的想法。他们对设备无关性的方法是最棒的想法之一。

ANSI C <stdio.h> 库基于原始的 Unix 文件 I/O 原语,但范围更广,以适应各种系统中最常见的特征。

无论是对物理设备(如终端和磁带驱动器)还是对结构化存储设备上的文件进行输入或输出,都映射到逻辑数据流,其属性比各种输入输出更统一。支持两种映射形式:文本流和二进制流。

文本流包含一行或多行。文本流中的一行包含零个或多个字符,外加一个终止换行符。(唯一的例外是,在某些实现中,文件的最后一行不需要终止换行符。)Unix 采用了一种所有文本流的标准内部格式。每行文本都以换行符结尾。这就是任何程序在读取文本时所期望的,也是任何程序在写入文本时所产生的。(这是最基本约定,如果它不能满足连接到 Unix 机器上的面向文本的外设的需求,则修复操作将在系统边缘进行。中间不需要进行任何更改。)进入或离开文本流的字符字符串可能需要修改以符合特定约定。这会导致文本流中输入的数据与输出的数据之间存在可能的差异。例如,在某些实现中,当输入中出现空格字符紧接换行符时,空格字符会从输出中删除。一般来说,当数据仅包含可打印字符和控制字符(如水平制表符和换行符)时,文本流的输入和输出是相等的。

与文本流相比,二进制流非常直观。二进制流是字符的有序序列,可以透明地记录内部数据。写入二进制流的数据将始终等于在相同实现下读取的数据。但是,二进制流可能在流末尾附加了实现定义数量的空字符。没有其他需要考虑的约定。

Unix 中没有任何内容可以阻止程序将任意 8 位二进制代码写入任何打开的文件,或者从适当的存储库中读取它们而不进行更改。因此,Unix 消除了文本流和二进制流之间长期存在的区别。

标准流

[编辑 | 编辑源代码]

当 C 程序开始执行时,程序会自动打开三个名为 stdinstdoutstderr 的标准流。这些流对于每个 C 程序都附加。

第一个标准流用于输入缓冲,另外两个用于输出。这些流是字节序列。

考虑以下程序

 /* An example program. */
 int main()
 {
     int var;
     scanf ("%d", &var); /* use stdin for scanning an integer from keyboard. */
     printf ("%d", var); /* use stdout for printing the integer that was just scanned in. */
     return 0;
 }
 /* end program. */

默认情况下,stdin 指向键盘,stdoutstderr 指向屏幕。在 Unix 下,甚至可能在其他操作系统下,可以将输入从文件重定向,或将输出重定向到文件,或两者兼而有之。

指向流的指针

[编辑 | 编辑源代码]
出于历史原因,表示流的 C 数据结构的类型称为 FILE,而不是 stream

<stdio.h> 头文件包含一个 FILE 类型的定义(通常通过 typedef),该类型能够处理控制流所需的所有信息,包括文件位置指示器、指向相关缓冲区(如果有)的指针、记录是否发生读/写错误的错误指示器以及记录是否已到达文件结尾的文件结束指示器。

除非程序员正在编写 <stdio.h> 及其内容的实现,否则直接访问 FILE 的内容被认为是不好的做法。通过 <stdio.h> 中的函数可以更好地访问 FILE 的内容。可以说,FILE 类型是 面向对象编程 的早期示例。

打开和关闭文件

[编辑 | 编辑源代码]

要打开和关闭文件,<stdio.h> 库提供了三个函数:fopenfreopenfclose

打开文件

[编辑 | 编辑源代码]
 #include <stdio.h>
 FILE *fopen(const char *filename, const char *mode);
 FILE *freopen(const char *filename, const char *mode, FILE *stream);

fopenfreopen 打开名称在 filename 指向的字符串中的文件,并将其与流关联。两者都返回一个指向控制流的对象的指针,或者,如果打开操作失败,则返回一个空指针。错误和文件结束指示器被清除,如果打开操作失败,则设置错误。freopenfopen 的区别在于,stream 指向的文件在已经打开的情况下会被先关闭,并且会忽略任何关闭错误。

这两个函数的 mode 都指向一个字符串,该字符串以以下序列之一开头(序列后可能还有其他字符)

r           open a text file for reading
w           truncate to zero length or create a text file for writing
a           append; open or create text file for writing at end-of-file
rb          open binary file for reading
wb          truncate to zero length or create a binary file for writing
ab          append; open or create binary file for writing at end-of-file
r+          open text file for update (reading and writing)
w+          truncate to zero length or create a text file for update
a+          append; open or create text file for update
r+b or rb+  open binary file for update (reading and writing)
w+b or wb+  truncate to zero length or create a binary file for update
a+b or ab+  append; open or create binary file for update

如果文件不存在或无法读取,则以读取模式(mode 参数中的第一个字符为“r”)打开文件将失败。

以追加模式(mode 参数中的第一个字符为“a”)打开文件会导致所有后续写入文件的操作都强制到当时的当前文件结尾,而与对 fseek 函数的中间调用无关。在某些实现中,以追加模式(mode 参数中的第二个或第三个字符为上述列表中的“b”)打开二进制文件可能会将流的文件位置指示器最初定位到最后一个写入数据的后面,因为有空字符填充。

当以更新模式(mode 参数值列表中的第二个或第三个字符为“+”)打开文件时,可以对关联的流执行输入和输出。但是,输出不能直接跟在输入之后,除非在中间调用 fflush 函数或文件定位函数(fseekfsetposrewind),并且输入不能直接跟在输出之后,除非输入操作遇到文件结尾,除非输入操作遇到文件结尾。在某些实现中,以更新模式打开(或创建)文本文件可能会改为打开(或创建)二进制流。

打开时,当且仅当可以确定流不引用交互式设备时,流才是完全缓冲的。

关闭文件

[编辑 | 编辑源代码]
 #include <stdio.h>
 int fclose(FILE *stream);

fclose 函数会导致 stream 指向的流被刷新,并关闭关联的文件。流的任何未写入的缓冲数据将被传递到主机环境,以写入文件;任何未读取的缓冲数据将被丢弃。流与文件分离。如果关联的缓冲区是自动分配的,则它将被释放。如果流成功关闭,则函数返回零;如果检测到任何错误,则返回 EOF

流缓冲函数

[编辑 | 编辑源代码]

fflush 函数

[编辑 | 编辑源代码]
 #include <stdio.h>
 int fflush(FILE *stream);

如果 stream 指向一个输出流或一个更新流,其中最近的操作不是输入,则 fflush 函数会导致该流的任何未写入的数据被延迟到主机环境,以写入文件。对于输入流,fflush 的行为是未定义的。

如果 stream 是一个空指针,则 fflush 函数会对所有行为如上所述定义的流执行此刷新操作。

如果发生写入错误,fflush 函数返回 EOF,否则返回零。

存在 fflush 函数的原因是,C 中的流可以具有缓冲的输入/输出;也就是说,写入文件的函数实际上写入 FILE 结构内的缓冲区。如果缓冲区已满,写入函数将调用 fflush 以实际将缓冲区中的数据“写入”文件。由于 fflush 只是偶尔被调用一次,因此调用操作系统执行原始写入的次数被最小化了。

setbuf 函数

[编辑 | 编辑源代码]
 #include <stdio.h>
 void setbuf(FILE *stream, char *buf);

除了不返回值之外,setbuf 函数等效于用 _IOFBF 作为 modeBUFSIZ 作为 size 调用 setvbuf 函数,或者(如果 buf 是一个空指针)用 _IONBF 作为 mode 调用 setvbuf 函数。

setvbuf 函数

[编辑 | 编辑源代码]
 #include <stdio.h>
 int setvbuf(FILE *stream, char *buf, int mode, size_t size);

setvbuf 函数只能在 stream 指向的流与一个打开的文件关联之后并且在对流执行任何其他操作之前使用。参数 mode 确定流将如何被缓冲,如下所示:_IOFBF 会导致输入/输出被完全缓冲;_IOLBF 会导致输入/输出被行缓冲;_IONBF 会导致输入/输出不被缓冲。如果 buf 不是一个空指针,则它指向的数组可以代替 setvbuf 函数关联的缓冲区。(缓冲区的生存期必须至少与打开的流一样长,因此在缓冲区具有自动存储期限的缓冲区在块退出时被释放之前,应该关闭流。)参数 size 指定数组的大小。数组中任何时候的内容都是不确定的。

如果成功,setvbuf 函数返回零;如果为 mode 提供了无效值,或者无法满足请求,则返回非零值。

修改文件位置指示器的函数

[编辑 | 编辑源代码]

stdio.h 库有五个函数可以影响文件位置指示器,除了那些执行读写操作的函数:fgetposfseekfsetposftellrewind

fseekftell 函数比 fgetposfsetpos 更早。

fgetposfsetpos 函数

[编辑 | 编辑源代码]
 #include <stdio.h>
 int fgetpos(FILE *stream, fpos_t *pos);
 int fsetpos(FILE *stream, const fpos_t *pos);

fgetpos 函数将 stream 指向的流的文件位置指示器的当前值存储在 pos 指向的对象中。存储的值包含 fsetpos 函数可用于将流重新定位到调用 fgetpos 函数时的位置的未指定信息。

如果成功,fgetpos 函数返回零;如果失败,fgetpos 函数返回非零值,并在 errno 中存储一个实现定义的正值。

fsetpos 函数根据 pos 指向的对象的值设置 stream 指向的流的文件位置指示器,该值应是从先前对相同流的 fgetpos 函数的调用中获得的值。

fsetpos 函数的成功调用会清除流的文件结束指示器,并撤消对相同流的 ungetc 函数的任何影响。在 fsetpos 调用之后,对更新流的下一个操作可以是输入或输出。

如果成功,fsetpos 函数返回零;如果失败,fsetpos 函数返回非零值,并在 errno 中存储一个实现定义的正值。

fseekftell 函数

[编辑 | 编辑源代码]
 #include <stdio.h>
 int fseek(FILE *stream, long int offset, int whence);
 long int ftell(FILE *stream);

fseek 函数设置 stream 指向的流的文件位置指示器。

对于二进制流,新的位置(以文件开头处的字符数衡量)通过将offset添加到whence指定的位置获得。stdio.h中的三个宏称为SEEK_SETSEEK_CURSEEK_END扩展为唯一值。如果whence指定的位置为SEEK_SET,则指定的位置为文件开头;如果whenceSEEK_END,则指定的位置为文件结尾;如果whenceSEEK_CUR,则指定的位置为当前文件位置。二进制流不必有意义地支持whence值为SEEK_ENDfseek调用。

对于文本流,offset应为零,或者offset应为先前对同一流调用ftell函数返回的值,而whence应为SEEK_SET

fseek函数仅对无法满足的请求返回非零值。

ftell函数获取stream指向的流的文件位置指示器的当前值。对于二进制流,该值是从文件开头的字符数;对于文本流,其文件位置指示器包含未指定的信息,可由fseek函数用于将流的文件位置指示器返回到调用ftell时的位置;两个这样的返回值之间的差值不一定是写入或读取的字符数的有效度量。

如果成功,ftell函数将返回流的文件位置指示器的当前值。如果失败,ftell函数将返回-1L,并在errno中存储实现定义的正值。

rewind函数

[edit | edit source]
 #include <stdio.h>
 void rewind(FILE *stream);

rewind函数将stream指向的流的文件位置指示器设置为文件开头。它等效于

(void)fseek(stream, 0L, SEEK_SET)

除了流的错误指示器也被清除。

错误处理函数

[edit | edit source]

clearerr函数

[edit | edit source]
 #include <stdio.h>
 void clearerr(FILE *stream);

clearerr函数清除stream指向的流的文件结尾指示器和错误指示器。

feof函数

[edit | edit source]
 #include <stdio.h>
 int feof(FILE *stream);

feof函数测试stream指向的流的文件结尾指示器,如果且仅当文件结尾指示器为stream设置时,返回非零值,否则返回零。

ferror函数

[edit | edit source]
 #include <stdio.h>
 int ferror(FILE *stream);

ferror函数测试stream指向的流的错误指示器,如果且仅当错误指示器为stream设置时,返回非零值,否则返回零。

perror函数

[edit | edit source]
 #include <stdio.h>
 void perror(const char *s);

perror函数将整数表达式errno中的错误号映射到错误消息。它向标准错误流写入一个字符序列,如下所示:首先,如果s不是空指针,并且s指向的字符不是空字符,则为s指向的字符串,后跟冒号(:) 和一个空格;然后是适当的错误消息字符串,后跟一个换行符。错误消息的内容与使用参数errno调用strerror函数返回的内容相同,这些内容是实现定义的。

其他文件操作

[edit | edit source]

stdio.h库包含各种函数,这些函数除了读写外还会对文件执行一些操作。

remove函数

[edit | edit source]
 #include <stdio.h>
 int remove(const char *filename);

remove函数导致名为filename指向的字符串的文件不再可以通过该名称访问。随后尝试使用该名称打开该文件将失败,除非它被重新创建。如果文件已打开,则remove函数的行为是实现定义的。

如果操作成功,remove函数将返回零,如果失败,则返回非零值。

rename函数

[edit | edit source]
 #include <stdio.h>
 int rename(const char *old_filename, const char *new_filename);

rename函数导致名为old_filename指向的字符串的文件从此以后被称为new_filename指向的字符串给出的名称。名为old_filename的文件不再可以通过该名称访问。如果在调用rename函数之前存在名为new_filename指向的字符串的文件,则行为是实现定义的。

如果操作成功,rename函数将返回零,如果失败,则返回非零值,在这种情况下,如果该文件先前存在,它仍然以其原始名称而闻名。

tmpfile函数

[edit | edit source]
 #include <stdio.h>
 FILE *tmpfile(void);

tmpfile函数创建一个临时二进制文件,该文件将在关闭时或程序终止时自动删除。如果程序异常终止,是否删除打开的临时文件是实现定义的。该文件以"wb+"模式打开以供更新。

tmpfile函数返回一个指向它创建的文件流的指针。如果无法创建文件,则tmpfile函数将返回一个空指针。

tmpnam函数

[edit | edit source]
 #include <stdio.h>
 char *tmpnam(char *s);

tmpnam函数生成一个有效的文件名字符串,该字符串不是现有文件的名称。

tmpnam函数每次调用时都会生成一个不同的字符串,最多TMP_MAX次。(TMP_MAXstdio.h中定义的宏。)如果它被调用超过TMP_MAX次,则行为是实现定义的。

实现的行为就好像没有库函数调用tmpnam函数一样。

如果参数为空指针,则tmpnam函数将其结果留在内部静态对象中,并返回指向该对象的指针。后续对tmpnam函数的调用可能会修改同一个对象。如果参数不是空指针,则假定它指向一个至少包含L_tmpnam个字符的数组(L_tmpnamstdio.h中的另一个宏);tmpnam函数将结果写入该数组,并返回参数作为其值。

TMP_MAX的值必须至少为 25。

从文件读取

[edit | edit source]

字符输入函数

[edit | edit source]

fgetc函数

[编辑 | 编辑源代码]
 #include <stdio.h>
 int fgetc(FILE *stream);

fgetc 函数从由 stream 指向的流中获取下一个字符(如果存在)作为 unsigned char 转换为 int,并推进与流相关的文件位置指示器(如果已定义)。

fgetc 函数返回由 stream 指向的流中的下一个字符。如果流处于文件末尾或发生读取错误,fgetc 返回 EOFEOF 是在 <stdio.h> 中定义的负值,通常为 (-1))。feofferror 必须用于区分文件末尾和错误。如果发生错误,全局变量 errno 将被设置为指示错误。

fgets 函数

[编辑 | 编辑源代码]
 #include <stdio.h>
 char *fgets(char *s, int n, FILE *stream);

fgets 函数从由 stream 指向的流中读取最多比由 n 指定的字符数少一个字符,并将它们写入由 s 指向的数组中。在新行字符(保留)或文件末尾之后不会读取其他字符。一个空字符将写入读取到数组中的最后一个字符之后。

如果成功,fgets 函数返回 s。如果遇到文件末尾且没有字符被读取到数组中,数组的内容将保持不变,并将返回空指针。如果在操作期间发生读取错误,数组内容将不确定,并将返回空指针。

警告:不同的操作系统可能使用不同的字符序列来表示行尾序列。例如,一些文件系统在文本文件中使用终止符 \r\nfgets 可能会读取这些行,删除 \n 但保留 \r 作为 s 的最后一个字符。在使用该字符串进行任何操作之前,应从字符串 s 中删除此伪字符(除非程序员不关心它)。Unix 通常使用 \n 作为其行尾序列,MS-DOS 和 Windows 使用 \r\n,而 Mac OSes 在 OS X 之前使用 \r。许多编译器在除 Unix 或 Linux 之外的操作系统上将换行序列映射到文本文件的 \n 中;检查您的编译器文档以了解它在这种情况下会做什么。

 /* An example program that reads from stdin and writes to stdout */
 #include <stdio.h>

 #define BUFFER_SIZE 100

 int main(void)
 {
     char buffer[BUFFER_SIZE]; /* a read buffer */
     while( fgets (buffer, BUFFER_SIZE, stdin) != NULL)
     {
          printf("%s",buffer);
     }
     return 0;
 }
 /* end program. */

getc 函数

[编辑 | 编辑源代码]
 #include <stdio.h>
 int getc(FILE *stream);

getc 函数等效于 fgetc,不同之处在于它可能被实现为宏。如果它被实现为宏,stream 参数可能被评估不止一次,因此该参数永远不应该是一个具有副作用的表达式(即具有赋值、递增或递减运算符,或是一个函数调用)。

getc 函数返回由 stream 指向的输入流中的下一个字符。如果流处于文件末尾,流的文件末尾指示器将被设置为 getc 并返回 EOFEOF 是在 <stdio.h> 中定义的负值,通常为 (-1))。如果发生读取错误,流的错误指示器将被设置为 getc 并返回 EOF

getchar 函数

[编辑 | 编辑源代码]
 #include <stdio.h>
 int getchar(void);

getchar 函数等效于带有参数 stdingetc

getchar 函数返回由 stdin 指向的输入流中的下一个字符。如果 stdin 处于文件末尾,stdin 的文件末尾指示器将被设置为 getchar 并返回 EOFEOF 是在 <stdio.h> 中定义的负值,通常为 (-1))。如果发生读取错误,stdin 的错误指示器将被设置为 getchar 并返回 EOF

gets 函数

[编辑 | 编辑源代码]
 #include <stdio.h>
 char *gets(char *s);

gets 函数从由 stdin 指向的输入流中读取字符到由 s 指向的数组中,直到遇到文件末尾或读取到换行符。任何换行符都会被丢弃,并且一个空字符将写入读取到数组中的最后一个字符之后。

如果成功,gets 函数返回 s。如果遇到文件末尾且没有字符被读取到数组中,数组的内容将保持不变,并将返回空指针。如果在操作期间发生读取错误,数组内容将不确定,并将返回空指针。

此函数和描述仅出于完整性而包含在此处。如今大多数 C 程序员都避免使用 gets,因为该函数无法知道程序员想要读取到哪个缓冲区有多大。

Henry SpencerC 程序员的十诫(带注释版) 中的诫律 #5 写道

你应该检查所有字符串(实际上是所有数组)的数组边界,因为当你输入 foo 时,有人总有一天会输入 supercalifragilisticexpialidocious

它在注释中提到了 gets

正如大蠕虫的所作所为所证明的那样,这条诫律的一个结果是,稳健的生产软件永远不应该使用 gets(),因为它确实是魔鬼的工具。你的接口应该始终告知你的仆人你的数组的界限,而那些拒绝这种建议或默默地不遵守这种建议的仆人,应该立即被派遣到 Rm 之地,在那里他们不会再对你造成伤害。

在 2018 年版的 C 标准之前,gets 函数已被弃用。希望程序员会改用 fgets 函数。

ungetc 函数

[编辑 | 编辑源代码]
 #include <stdio.h>
 int ungetc(int c, FILE *stream);

ungetc 函数将由 c 指定的字符(转换为 unsigned char)推回到由 stream 指向的输入流中。推回的字符将按它们被推回的相反顺序由该流上的后续读取返回。对指向 stream 的流的成功干预调用(使用文件定位函数(fseekfsetposrewind))将丢弃该流的任何推回字符。与流相对应的外部存储将保持不变。

保证有一个字符的回退。如果在对同一个流进行干预读取或文件定位操作之前,ungetc 函数在同一个流上被调用了太多次,操作可能会失败。

如果 c 的值等于宏 EOF 的值,则操作将失败,输入流将保持不变。

ungetc 函数的成功调用将清除流的文件末尾指示器。在读取或丢弃所有推回的字符后,流的文件位置指示器的值应与其在字符被推回之前的值相同。对于文本流,在成功调用 ungetc 函数后,其文件位置指示器的值在所有推回的字符被读取或丢弃之前是不确定的。对于二进制流,其文件位置指示器在每次成功调用 ungetc 函数时都会递减;如果其值在调用之前为零,则调用后它将是不确定的。

ungetc 函数在转换后返回推回的字符,或者在操作失败时返回 EOF

EOF 陷阱

[编辑 | 编辑源代码]

使用 fgetcgetcgetchar 时,一个错误是将结果分配给 char 类型的变量EOF 进行比较之前。以下代码片段展示了这个错误,然后显示了正确的方法(使用 int 类型)

错误 更正
char c;
while ((c = getchar()) != EOF)
    putchar(c);
int c;
while ((c = getchar()) != EOF)
    putchar(c);

考虑一个系统,其中 char 类型是 8 位宽,表示 256 个不同的值。getchar 可以返回 256 个可能的字符中的任何一个,它也可以返回 EOF 来指示文件末尾,总共 257 个不同的可能返回值。

getchar的结果被赋值给一个char时,char只能表示 256 个不同的值,因此必然会丢失一些信息——当把 257 个项目塞进 256 个槽位时,必然会发生碰撞EOF 值在转换为 char 时,将无法与 256 个字符中的任何一个区分开来,因为它们具有相同的数值。如果该字符在文件中找到,上面的示例可能会将其误认为是文件结尾指示符;或者,同样糟糕的是,如果类型 char 是无符号的,那么由于 EOF 是负数,它永远不可能等于任何无符号 char,因此上面的示例不会在文件结尾处终止。它将无限循环,重复打印将 EOF 转换为 char 所产生的字符。

然而,如果 char 定义是有符号的(C 使默认 char 类型实现取决于实现),[1] 并且假设通常使用的 EOF 值为 -1,则不会发生这种循环失败模式。但是,根本问题仍然存在,如果 EOF 值定义在 char 类型范围之外,当它被赋值给 char 时,该值会被切片,并且不再与退出循环所需的完整 EOF 值匹配。另一方面,如果 EOFchar 的范围内,这保证了 EOF 和 char 值之间的碰撞。因此,无论系统类型如何定义,在测试 EOF 时永远不要使用 char 类型。

intchar 大小相同的系统上(即与 POSIX 和 C99 标准不兼容的系统),即使是“良好”的示例也会受到 EOF 和某些字符值的无法区分的影响。处理这种情况的正确方法是在 getchar 返回 EOF 后检查 feofferror。如果 feof 指示尚未到达文件结尾,并且 ferror 指示没有发生错误,那么 getchar 返回的 EOF 可以被认为代表一个实际的字符。这些额外的检查很少进行,因为大多数程序员假设他们的代码永远不需要在这些“大 char”系统上运行。另一种方法是使用编译时断言来确保 UINT_MAX > UCHAR_MAX,这至少可以防止具有这种假设的程序在这样的系统上编译。


直接输入函数:fread 函数

[edit | edit source]
 #include <stdio.h>
 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

fread 函数从 stream 指向的流中读取最多 nmemb 个元素,这些元素的大小由 size 指定,并写入 ptr 指向的数组中。流的文件位置指示器(如果已定义)将根据成功读取的字符数进行前进。如果发生错误,流的文件位置指示器的结果值将是不确定的。如果读取部分元素,其值将是不确定的。

fread 函数返回成功读取的元素数,如果遇到读取错误或文件结尾,则该数可能小于 nmemb。如果 sizenmemb 为零,fread 将返回零,数组的内容和流的状态将保持不变。

格式化输入函数:scanf 函数系列

[edit | edit source]
 #include <stdio.h>
 int fscanf(FILE *stream, const char *format, ...);
 int scanf(const char *format, ...);
 int sscanf(const char *s, const char *format, ...);

fscanf 函数从 stream 指向的流中读取输入,在 format 指向的字符串的控制下,该字符串指定允许的序列以及如何转换它们以进行赋值,使用后续参数作为指向接收转换输入的对象的指针。如果格式参数不足,则行为未定义。如果格式用尽而参数仍然存在,则会评估多余的参数(与往常一样),但会被忽略。

格式应为多字节字符序列,以其初始移位状态开始和结束。格式由零个或多个指令组成:一个或多个空白字符;一个普通的多字节字符(既不是%也不是空白字符);或一个转换说明符。每个转换说明符都以字符%开头。在%之后,以下内容按顺序出现

  • 一个可选的赋值抑制字符*.
  • 一个可选的非零十进制整数,指定最大字段宽度。
  • 一个可选的h, l(ell)或L指示接收对象的尺寸。转换说明符d, in应以h开头,如果相应的参数是指向 short int 的指针而不是指向 int 的指针,或者以l开头,如果它是指向 long int 的指针。类似地,转换说明符o, ux应以h应以l开头,如果相应的参数是指向 unsigned short int 的指针而不是 unsigned int 的指针,或者以开头,如果它是指向 unsigned long int 的指针。最后,转换说明符, ef应以lgL应以h, l开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以L开头,如果它是指向 long double 的指针。如果

与任何其他格式说明符一起出现,则行为未定义。

指定要应用的转换类型的字符。有效的转换说明符如下所述。

fscanf 函数依次执行格式的每个指令。如果指令失败,如以下所述,fscanf 函数将返回。失败被描述为输入失败(由于输入字符不可用)或匹配失败(由于输入不合适)。

由空白字符组成的指令通过读取输入直到第一个非空白字符(保持未读)或直到没有更多字符保持未读来执行。

一个普通多字节字符的指令通过读取流的下一个字符来执行。如果其中一个字符与指令中的一个字符不同,则指令失败,并且不同的字符和后续字符将保持未读。[, 一个转换说明符指令定义了一组匹配的输入序列,如以下对每个说明符的描述。转换说明符按以下步骤执行开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以n除非说明符包含

cn说明符,否则将跳过输入空白字符(如 isspace 函数所指定)。(空白字符不会计入指定的字段宽度。)

除非说明符包含%说明符,否则将从流中读取一个输入项。输入项被定义为最长的匹配输入字符序列,除非它超过了指定的字段宽度,在这种情况下,它将是该序列中长度为初始子序列。如果存在,则输入项后的第一个字符将保持未读。如果输入项的长度为零,则指令的执行将失败;此情况是匹配失败,除非错误阻止了流的输入,在这种情况下,它是输入失败。除了说明符的情况外,输入项(或在*%n

指令的情况下,输入字符数)将转换为适合转换说明符的类型。如果输入项不是匹配序列,则指令的执行将失败;此情况是匹配失败。除非由

d
指示赋值抑制,否则转换结果将放在 format 参数后的第一个尚未接收转换结果的参数所指向的对象中。如果此对象没有合适的类型,或者转换结果不能在提供的空间中表示,则行为未定义。
i
以下转换说明符有效
o
匹配一个可选的带符号十进制整数,其格式与 strtol 函数的主题序列(使用 10 作为 base 参数的值)的预期格式相同。相应的参数应是指向整数的指针。
u
匹配一个可选的带符号整数,其格式与 strtol 函数的主题序列(使用 0 作为 base 参数的值)的预期格式相同。相应的参数应是指向整数的指针。
x
匹配一个可选的带符号八进制整数,其格式与 strtoul 函数的主题序列(使用 8 作为 base 参数的值)的预期格式相同。相应的参数应是指向无符号整数的指针。
开头,如果它是指向 unsigned long int 的指针。最后,转换说明符, e, f
匹配一个可选的带符号十进制整数,其格式与 strtoul 函数的主题序列(使用 10 作为 base 参数的值)的预期格式相同。相应的参数应是指向无符号整数的指针。
匹配一个可选的带符号十六进制整数,其格式与 strtoul 函数的主题序列(使用 16 作为 base 参数的值)的预期格式相同。相应的参数应是指向无符号整数的指针。
匹配一个可选的带符号浮点数,其格式与 strtod 函数的主题字符串的预期格式相同。相应的参数将是指向浮点数的指针。
[
s]匹配一个非空白字符序列。(没有为多字节字符提供特殊规定。)相应的参数应是指向足够大的数组的初始字符的指针,以容纳该序列和一个终止空字符,该字符将被自动添加。^匹配来自一组预期字符(扫描集)的非空字符序列(没有为多字节字符提供特殊规定)。相应的参数应是指向足够大的数组的初始字符的指针,以容纳该序列和一个终止空字符,该字符将被自动添加。转换说明符包含 format 字符串中的所有后续字符,直到且包括匹配的右括号([])。括号之间的字符(扫描列表)构成扫描集,除非左括号后的字符是插入符([^]),在这种情况下,扫描集包含所有不在插入符和右括号之间的扫描列表中的字符。如果转换说明符以-字符在扫描列表中,并且不是第一个字符,也不是第一个字符为 a 的第二个字符。^,也不是最后一个字符,行为是实现定义的。
一个转换说明符指令定义了一组匹配的输入序列,如以下对每个说明符的描述。转换说明符按以下步骤执行
匹配由字段宽度指定的数字(如果指令中没有字段宽度,则为 1)的字符序列(对于多字节字符没有特殊规定)。相应的参数应是指向足够大的数组的初始字符的指针,以接受该序列。不会添加空字符。
p
匹配一组实现定义的序列,这组序列应与函数%p转换可能产生的序列相同。相应的参数应是指向 void 的指针。输入的解释是实现定义的。如果输入项是同一程序执行期间先前转换的值,则结果指针应与该值相等;否则,%p转换的行为是未定义的。
n
不使用任何输入。相应的参数应是指向整数的指针,该整数将写入迄今为止通过此调用 fscanf 函数从输入流中读取的字符数。执行除了指令不会增加在执行 fscanf 函数完成时返回的赋值计数。
%
匹配单个%;不会发生转换或赋值。完整的转换说明应为%%.

如果转换说明无效,则行为未定义。

转换说明符E, GX也是有效的,并且分别与开头,如果它是指向 unsigned long int 的指针。最后,转换说明符, fx.

如果在输入期间遇到文件结束符,则转换将终止。如果在读取与当前指令匹配的任何字符(除了允许的领先空白字符)之前发生文件结束符,则当前指令的执行将以输入失败终止;否则,除非当前指令的执行以匹配失败终止,否则以下指令(如果有)的执行将以输入失败终止。

如果转换在冲突的输入字符上终止,则有问题的输入字符将留在输入流中未读取。尾随空白字符(包括换行符)将保留未读取,除非与指令匹配。除通过除了指令外,无法直接确定文字匹配和抑制赋值的成功与否。

如果在任何转换之前发生输入失败,fscanf 函数将返回宏 EOF 的值。否则,fscanf 函数将返回分配的输入项数,这可能少于提供的项,甚至为零,如果发生早期匹配失败。

scanf 函数等效于 fscanf,但参数 stdin 插入在 scanf 的参数之前。它的返回值类似于 fscanf

sscanf 函数等效于 fscanf,只是参数 s 指定要从中获取输入的字符串,而不是来自流。到达字符串的末尾等效于遇到 fscanf 函数的文件结束符。如果在重叠的对象之间进行复制,则行为未定义。

写入文件

[edit | edit source]

字符输出函数

[edit | edit source]

fputc 函数

[edit | edit source]
#include <stdio.h>
int fputc(int c, FILE *stream);

fputc 函数将 c 指定的字符(转换为 unsigned char)写入 stream 指向的流,位置由关联的文件位置指示器(如果定义)指示,并适当地向前移动指示器。如果文件不支持定位请求,或者流以追加模式打开,则字符将追加到输出流。该函数将返回写入的字符,除非发生写入错误,在这种情况下,流的错误指示器将被设置,并且 fputc 将返回 EOF

fputs 函数

[edit | edit source]
#include <stdio.h>
int fputs(const char *s, FILE *stream);

fputs 函数将 s 指向的字符串写入 stream 指向的流。不会写入终止的空字符。如果发生写入错误,该函数将返回 EOF,否则它将返回一个非负值。

putc 函数

[edit | edit source]
#include <stdio.h>
int putc(int c, FILE *stream);

putc 函数等效于 fputc,只是如果它被实现为宏,它可能多次评估 stream,因此参数永远不应是具有副作用的表达式。该函数将返回写入的字符,除非发生写入错误,在这种情况下,流的错误指示器将被设置,并且该函数将返回 EOF

putchar 函数

[edit | edit source]
#include <stdio.h>
int putchar(int c);

putchar 函数等效于带有第二个参数 stdoutputc。它将返回写入的字符,除非发生写入错误,在这种情况下,stdout 的错误指示器将被设置,并且该函数将返回 EOF

puts 函数

[edit | edit source]
#include <stdio.h>
int puts(const char *s);

puts 函数将 s 指向的字符串写入 stdout 指向的流,并在输出中附加一个换行符。不会写入终止的空字符。如果发生写入错误,该函数将返回 EOF;否则,它将返回一个非负值。

直接输出函数:fwrite 函数

[edit | edit source]
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

fwrite 函数从 ptr 指向的数组中写入,最多 nmemb 个大小由 size 指定的元素到 stream 指向的流。流的文件位置指示器(如果定义)将按成功写入的字符数向前移动。如果发生错误,流的文件位置指示器的结果值是不确定的。该函数将返回成功写入的元素数,这将小于 nmemb,只有在遇到写入错误时才会这样。

格式化输出函数:printf 函数系列

[edit | edit source]
#include <stdarg.h>
#include <stdio.h>
int fprintf(FILE *stream, const char *format, ...);
int printf(const char *format, ...);
int sprintf(char *s, const char *format, ...);
int vfprintf(FILE *stream, const char *format, va_list arg);
int vprintf(const char *format, va_list arg);
int vsprintf(char *s, const char *format, va_list arg);

注意:一些长度说明符和格式说明符是在 C99 中新增的。这些可能在旧的编译器和 stdio 库版本中不可用,这些版本遵循 C89/C90 标准。在可能的情况下,新说明符将用 (C99) 标记。

fprintf 函数在 format 指向的字符串的控制下,将输出写入 stream 指向的流,该字符串指定如何为输出转换后续参数。如果参数不足以满足格式,则行为未定义。如果格式在参数仍存在的情况下耗尽,则将评估多余的参数(与往常一样),但其他方面将被忽略。fprintf 函数在遇到格式字符串的结尾时返回。

格式应为多字节字符序列,在初始移位状态下开始和结束。格式由零个或多个指令组成:普通多字节字符(不是%),这些字符将原样复制到输出流中;以及转换说明,每个说明都将导致获取零个或多个后续参数,如果适用,则根据相应的转换说明符进行转换,然后将结果写入输出流。

每个转换说明都由字符%开头。在%之后,以下内容按顺序出现

  • 零个或多个标志(以任何顺序),它们修改转换说明的含义。
  • 可选的最小字段宽度。如果转换后的值的字符数少于字段宽度,则将在左侧(如果已给出左侧调整标志,如后所述,则在右侧)用空格(默认情况下)填充到字段宽度。字段宽度采用星号形式*(稍后描述)或十进制整数。(请注意,0 被视为一个标志,而不是一个字段宽度的开始。)
  • 可选的精度,它给出要显示的最小位数d, i, o, u, xX转换、显示在小数点字符后面的位数a, A, 开头,如果它是指向 unsigned long int 的指针。最后,转换说明符, E, eF转换、对fG转换的有效位数的最大值,或从字符串中写入的最大字符数匹配一个可选的带符号十六进制整数,其格式与 strtoul 函数的主题序列(使用 16 作为 base 参数的值)的预期格式相同。相应的参数应是指向无符号整数的指针。转换。精度采用句点(.)后跟星号*(稍后描述)或可选的十进制整数;如果只指定句点,则精度将取为零。如果精度与任何其他转换说明符一起出现,则行为未定义。浮点数将舍入以适合精度;即printf("%1.1f\n", 1.19);产生1.2.
  • 可选的长度修饰符,它指定参数的大小。
  • 指定要应用的转换类型的转换说明符字符。

如上所述,字段宽度或精度或两者都可以用星号 (*) 表示。在这种情况下,int 参数提供字段宽度或精度。指定字段宽度或精度或两者的参数应(按此顺序)出现在要转换的参数(如果有)之前。负字段宽度参数被视为-标志后跟一个正字段宽度。负精度参数被视为精度被省略。

标志字符及其含义如下:

-
转换结果在字段中左对齐。(如果未指定此标志,则右对齐。)
+
带符号转换的结果始终以加号或减号开头。(如果未指定此标志,则仅当转换负值时,它才以符号开头。负零的所有浮点转换的结果以及舍入为零的负值的结果都包含减号。)
空格
如果带符号转换的第一个字符不是符号,或者带符号转换导致没有字符,则在结果之前添加一个空格。如果空格和+标志都出现,则忽略空格标志。
#
结果将转换为“备用形式”。对于o转换,如果且仅当必要时,它会增加精度以强制结果的第一个数字为零(如果值和精度都为 0,则打印单个 0)。对于x(或X)转换,非零结果将0x(或0X)作为前缀添加。对于a, A, 开头,如果它是指向 unsigned long int 的指针。最后,转换说明符, E, e, F, fG转换,结果始终包含小数点字符,即使没有数字跟随它。(通常,只有在数字跟随它时,这些转换的结果才会出现小数点字符。)对于fG转换,尾随零不会从结果中删除。对于其他转换,行为未定义。
0
对于d, i, o, u, x, X, a, A, 开头,如果它是指向 unsigned long int 的指针。最后,转换说明符, E, e, F, fG转换,前导零(在任何符号或基数指示之后)用于填充到字段宽度;不执行空格填充。如果0-标志都出现,则0标志被忽略。对于d, i, o, u, xX转换,如果指定了精度,则0标志被忽略。对于其他转换,行为未定义。

长度修饰符及其含义如下:

hh
(C99)指定以下d, i, o, u, x开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以X转换说明符应用于 signed charunsigned char 参数(参数将根据整数提升进行提升,但其值应在打印之前转换为 signed charunsigned char);或者以下n转换说明符应用于指向 signed char 参数的指针。
h
指定以下d, i, o, u, x开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以X转换说明符应用于 short intunsigned short int 参数(参数将根据整数提升进行提升,但其值应在打印之前转换为 short intunsigned short int);或者以下n转换说明符应用于指向 short int 参数的指针。
l(ell)
指定以下d, i, o, u, x开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以X转换说明符应用于 long intunsigned long int 参数;以下n转换说明符应用于指向 long int 参数的指针;(C99)以下一个转换说明符指令定义了一组匹配的输入序列,如以下对每个说明符的描述。转换说明符按以下步骤执行转换说明符应用于 wint_t 参数;(C99)以下匹配一个可选的带符号十六进制整数,其格式与 strtoul 函数的主题序列(使用 16 作为 base 参数的值)的预期格式相同。相应的参数应是指向无符号整数的指针。转换说明符应用于指向 wchar_t 参数的指针;或者对以下a, A, 开头,如果它是指向 unsigned long int 的指针。最后,转换说明符, E, e, F, f开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以G转换说明符没有影响。
ll(ell-ell)
(C99)指定以下d, i, o, u, x开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以X转换说明符应用于 long long intunsigned long long int 参数;或者以下n转换说明符应用于指向 long long int 参数的指针。
j
(C99)指定以下d, i, o, u, x开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以X转换说明符应用于 intmax_tuintmax_t 参数;或者以下n转换说明符应用于指向 intmax_t 参数的指针。
z
(C99)指定以下d, i, o, u, x开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以X转换说明符应用于 size_t 或相应的带符号整数类型参数;或者以下n转换说明符应用于指向与 size_t 参数对应的带符号整数类型的指针。
t
(C99)指定以下d, i, o, u, x开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以X转换说明符应用于 ptrdiff_t 或相应的无符号整数类型参数;或者以下n转换说明符应用于指向 ptrdiff_t 参数的指针。
L
指定以下a, A, 开头,如果它是指向 unsigned long int 的指针。最后,转换说明符, E, e, F, f开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以G转换说明符应用于 long double 参数。

如果长度修饰符与除上面指定的以外的任何转换说明符一起出现,则行为未定义。

转换说明符及其含义如下:

d, i
int 参数将转换为带符号十进制,格式为 []dddd。精度指定要出现的最小位数;如果要转换的值可以用更少的位数表示,则它将用前导零扩展。默认精度为 1。将精度为零的零值转换的结果为无字符。
o, u, x, X
unsigned int 参数将转换为无符号八进制(o),无符号十进制(u)或无符号十六进制表示法(x)。括号之间的字符(扫描列表)构成扫描集,除非左括号后的字符是插入符(X)格式为 dddd;字母abcdef用于x转换和字母ABCDEF用于X转换。精度指定要出现的最小位数;如果要转换的值可以用更少的位数表示,则它将用前导零扩展。默认精度为 1。将精度为零的零值转换的结果为无字符。
e, F
表示(有限)浮点数的 double 参数将转换为十进制表示法,格式为 []ddd.ddd,其中小数点字符后的位数等于精度规范。如果精度缺失,则默认为 6;如果精度为零且#标志未指定,则不出现小数点字符。如果出现小数点字符,则在其之前至少出现一位数字。该值将舍入到适当的位数。
(C99)表示无穷大的 double 参数将转换为以下格式之一 [-]inf[-]infinity— 哪个格式由实现定义。表示 NaN 的 double 参数将转换为以下格式之一 [-]nan[-]nan(n-char-sequence)— 哪个格式以及任何 n-char-sequence 的含义由实现定义。该F转换说明符生成INF, INFINITY开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以NAN而不是inf, infinity开头,如果相应的参数是指向 double 的指针而不是指向 float 的指针,或者以nan,分别。(当应用于无穷大和 NaN 值时,该-, +,和 空格 标志具有其通常的含义;该#0标志没有效果。)
开头,如果它是指向 unsigned long int 的指针。最后,转换说明符, E
表示(有限)浮点数的 double 参数将转换为以下格式 []d.ddddd,其中小数点字符之前有一位数字(如果参数非零,则该数字非零),小数点字符之后出现的位数等于精度;如果精度缺失,则默认为 6;如果精度为零且#标志未指定,则不出现小数点字符。该值将舍入到适当的位数。该E转换说明符生成一个带有E而不是开头,如果它是指向 unsigned long int 的指针。最后,转换说明符引入指数。指数始终包含至少两位数字,并且仅包含表示指数所需的更多位数。如果值为零,则指数为零。
(C99)表示无穷大或 NaN 的 double 参数将转换为e)。括号之间的字符(扫描列表)构成扫描集,除非左括号后的字符是插入符(F转换说明符没有影响。
f, G
表示(有限)浮点数的 double 参数将转换为以下格式e)。括号之间的字符(扫描列表)构成扫描集,除非左括号后的字符是插入符(开头,如果它是指向 unsigned long int 的指针。最后,转换说明符(或以以下格式F)。括号之间的字符(扫描列表)构成扫描集,除非左括号后的字符是插入符(EG转换说明符的情况下),精度指定有效数字的个数。如果精度为零,则默认为 1。使用的格式取决于转换的值;格式开头,如果它是指向 unsigned long int 的指针。最后,转换说明符(或E)仅在从这种转换产生的指数小于 –4 或大于或等于精度时使用。尾随零将从结果的小数部分删除,除非#标志指定;只有在小数点字符后跟有数字时才会出现小数点字符。
(C99)表示无穷大或 NaN 的 double 参数将转换为e)。括号之间的字符(扫描列表)构成扫描集,除非左括号后的字符是插入符(F转换说明符没有影响。
a, A
(C99)表示(有限)浮点数的 double 参数将转换为以下格式 []0xh.hhhhd,其中小数点字符之前有一位十六进制数字(如果参数是标准化的浮点数,则该数字非零,否则未指定),小数点字符之后出现的十六进制数字个数等于精度;如果精度缺失并且 FLT_RADIX 是 2 的幂,则精度足以精确表示该值;如果精度缺失并且 FLT_RADIX 不是 2 的幂,则精度足以区分(精度 p 足以区分源类型的值,如果 16p–1 > bn,其中 bFLT_RADIX,而 n 是源类型有效数字中的以 b 为基数的位数。取决于实现确定小数点字符左侧数字的方案,较小的 p 可能就足够了。)类型 double 的值,但尾随零可能会被省略;如果精度为零且#标志未指定,则不出现小数点字符。字母abcdef用于a转换和字母ABCDEF用于A转换。该A转换说明符生成一个带有XP而不是xp。指数始终包含至少一位数字,并且仅包含表示 2 的十进制指数所需的更多位数。如果值为零,则指数为零。
表示无穷大或 NaN 的 double 参数将转换为e)。括号之间的字符(扫描列表)构成扫描集,除非左括号后的字符是插入符(F转换说明符没有影响。
一个转换说明符指令定义了一组匹配的输入序列,如以下对每个说明符的描述。转换说明符按以下步骤执行
如果没有l长度修饰符存在,则 int 参数将转换为 unsigned char,并将生成的字符写入。
(C99)如果l长度修饰符存在,则 wint_t 参数将转换为ls转换规范,没有精度,并且参数指向包含 wint_t 参数的两个元素的 wchar_t 数组的初始元素,第一个元素包含 wint_t 参数到lc转换规范,第二个元素是空宽字符。
匹配一个可选的带符号十六进制整数,其格式与 strtoul 函数的主题序列(使用 16 作为 base 参数的值)的预期格式相同。相应的参数应是指向无符号整数的指针。
如果没有l如果存在长度修饰符,则参数应是指向字符类型数组的初始元素的指针。(对于多字节字符没有特殊规定。)从数组中写入字符,直到(但不包括)终止空字符。如果指定了精度,则最多写入那么多字符。如果未指定精度或精度大于数组大小,则数组应包含一个空字符。
(C99)如果l如果存在长度修饰符,则参数应是指向 wchar_t 类型数组的初始元素的指针。数组中的宽字符被转换为多字节字符(每个字符都像调用 wcrtomb 函数一样,转换状态由一个在第一个宽字符转换之前初始化为零的 mbstate_t 对象描述),直到并包括终止空宽字符。生成的 多字节字符被写入,直到(但不包括)终止空字符(字节)。如果没有指定精度,则数组应包含一个空宽字符。如果指定了精度,则最多写入那么多字符(字节)(包括任何移位序列),并且数组应包含一个空宽字符,如果要等于由精度给出的多字节字符序列长度,该函数将需要访问数组末尾的一个宽字符。在任何情况下都不会写入部分多字节字符。(如果多字节字符具有状态相关编码,则可能会导致冗余的移位序列。)
p
参数应是指向 void 的指针。指针的值以实现定义的方式转换为可打印字符序列。
n
参数应是指向有符号整数的指针,该指针写入到目前为止对该 fprintf 调用写入到输出流中的字符数。没有参数被转换,但会消耗一个参数。如果转换说明包含任何标志、字段宽度或精度,则行为未定义。
%
A%写入一个字符。没有参数被转换。完整的转换说明应为%%.

如果转换说明无效,则行为未定义。如果任何参数的类型与相应的转换说明不匹配,则行为未定义。

在任何情况下,不存在或小的字段宽度都不会导致字段截断;如果转换的结果比字段宽度更宽,则字段会扩展以包含转换结果。

对于aA转换,如果 FLT_RADIX 是 2 的幂,则该值将被正确舍入为具有给定精度的十六进制浮点数。

建议的做法是,如果 FLT_RADIX 不是 2 的幂,则结果应为具有给定精度的十六进制浮点样式中的两个相邻数字之一,并额外规定错误应具有当前舍入方向的正确符号。

建议的做法是,对于开头,如果它是指向 unsigned long int 的指针。最后,转换说明符, E, e, F, fG转换,如果有效小数位数最多为 DECIMAL_DIG,则结果应被正确舍入。(对于二进制到十进制的转换,结果格式的值是可以使用给定格式说明符表示的数字。有效位数由格式说明符决定,对于定点转换,还由源值决定。)如果有效小数位数超过 DECIMAL_DIG 但源值可以用 DECIMAL_DIG 位数精确表示,则结果应为带有尾随零的精确表示。否则,源值将被两个相邻的十进制字符串 L < U 限制,两者都具有DECIMAL_DIG有效位数;结果十进制字符串 D 的值应满足 L ≤ D ≤ U,并额外规定错误应具有当前舍入方向的正确符号。

fprintf 函数返回传输的字符数,如果发生输出或编码错误,则返回负值。

printf 函数等效于 fprintf,只是在 printf 的参数之前插入了参数 stdout。它返回传输的字符数,如果发生输出错误,则返回负值。

sprintf 函数等效于 fprintf,不同之处在于参数 s 指定一个数组,生成的输入将写入该数组,而不是写入流。在写入的字符末尾写入一个空字符;它不计入返回的总和。如果在重叠的对象之间进行复制,则行为未定义。该函数返回写入数组中的字符数,不包括终止空字符。

vfprintf 函数等效于 fprintf,只是可变参数列表被 arg 替换,arg 应已由 va_start 宏(以及可能随后的 va_arg 调用)初始化。vfprintf 函数不会调用 va_end 宏。该函数返回传输的字符数,如果发生输出错误,则返回负值。

vprintf 函数等效于 printf,只是可变参数列表被 arg 替换,arg 应已由 va_start 宏(以及可能随后的 va_arg 调用)初始化。vprintf 函数不会调用 va_end 宏。该函数返回传输的字符数,如果发生输出错误,则返回负值。

vsprintf 函数等效于 sprintf,只是可变参数列表被 arg 替换,arg 应已由 va_start 宏(以及可能随后的 va_arg 调用)初始化。vsprintf 函数不会调用 va_end 宏。如果在重叠的对象之间进行复制,则行为未定义。该函数返回写入数组中的字符数,不包括终止空字符。


参考资料

[edit | edit source]
  1. C99 §6.2.5/15
上一个:错误处理 C 编程 下一个:字符串
华夏公益教科书