12

我正在尝试在 AIX 上构建 python-kerberos。kerberospw.c 使用对 asprintf 的调用,但从 Google 告诉我的内容来看,AIX 上不存在 asprintf。

我看到http://www.koders.com/c/fidAA9B130D588302673A28B568430A83131B7734C0.aspx?s=windows.h,看起来我可以创建一个替代 asprintf,但我不知道这会去哪里或我会如何#将其包含在 kerberospw.c 中。

有没有办法可以使用 koders.com 示例或其他代码来“伪造”asprintf?我可以只包含 kerberospw.c 中所示的 asprintf 函数吗?我不是 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

4 回答 4

21

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

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;
}
于 2011-02-04T15:06:21.403 回答
14

基于Sylvain回答,这里有一个简单的实现,asprintf()因为vasprintf()你需要一个,你通常最终也需要另一个。而且,鉴于va_copy()C99 中的宏,它很容易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()的这个手册页说,如果出现错误,指针中的返回值是不确定的。其他手册页,包括问题中引用的手册页,表明它在错误时显式设置为 NULL。C 标准委员会文件 ( n1337.pdf ) 没有指定内存不足的错误行为。

  • 如果使用 asprintf(),不要假设函数失败时指针已初始化。
  • 如果实现 asprintf(),请确保指针在错误时设置为 null 以提供确定性行为。
于 2011-02-04T17:14:42.600 回答
1

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

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

我做了更多的搜索,发现了关于实现 asprintf 的讨论,这让我明白 Jonathan 和 Sylvain 都没有正确处理溢出。

我现在推荐与上述讨论一起提供的这个解决方案,它似乎涵盖了所有重要的平台,并且显然可以正确处理每个故障场景。

于 2014-07-03T13:21:09.183 回答
1

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()

于 2014-09-30T17:06:37.497 回答