【问题标题】:difference between %ms and %s scanf%ms 和 %s scanf 之间的区别
【发布时间】:2016-12-05 18:13:20
【问题描述】:

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

可选的“m”字符。这与字符串转换一起使用 (%s, %c, %[),

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

【问题讨论】:

  • 你以为我只看第一行吗?! -_- 老实说我没看懂!这就是为什么我只想要一个带有简单解释的小例子,用简单的话说明区别?
  • 1) char word[32]; scanf("%31s", word);/* The maximum number of characters in the input is known. */
  • 2) char *word; scanf("%ms", &word);/* input size is unknown. dynamically allocated according to the input. */
  • 我讨厌听起来刺耳,但这是您的问题留下的印象。该段很好地解释了差异。如果您不理解某个特定的短语,为什么不提及它?
  • 我仍然是 C 的初学者,我正在尽我所能理解它。我讨厌阅读 sthg 就这样跳过它!我真的很感激 !非常感谢@BLUEPIXY

标签: c gcc glibc scanf


【解决方案1】:

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

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

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

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

手册页的 NOTES 部分说:

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

从 2.7 版开始,glibc 还提供了 m 修饰符,其目的与 a 修饰符相同。 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'。指向已分配数组的指针将存储到 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.

【讨论】:

  • 它是 POSIX 2013 标准的一部分——因此任何符合该标准的系统都将支持它。
  • 可在线访问here
猜你喜欢
  • 1970-01-01
  • 2022-12-10
  • 1970-01-01
  • 1970-01-01
  • 2020-02-26
  • 1970-01-01
  • 2014-02-14
  • 1970-01-01
相关资源
最近更新 更多