首页>Program>source

我正在将一些非常旧的(>10y)C代码移植到现代Linux.我在自定义编写的vsnprintf()包装器中遇到分段错误(显然,它的任务是检测重复的输出字符串并进行内插):

char* strVPrintf(const String fmt, va_list ap)
{
  /* Guess we need no more than 50 bytes. */
  int n, size = 50;
  char* p = (char*)memMalloc(size), q;
  while (1) {
    /* Try to print in the allocated space. */
    n = vsnprintf(p, size, fmt, ap);
    /* If that worked, return the string. */
    if (n > -1 && n < size) {
      break;
    }
    /* Else try again with more space. */
    if (n > -1)                /* glibc 2.1 */
      size = n + 1;            /* precisely what is needed */
    else                   /* glibc 2.0 */
      size *= 2;               /* twice the old size */
    p = memRealloc(p, size);
  }
  q =  strRegister(p);
  memFree(p);
  return q;
}

作者似乎已经假设标准 vsnprintf() 函数返回写入的字符数,如果没有收到足够的空间来格式化所有参数,则仅返回一个哨兵值.这意味着您可以猜测缓冲区的大小,并在必要时增加缓冲区大小。

但是在我的系统上(Ubuntu 14.04,glibc 2.19),vnprintf在为提供的空间使用太多参数时会导致分段错误.是否做了 snprintf()的语义 与此同时,家庭发生了巨大变化吗? 确保您有足够缓冲区空间的现代方法是什么?

最新回答
  • 10天前
    1 #

    这是使用 snprintf的正确方法 和 vsnprintf 除SunOS 4(已淘汰20年)以外的所有操作系统上,都存在其他问题。

    我会做一个纯粹的猜测,我可以肯定地说,您的问题是您要传递va_list ap 进入 vsnprintf 它将消耗掉它,然后您希望在下一次调用时将其重置.这是不正确的,并且已在多年前停止在gcc中工作(因为它仅适用于某些体系结构)。

    更改:

    n = vsnprintf(p, size, fmt, ap);
    

    收件人:

    va_list apc;
    va_copy(apc, ap);
    n = vsnprintf(p, size, fmt, apc);
    va_end(apc);
    

    看看是否有帮助。

    这是一个简单的测试,以了解发生了什么:

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdarg.h>
    void
    foo(const char *fmt, va_list ap)
    {
    #ifdef BAD
        vprintf(fmt, ap);
    #else
        va_list apc;
        va_copy(apc, ap);
        vprintf(fmt, apc);
        va_end(apc);
    #endif
        vprintf(fmt, ap);
    }
    void
    bar(const char *fmt, ...)
    {
        va_list ap;
        va_start(ap, fmt);
        foo(fmt, ap);
        va_end(ap);
    }
    int
    main(int argc, char **argv)
    {
        bar("foo %s\n", "bar");
        return 0;
    }
    

    运行时,我得到了:

    $ cc -o foo foo.c && ./foo
    foo bar
    foo bar
    $ cc -DBAD -o foo foo.c && ./foo
    foo bar
    foo ����
    

  • 10天前
    2 #

    据我了解的代码,其目的是检测 sprintf所需的大小 将输出字符串完全写入缓冲区.有一个函数可以为您执行此操作: asprintf (或 vasprintf 在这里)。

    原型:

    int vasprintf(char **strp, const char *fmt, va_list ap);
    

    只需按以下方式使用它即可:

    String strVPrintf(const String fmt, va_list ap)
    {
        char *ans;
        int n;
        n = vasprintf(&ans, fmt, ap);
        // do the checks
        return ans;
    }
    

    使用此功能,我认为您不再需要此包装器。

  • 10天前
    3 #

    不确定,但是我关于可变参数列表的手册页指出:

    威兹威兹

    正如您所说的,这是很旧的代码,也许在此例程中接收到的va_list不是

    COMPATIBILITY
    These macros are not compatible with the historic macros they replace. A backward compatible version can be found in the include file <varargs.h>.

    期望的va_list .您应该首先尝试使用一个标头提取所有参数,然后再确定另一个标头(通常是 vsnprintf 与stdarg.h兼容)

    vsnprintf

  • java:在ASyncTaskexecute()之后从Activity执行x()
  • php:如何在不启用的情况下同时使用URI段和查询字符串:enable_query_strings