AIX上的asprintf的替代或解决方法

时间:2011-02-04 14:39:23

标签: c printf aix asprintf

我试图在AIX上构建python-kerberos。 kerberospw.c使用了对asprintf的调用,但是从谷歌告诉我的是,asprintf在AIX上不存在。

我看到了http://www.koders.com/c/fidAA9B130D588302673A28B568430A83131B7734C0.aspx?s=windows.h,看起来我可以创建一个替代asprintf,但我不知道这会发生什么,或者我将如何#include它在kerberospw.c。

有没有办法可以使用koders.com示例或其他代码来"假" asprintf?我可以只包含asprintf函数,如kerberospw.c所示?我不是C编码员,但是

  

asprintf(char ** resultp,const char * format,...)

对我来说看起来不是一个有效的签名,最后是圆点。来自kerberospw.c的相关行在

之下
  

asprintf(& message,"%。* s:%。* s",           (int)result_code_string.length,
          (char *)result_code_string.data,
          (int)result_string.length,
          (char *)result_string.data);

我意识到我可以联系python-kerberos的作者,但是a)我认为如果我这样做有一个潜在的补丁是有帮助的,并且b)我可能会运行其他使用asprintf的软件,并且有一个解决方法会很好。

4 个答案:

答案 0 :(得分:21)

asprintfprintf函数系列的变体,它分配缓冲区来保存格式化字符串的内存并将其返回。它是一个带有可变参数个数的函数(因此声明中的...是有效的C代码)。您可以找到说明here

如果vsnprintf运行正常(例如,如果缓冲区太小而无法保存格式化字符串,则返回错误),可以相对容易地重新实现。

以下是这样的实现:

#include <stdarg.h>

int asprintf(char **ret, const char *format, ...)
{
    va_list ap;

    *ret = NULL;  /* Ensure value can be passed to free() */

    va_start(ap, format);
    int count = vsnprintf(NULL, 0, format, ap);
    va_end(ap);

    if (count >= 0)
    {
        char* buffer = malloc(count + 1);
        if (buffer == NULL)
            return -1;

        va_start(ap, format);
        count = vsnprintf(buffer, count + 1, format, ap);
        va_end(ap);

        if (count < 0)
        {
            free(buffer);
            return count;
        }
        *ret = buffer;
    }

    return count;
}

答案 1 :(得分:14)

Sylvainanswer上构建,这是一个简单的实现asprintf()vasprintf(),因为你需要一个,你通常最终需要另一个。而且,考虑到C99中的va_copy()宏,就asprintf()而言,很容易实现vasprintf()。实际上,在编写varargs函数时,将它们成对使用通常很有帮助,一个用省略号表示法,另一个用va_list参数代替省略号,你可以用后者来简单地实现前者。

这导致代码:

int vasprintf(char **ret, const char *format, va_list args)
{
    va_list copy;
    va_copy(copy, args);

    /* Make sure it is determinate, despite manuals indicating otherwise */
    *ret = NULL;

    int count = vsnprintf(NULL, 0, format, args);
    if (count >= 0)
    {
        char *buffer = malloc(count + 1);
        if (buffer == NULL)
            count = -1;
        else if ((count = vsnprintf(buffer, count + 1, format, copy)) < 0)
            free(buffer);
        else
            *ret = buffer;
    }
    va_end(copy);  // Each va_start() or va_copy() needs a va_end()

    return count;
}

int asprintf(char **ret, const char *format, ...)
{
    va_list args;
    va_start(args, format);
    int count = vasprintf(ret, format, args);
    va_end(args);
    return(count);
}

在未提供这些功能的系统中使用这些功能的棘手部分是决定应该声明功能的位置。理想情况下,他们会在<stdio.h>,但是你不需要写它们。因此,您必须拥有一些包含<stdio.h>的其他标头,但如果未在<stdio.h>中声明这些函数,则声明这些函数。理想情况下,代码应该半自动检测到这一点。也许标题是"missing.h",并且包含(部分):

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#include <stdarg.h>

#ifndef HAVE_ASPRINTF
extern int asprintf(char **ret, const char *format, ...);
extern int vasprintf(char **ret, const char *format, va_list args);
#endif /* HAVE_ASPRINTF */

另请注意,asprintf()的此手册页说明指针中的返回值在发生错误时是不确定的。其他man pages(包括问题中引用的那个)表示它在出错时显式设置为NULL。 C标准委员会文件(n1337.pdf)没有规定缺乏记忆的错误行为。

  • 如果使用asprintf(),如果函数失败,请不要假设指针已初始化。
  • 如果实现asprintf(),请确保在出错时将指针设置为null以提供确定性行为。

答案 2 :(得分:1)

我来到这里寻找Windows和Linux的快速实现,它在失败时将返回指针设置为NULL。

Jonathan Leffler的回答看起来更好,但后来我注意到当malloc失败时它没有设置-1。

我做了更多的搜索并遇到了这个discussion of implementing asprintf,然后启发我,Jonathan和Sylvain都没有正确处理溢出。

我现在推荐上述讨论提供的this solution,它似乎涵盖了所有重要的平台,并且显然正确处理了每个故障情况。

答案 3 :(得分:0)

这是一种在大多数情况下不会两次调用snprintf()的实现。我省略了包含和定义,如其他响应中所示。

应该如此,将asprintf()定义为对vasprintf()

的调用
int asprintf(char **dst, const char * pcFormat, ...)
{
va_list ap;

  va_start(ap, pcFormat);
  int len = vasprintf(dst, pcFormat, ap);
  va_end(ap);
  return len;
}

我们预先将缓冲区预分配到预定义的适当大小,并且仅在第二次溢出调用vsnprintf()的情况下。理由是s*printf()功能被认为是非常沉重的,并且可以接受记忆的整体记忆。

int vasprintf(char **dst, const char * pcFormat, va_list ap)
{
  int len = 512;      /* Worked quite well on our project */
  int allocated = 0;
  va_list ap_copy;
  char *buff = NULL;

  while(len >= allocated) {
    free(buff);
    buff = malloc(len+1);
    if(buff) {
      allocated = len+1;
      va_copy(ap_copy, ap);
      len = vsnprintf(buff, len+1, pcFormat, ap_copy);
      va_end(ap_copy);
    }
    else   /* malloc() failed */
      return -1;
  }
  *dst = buff;
  return len;
}

编辑:我用简单realloc()替换malloc()来电,因为它更便宜。在溢出的情况下,free()/malloc()对的成本低于realloc(),因为其内部隐藏memcpy()。当我们随后调用vsnprintf()覆盖整个缓冲区时,该副本没有任何意义。