C 标准没有在 scanf() 格式中定义这样的可选字符。
GNU lib C 确实以这种方式定义了一个可选的a 指示符(来自scanf 的手册页):
一个可选的a 字符。这与字符串转换一起使用,并且使调用者无需分配相应的缓冲区来保存输入:相反,scanf() 分配了一个足够大的缓冲区,并将该缓冲区的地址分配给相应的指针参数,这应该是一个指向char *变量的指针(这个变量在调用之前不需要初始化)。
调用者随后应在不再需要此缓冲区时free。这是一个 GNU 扩展; C99 使用 a 字符作为转换说明符(它也可以在 GNU 实现中使用)。
手册页的 NOTES 部分说:
如果程序使用gcc -std=c99 或gcc -D_ISOC99_SOURCE 编译(除非还指定了_GNU_SOURCE),则a 修饰符不可用,在这种情况下a 被解释为浮点说明符数字(见上文)。
从 2.7 版开始,glibc 还提供了 m 修饰符,其目的与 a 修饰符相同。 m 修饰符具有以下优点:
http://linux.die.net/man/3/scanf 的在线 linux 手册页仅将此选项记录为:
可选的“m”字符。这与字符串转换(%s、%c、%[)一起使用,并且使调用者无需分配相应的缓冲区来保存输入:相反,scanf() 分配了一个足够大小的缓冲区,并且将此缓冲区的地址分配给对应的指针参数,该参数应该是指向char *变量的指针(调用前不需要初始化此变量)。调用者随后应在不再需要此缓冲区时free(3)。
Posix 标准在其 POSIX.1-2008 版本中记录了此扩展(参见 http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html):
%c、%s 和%[ 转换说明符应接受可选的赋值分配字符m,这将导致分配内存缓冲区以保存包含终止空字符的转换字符串。在这种情况下,对应于转换说明符的参数应该是对指针变量的引用,该指针变量将接收指向已分配缓冲区的指针。系统应分配一个缓冲区,就像调用了malloc() 一样。应用程序应负责在使用后释放内存。如果没有足够的内存来分配缓冲区,该函数应将errno 设置为[ENOMEM] 并导致转换错误。如果函数返回EOF,则在函数返回之前,应释放通过此调用使用赋值分配字符m 为参数成功分配的任何内存。
使用这个扩展,你可以写:
char *p;
scanf("%ms", &p);
导致scanf 解析来自标准输入的单词并分配足够的内存来存储其字符以及终止'\0'。指向已分配数组的指针将存储到 p 中,scanf() 将返回 1,除非无法从 stdin 读取非空白字符。
其他系统完全有可能将m 用于类似的语义或完全用于其他东西。非标准扩展是不可移植的,在标准方法繁琐不切实际或完全不可能的情况下,应非常谨慎地使用并记录在案。
请注意,使用标准版scanf() 确实无法解析任意大小的单词:
您可以解析一个最大大小的单词,并且应该在'\0'之前指定要存储的最大字符数:
char buffer[20];
scanf("%19s", buffer);
但这并不能告诉您在标准输入中还有多少字符可用于解析。在任何情况下,如果输入足够长,未传递最大字符数可能会引发未定义的行为,攻击者甚至可能使用特制的输入来破坏您的程序:
char buffer[20];
scanf("%s", buffer); // potential undefined behavior,
// that could be exploited by an attacker.