%ms和%s scanf

时间:2016-07-31 15:51:09

标签: c gcc glibc scanf

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

  

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

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

1 个答案:

答案 0 :(得分:7)

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

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

  

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

     

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

手册页的 NOTES 部分说:

  

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

     

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

     
      
  • 它也可能适用于m转换说明符(例如%c)。

  •   
  • 它避免了%3mc浮点转换说明符的歧义(并且不受%a等影响。)

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

  •   

http://linux.die.net/man/3/scanf的在线linux手册页仅将此选项记录为:

  

可选的'm'字符。这与字符串转换(gcc -std=c99%s%c)一起使用,并使调用者无需分配相应的缓冲区来保存输入:相反,%[分配一个足够大小的缓冲区,并将该缓冲区的地址分配给相应的指针参数,该参数应该是指向scanf()变量的指针(该变量在调用之前不需要初始化)。当不再需要时,调用者应该char *这个缓冲区。

Posix标准在POSIX.1-2008版本中记录了此扩展(见http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html):

  

free(3)%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);