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

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

我看到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的软件,它会是很高兴有一个解决方法。

asprintfprintf系列的一种变体,它分配一个缓冲区来保存格式化字符串的内存并返回它。 它是一个具有可变数量参数的函数(因此声明中的...是有效的C代码)。 你可以在这里找到描述。

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

这是一个实现:

 #include  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; } 

基于Sylvain的回答 ,这里有一个简单的实现,包括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); } 

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

 #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include  #include  #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()的此手册页说,如果出现错误,指针中的返回值是不确定的。 其他手册页 ,包括问题中引用的手册页 ,表明它在出错时显式设置为NULL。 C标准委员会文件( n1337.pdf )没有规定缺乏内存的错误行为。

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

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

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

我做了更多的搜索并遇到了关于实现asprintf的讨论 ,然后启发我Jonathan和Sylvain都没有正确处理溢出。

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

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

应该是这样,将asprintf()定义为对asprintf()的调用

 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; } 

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