【问题标题】:NULL arg allowed to sscanf?NULL arg 允许 sscanf?
【发布时间】:2011-03-02 14:11:46
【问题描述】:

是否允许在调用sscanf 时将NULL 指针作为字符串存储结果?我在任何文档中都没有找到任何关于它的信息,但它似乎工作正常。与scanf 相同。

例子:

int main(int arc, char* argv[])
{
  char* s = NULL;
  sscanf("Privjet mir!", "%s", s);
  printf("s: %s\n", s);
  return 0;
}

输出:s: (null)

【问题讨论】:

    标签: c scanf


    【解决方案1】:

    没有:

    匹配一个非空白序列 人物;下一个指针必须是 指向字符数组的指针 足够长的时间来保存输入序列 和终止的空字符 ('\0'),它是自动添加的。 输入字符串在空白处停止 或在最大字段宽度, 以先发生者为准。

    (http://linux.die.net/man/3/sscanf)

    【讨论】:

    • 这个答案也告诉我们,禁止null不需要明确提及“null”这个词。声明某事必须是“指向 的指针”隐含地禁止 null。
    【解决方案2】:

    正如其他答案所提到的,NULL 不能作为附加参数传递给sscanf

    http://www.cplusplus.com/reference/cstdio/sscanf 谈到额外的论点:

    根据格式字符串,该函数可能需要一系列附加参数,每个参数都包含一个指向已分配存储空间的指针,其中提取字符的解释以适当的类型存储

    For the %s specifier these extracted characters are:

    任意数量的非空白字符,在找到的第一个空白字符处停止。一个终止的空字符会自动添加到存储序列的末尾。

    所以当存储“非空白字符”和“终止空字符”时,就会出现段错误。这正是 Visual Studio 将产生的结果(您可以在 http://webcompiler.cloudapp.net/ 测试它是否失败):

    现在就非 Visual Studio 编译器而言,libc 的 %s 说明符的提取代码:https://github.com/ffainelli/uClibc/blob/master/libc/stdio/_scanf.c#L1376 具有前导注释:/* We might have to handle the allocation ourselves */ 这是因为:

    GNU C 库通过a 字符支持动态分配转换说明符(作为非标准扩展)。这个特性似乎至少可以追溯到 glibc 2.0。
    从 2.7 版开始,glibc 还提供了 m 修饰符,其用途与 a 修饰符相同。

    [Source]

    因此,由于 libc 提取到内部构造为 sscanf 的缓冲区,并随后检查缓冲区参数在分配之前没有设置标志,因此它永远不会将字符写入 NULL 缓冲区参数。

    我不能强调这是非标准的,即使在次要库更新之间也不能保证保留。一个更好的方法是使用 * 子说明符:

    表示数据将从流中读取但被忽略(即它不存储在参数指向的位置)。

    [Source]

    这可以像这样完成,例如:

    s == NULL ? sscanf("Privjet mir!", "%*s") : sscanf("Privjet mir!", "%s", s);
    

    显然三元组的真正分支是无操作的,但我将它包括在内是希望从字符串中读取其他数据。

    【讨论】:

      【解决方案3】:

      手册页说,当使用%s 时,参数必须是一个指针,该指针具有足够的空间用于字符串和\0。所以我的猜测是你的情况下的行为是未定义的。它可能会起作用,也可能会崩溃或损坏内存并在以后引起问题。

      【讨论】:

        【解决方案4】:

        不,这是不允许的。 sscanf %s 需要一个 char* 指向一个足够大的缓冲区, printf %s 需要一个 nul char* 缓冲区。其他任何事情都会导致未定义的行为。 (这意味着某些实现可能会以某种方式检测和处理空指针,而其他实现可能不会)

        【讨论】:

          【解决方案5】:

          我没有在标准中找到任何明确涉及 NULL*printf/*scanf 的内容。

          我认为这是未定义的行为1,因为它被视为传递与格式说明符不一致的参数(§7.19.6.1 ¶13,§7.19.6.2 ¶13): %s 表示您将传递一个指向字符数组第一个元素的指针(对于*scanf 获取的字符串足够大,包含NUL 终止的字符串*printf) - 并传递NULL 不满足这个要求。


          1.在这种情况下UB显示为“忽略采集”和“打印(空)”,在其他平台上可能会导致飞机从天上掉下来或通常的nasal demons

          【讨论】:

            【解决方案6】:

            将内存分配给 s 。将 s 分配给字符数组。然后运行程序。 以下将起作用。

            int main(int arc, char* argv[])
            {
              char s[100];
              sscanf("Privjet mir!", "%[^\t]s", s);
              printf("s: %s\n", s);
              return 0;
            }
            

            【讨论】:

              猜你喜欢
              • 2010-12-18
              • 1970-01-01
              • 2016-06-04
              • 2018-03-09
              • 2015-06-29
              • 2019-04-20
              • 2018-06-25
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多