3

根据手册页,snprintf 返回从 glibc 2.2 版开始写入的字节数。但在较低版本的 libc2.2 和 HP-UX 上,它返回一个正整数,这可能导致缓冲区溢出。

如何克服这一点并编写可移植的代码?

编辑:为了更清楚

此代码在 lib 2.3 中完美运行

    if ( snprintf( cmd, cmdLen + 1, ". %s%s", myVar1, myVar2 )  != cmdLen )
    {
        fprintf( stderr, "\nError: Unable to  copy bmake command!!!");
        returnCode = ERR_COPY_FILENAME_FAILED;
    }

它在 Linux 上返回字符串 (10) 的长度。但相同的代码返回的正数大于 HP-UX 机器上打印的字符数。希望这个解释很好。

4

6 回答 6

4

您可以创建一个 snprintf 包装器,当缓冲区中没有足够的空间时,它会为每种情况返回 -1。

有关更多文档,请参见手册页。它还有一个威胁所有案例的例子。

  while (1) {
      /* Try to print in the allocated space. */
      va_start(ap, fmt);
      n = vsnprintf (p, size, fmt, ap);
      va_end(ap);
      /* If that worked, return the string. */
      if (n > -1 && n < size)
         return p;
      /* 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 */
      if ((np = realloc (p, size)) == NULL) {
         free(p);
         return NULL;
      } else {
         p = np;
      }
   }
于 2008-09-19T10:32:47.963 回答
2

您是否考虑过 printf 的可移植实现?不久前我找了一个,然后选择了三重奏。

http://daniel.haxx.se/projects/trio/

于 2008-09-19T11:46:51.287 回答
1

你的问题还不清楚。链接到的手册页是这样说的:

函数 snprintf() 和 vsnprintf()写入的字节数不超过 size 字节(包括尾随的 '\0')。如果输出由于此限制而被截断,则返回值是在有足够空间可用的情况下写入最终字符串的字符数(不包括尾随的 '\0')。因此,大小或更大的返回值意味着输出被截断。

因此,如果您想知道您的输出是否被截断:

int ret = snprintf(cmd, cmdLen + 1, ". %s%s", myVar1, myVar2 ) == -1)
if(ret == -1 || ret > cmdLen)
{
    //output was truncated
}
else
{
    //everything is groovy
}
于 2008-09-19T14:53:25.783 回答
1

*printf 可移植性存在很多问题,实际上您可能希望遵循以下三种路径之一:

  1. 需要一个符合 c99 的 *printf,因为 9 年对任何人来说都足够了,否则就说平台坏了。

  2. 有一个带有一堆#ifdef 的 my_snprintf(),用于您想要支持所有调用下面的 vsnprintf() 的特定平台(了解最低公分母就是您所拥有的)。

  3. 只需在您的代码中随身携带一份 vsnprintf() 副本,对于简单的用例,它实际上非常简单,对于其他人您可能想查看vstr,您将免费获得客户格式化程序。

...正如其他人建议的那样,您可以针对 -1 的情况进行 hack 合并 #1 和 #2,但这是有风险的,因为 c99 *printf 在某些情况下可以/确实返回 -1。

我个人建议只使用像 ustr 这样的字符串库,它为您提供简单的解决方法并免费为您提供托管字符串。如果您真的很在意,可以与vstr结合使用。

于 2008-09-19T16:21:17.147 回答
0

我找到了一种可移植的方法来预测和/或限制 sprintf 和相关函数返回的字符数,但它效率低下,许多人认为它不雅。

您所做的是使用 tmpfile()、fprintf() 创建一个临时文件(它可靠地返回写入的字节数),然后倒带并将全部或部分文本读入缓冲区。

例子:

int my_snprintf(char *buf, size_t n, const char *fmt, ...)
{
    va_list va;
    int nchars;
    FILE *tf = tmpfile();

    va_start(va, n);
    nchars = vfprintf(tf, fmt, va);
    if (nchars >= (int) n)
        nchars = (int) n - 1;
    va_end(va);
    memset(buf, 0, 1 + (size_t) nchars);

    if (nchars > 0)
    {
        rewind(tf);
        fread(buf, 1, (size_t) nchars, tf);
    }

    fclose(tf);

    return nchars;   
}
于 2008-09-19T10:18:44.237 回答
0

请改用更高级的 asprintf() 。

这是一个 GNU 扩展,但如果它本身不可用,则值得将其复制到目标平台。

于 2008-09-19T14:58:43.243 回答