%ms和%s scanf之间的差异

阅读scanf手册我遇到这一行:

一个可选的’m’字符。 这用于字符串转换(%s,%c,%[),

有人可以用简单的例子来解释它,在某些情况下说明这种选择的区别和需要吗?

C标准没有在scanf()格式中定义这样的可选字符。

GNU lib C确实a这种方式定义了一个可选a指示符(来自scanf的手册页):

一个可选a角色。 这与字符串转换一起使用,并且释放调用者需要分配相应的缓冲区来保存输入:相反, scanf()分配足够大小的缓冲区,并将此缓冲区的地址分配给相应的指针参数,应该是一个指向char *变量的指针(这个变量不需要在调用之前初始化)。

当不再需要时,调用者应该随后free该缓冲区。 这是一个GNU扩展; C99使用a字符作为转换说明符(它也可以在GNU实现中使用)。

手册页的NOTES部分说:

如果程序是使用gcc -std=c99gcc -D_ISOC99_SOURCE编译的,则a修饰符不可用(除非还指定了_GNU_SOURCE ),在这种情况下, a被解释为浮点数的说明符(见上文)。

从版本2.7开始,glibc还提供了m修饰符,其用途与修饰符相同。 m修饰符具有以下优点:

  • 它也可以应用于%c转换说明符(例如, %3mc )。

  • 它避免了%a浮点转换说明符的模糊性(并且不受gcc -std=c99等的影响)

  • 它在即将发布的POSIX.1标准版本中指定。

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' 。 指向已分配数组的指针将存储到pscanf()将返回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.