【问题标题】:Dynamic allocation with scanf()使用 scanf() 进行动态分配
【发布时间】:2010-10-12 03:44:56
【问题描述】:

我的问题和这个one完全一样。也就是说,我正在尝试使用scanf() 接收一个不确定长度的字符串,并且我希望scanf() 为其动态分配内存。

但是,在我的情况下,我使用的是 VS2010。据我所知,MS 的scanf() 在扫描字符串时没有 a 或 m 修饰符。有没有办法做到这一点(除了一次接收输入一个字符)?

【问题讨论】:

  • GNU C 库支持“a”作为“分配内存”修饰符; GNU C Library 2.8 版(我碰巧有的手册)没有提到'm'作为修饰符。这是自 2.8 以来的另一个新增功能吗?
  • 我在 2008 年 7 月 12 日版本 3.23 的联机帮助页中提到了两者。 m 修饰符是 GNU 扩展 a 的替代品,因为它与匹配浮点数的 c99 修饰符 a 发生冲突。
  • @Frank:是时候下载更新了……谢谢。这有时是非标准扩展的另一个缺点——标准通过标准化改变了非标准的含义。
  • 好吧,我自己可能需要一份新副本。我不知道我的太旧了,我猜没有人会刷新 Ubuntu 存储库中的联机帮助页,那里和 Debian 存储库中都有很多过时的软件......为什么没有一个自动更新存储库的系统?

标签: c++ c visual-studio-2010


【解决方案1】:

scanf() 的标准版本不会为其读取的任何变量分配内存。

如果您在scanf() 的某些版本中被欺骗使用非标准扩展,那么您刚刚学习了如何编写可移植代码的第一课——不要使用非标准扩展。您可以细微地说“不要使用并非在您感兴趣的所有平台上都可用的扩展程序”,但要意识到平台集可能会随着时间而改变。

【讨论】:

  • +1 指出依赖非便携式扩展如何影响 OP。
  • scanf 中 s/c/[ 的 m 修饰符至少从 2001 年起就已成为 POSIX 标准的一部分。因此它几乎可以在任何地方使用,除了 Microsoft
  • 在 macOS X 上仍然不可用。
【解决方案2】:

您必须绝对使用scanf 吗? std::string s; std::cin >> s;getline( std::cin, s ); 不适合您吗?

【讨论】:

    【解决方案3】:

    如果你想使用scanf,你可以分配一个足够大的缓冲区来保存任何可能的值,比如 1024 字节,然后使用 1024 的最大字段宽度说明符。

    ma 是特定的非标准 GNU 扩展,这就是为什么微软的编译器不支持它们。人们可能希望视觉工作室做到这一点。

    这是一个使用scanf 读取设置并将其打印出来的示例:

    #include <stdio.h>
    #include <errno.h>
    #include <malloc.h>
    
    int
    main( int argc, char **argv )
    {   // usage ./a.out < settings.conf
    
        char *varname;
        int value, r, run = 1;
    
        varname = malloc( 1024 );
    
        // clear errno
        errno = 0;
    
        while( run )
        {   // match any number of "variable = #number" and do some "processing"
    
            // the 1024 here is the maximum field width specifier.
            r = scanf ( "%1024s = %d", varname, &value );
            if( r == 2 )
            {   // matched both string and number
                printf( " Variable %s is set to %d \n", varname, value );
            } else {
                // it did not, either there was an error in which case errno was
                // set or we are out of variables to match
                if( errno != 0 )
                {   // an error has ocurred.
                    perror("scanf");
                }
                run = 0;
            }
        }
    
        return 0;
    }
    

    这是一个例子settings.conf

    cake = 5
    three = 3
    answertolifeuniverseandeverything = 42
    charcoal = -12
    

    你可以阅读更多关于scanf on the manpages的信息。

    当然你也可以使用getline(),然后解析一个字符一个字符。

    如果你能更深入地了解你想要实现的目标,你可能会得到更好的答案。

    【讨论】:

    • 我将解释如何编写字段宽度说明符,因为 OP 可能不知道并且只是忽略建议,从而创建缓冲区溢出漏洞...
    • 对,我可能应该。但是 OP 代表什么?
    【解决方案4】:

    我认为,在现实世界中,需要对用户输入的长度有一些最大限制。

    然后您可以使用getline() 之类的内容阅读整行内容。见http://www.cplusplus.com/reference/iostream/istream/getline/

    请注意,如果您希望用户提供多个输入,则不需要为每个输入单独的 char 数组。你可以有一个大缓冲区,例如char buffer[2048],用于与getline() 一起使用,并将内容复制到适当分配(和命名)的变量,例如类似char * name = strdup( buffer )

    【讨论】:

    • 最好让getline() 为你分配一个缓冲区,如果缓冲区不够大,那么getline() 会使其足够大。
    【解决方案5】:

    不要使用scanf 来读取字符串。它甚至可能不会像您认为的那样做。 %s 只读取到下一个空格。

    【讨论】:

    • 别这样,你的“答案”没有用。这家伙可能没有手册页,而是在问他应该怎么做——而不是他应该怎么做
    • @Frank:我认为关于%s 和空格的观点是一个相当精明的观察!
    • 是的,你是对的,但也许这家伙知道 %s 只会像 std::cin 那样读取下一个空格。 High 可能有一些以前在 *nix/GNU 系统上编译过的代码,并试图让它在他的 vc10 上运行。如果我们/他有一些现有的代码可以处理/显示会更容易。
    • 我的第一句话是独立的;第二个只是额外的评论。
    猜你喜欢
    • 2012-08-09
    • 2020-04-21
    • 1970-01-01
    • 2020-11-12
    • 1970-01-01
    • 2016-01-10
    • 2016-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多