如何理解scanf和printf的第0个参数

2022-10-22

要理解scanf和printf的第0个参数,也就是所谓的格式化字符串,首先就要理解什么是格式化字符串。

什么是格式化字符串

格式化字符串,顾名思义,就是对相应内容的格式进行规定的字符串,放在scanf / printf里,就是对要输入/输出的内容应该是什么形式进行说明的一个模板

知道了格式化字符串的概念,那么格式化字符串在scanf和printf有什么作用呢?下面我们就逐个讨论。

scanf中格式化字符串的作用

scanf(scan+f(unction))的功能顾名思义,就是根据格式化字符串的指示,从标准输入(stdin,通常默认指向键盘缓冲区)缓冲区扫描相应内容写入到其他参数给出的地址的函数。格式化字符串在scanf中的作用,就是告诉scanf,它应该以什么格式解析缓冲区中的内容。

在不与格式化字符串本身的语法冲突的前提下,scanf对格式化字符串的解释符合正则表达式的规则。也就是说,在不与格式化字符串本身的语法冲突的前提下,scanf用格式化字符串作为一个正则表达式去匹配缓冲区中的内容。特别地,scanf将空格(space,ASCII码为0x20)和换行(ASCII码为0x0A)缺省地作为分隔数据的符号。例如”%d%c”与”%d %c”、”%d\n%c”完全等价。

*思考:用scanf接受字符串时,输入的字符串真的的不能带空格吗?

答案显然是否定的。当我们使用”%[^\f\n\r\t\v]“作为格式化字符串时,用scanf接受的输入字符串就可以包含空格了。

scanf不会检查格式化字符串的正确性。当格式化字符串存在错误语法时,变量不会获取到任何数据,因此保持原值。

另外,需要特别注意的是,scanf解析缓冲区中数据的唯一凭据就是格式化字符串。即scanf不会检查格式化字符串中%序列指定的数据型别与对应变量的地址中数据应具有的实际型别是否一致。例如,当格式化字符串为”%d”,而实际的对应变量的型别为char时,当输入48(即0x30,字符0的ASCII值)时,该变量实际值会是’0’。

scanf对与格式化字符串不一致数据的处理

对与格式化字符串不一致的输入,scanf会将其继续留在输入缓冲区中,也就是造成了所谓的输入阻塞,直到这部分数据能在某个scanf中与格式化字符串匹配。

例如当格式化字符串是”%d/%d”,而输入是123.456/789时,第0个变量获取的值实际是123,而第1个变量由于输入阻塞根本没有获取到任何数据,故保持原值。

printf中格式化字符串的作用

printf(print+f(unction))的功能顾名思义,就是根据格式化字符串的指示解析相应参数给出的值并向标准输出(stdout,通常默认指向控制台缓冲区)缓冲区写入内容的函数。格式化字符串在printf中的作用,就是告诉printf,它应该以什么格式解析对应变量中的值。

printf仅按照格式化字符串的语法对格式化字符串进行解释。也就是说,printf中格式化字符串的用法比scanf简单许多。特别地,对于printf,格式化字符串中从前向后每两个连续的%为一个整体表示一个%本身。

需要特别注意的是,printf解析变量的值的唯一凭据就是格式化字符串。即printf不会检查格式化字符串中%序列指定的数据型别与对应变量的实际型别是否一致。例如,当格式化字符串为”%d”,而实际的对应变量的型别为char时,当变量的值为’0’时,实际的输出会是48(即0x30,字符0的ASCII值)。