在Windows上使用asprintf()

Vas*_*hta 5 c windows asprintf

我编写了一个在linux上运行完美的C程序,但是当我在windows上编译它时,它给出了一个错误,说asprintf()是未定义的.它应该是stdio库的一部分,但似乎许多编译器不包含它.哪个编译器可以用于windows,这将允许我使用asprintf()函数?我已经尝试了多个编译器,到目前为止似乎都没有定义它.

Die*_*Epp 13

asprintf()函数不是C语言的一部分,并不是所有平台都可用.Linux拥有它的事实是不寻常的.

你可以用_vscprintf和编写自己的_vsprintf_s.

int vasprintf(char **strp, const char *fmt, va_list ap) {
    // _vscprintf tells you how big the buffer needs to be
    int len = _vscprintf(fmt, ap);
    if (len == -1) {
        return -1;
    }
    size_t size = (size_t)len + 1;
    char *str = malloc(size);
    if (!str) {
        return -1;
    }
    // _vsprintf_s is the "secure" version of vsprintf
    int r = _vsprintf_s(str, len + 1, fmt, ap);
    if (r == -1) {
        free(str);
        return -1;
    }
    *strp = str;
    return r;
}
Run Code Online (Sandbox Code Playgroud)

这是来自内存,但它应该与您vasprintf为Visual Studio运行时编写的方式非常接近.

对于Microsoft C运行时来说,使用_vscprintf_vsprintf_s是奇怪的,你不会在Linux或OS X上以这种方式编写代码._s特别是版本,虽然标准化,但实际上并不经常在Microsoft生态系统之外遇到,并且_vscprintf不会甚至存在于其他地方.

当然,asprintf只是一个包装vasprintf:

int asprintf(char **strp, const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    int r = vasprintf(strp, fmt, ap);
    va_end(ap);
    return r;
}
Run Code Online (Sandbox Code Playgroud)

这不是一种"便携式"编写方式asprintf,但如果您的唯一目标是支持Linux + Darwin + Windows,那么这是最好的方法.


Max*_*xim 12

根据7vujy0f0hy 提供答案,这里是一个头文件,为多个平台/编译器(GNU-C 兼容编译器 + MSVC)提供asprintfvasprintfvscprintf。请注意,由于使用了va_copy,这需要 C99 。请参阅以下链接以使用在线编译器测试此代码的略微修改版本。

asprintf.h:

#ifndef ASPRINTF_H
#define ASPRINTF_H

#if defined(__GNUC__) && ! defined(_GNU_SOURCE)
#define _GNU_SOURCE /* needed for (v)asprintf, affects '#include <stdio.h>' */
#endif
#include <stdio.h>  /* needed for vsnprintf    */
#include <stdlib.h> /* needed for malloc, free */
#include <stdarg.h> /* needed for va_*         */

/*
 * vscprintf:
 * MSVC implements this as _vscprintf, thus we just 'symlink' it here
 * GNU-C-compatible compilers do not implement this, thus we implement it here
 */
#ifdef _MSC_VER
#define vscprintf _vscprintf
#endif

#ifdef __GNUC__
int vscprintf(const char *format, va_list ap)
{
    va_list ap_copy;
    va_copy(ap_copy, ap);
    int retval = vsnprintf(NULL, 0, format, ap_copy);
    va_end(ap_copy);
    return retval;
}
#endif

/*
 * asprintf, vasprintf:
 * MSVC does not implement these, thus we implement them here
 * GNU-C-compatible compilers implement these with the same names, thus we
 * don't have to do anything
 */
#ifdef _MSC_VER
int vasprintf(char **strp, const char *format, va_list ap)
{
    int len = vscprintf(format, ap);
    if (len == -1)
        return -1;
    char *str = (char*)malloc((size_t) len + 1);
    if (!str)
        return -1;
    int retval = vsnprintf(str, len + 1, format, ap);
    if (retval == -1) {
        free(str);
        return -1;
    }
    *strp = str;
    return retval;
}

int asprintf(char **strp, const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    int retval = vasprintf(strp, format, ap);
    va_end(ap);
    return retval;
}
#endif

#endif // ASPRINTF_H
Run Code Online (Sandbox Code Playgroud)

例子.c:

#include "asprintf.h" /* NOTE: this has to be placed *before* '#include <stdio.h>' */

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char *str = NULL;
    int len = asprintf(&str, "The answer to %s is %d", "life, the universe and everything", 42);
    if (str != NULL) {
        printf("String: %s\n", str);
        printf("Length: %d\n", len);
        free(str);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

使用在线编译器进行测试:

reextester C (gcc) | 雷克斯特 C (clang) | 雷克斯特 C (msvc)


7vu*_*0hy 6

多平台实现 asprintf()

基于@DietrichEpp@MarcusSun在此线程中的回答以及在另一个线程中为 MacOS/Linux 的协作实现_vscprintf()。在 GCC/Linux、MSVC/Windows、MinGW/Windows(TDM-GCC 通过 Code::Blocks)上测试。希望也能在 Android 上运行。

头文件

(大概命名为asprintf.h。)

#include <stdio.h> /* needed for vsnprintf */
#include <stdlib.h> /* needed for malloc-free */
#include <stdarg.h> /* needed for va_list */

#ifndef _vscprintf
/* For some reason, MSVC fails to honour this #ifndef. */
/* Hence function renamed to _vscprintf_so(). */
int _vscprintf_so(const char * format, va_list pargs) {
    int retval;
    va_list argcopy;
    va_copy(argcopy, pargs);
    retval = vsnprintf(NULL, 0, format, argcopy);
    va_end(argcopy);
    return retval;}
#endif // _vscprintf

#ifndef vasprintf
int vasprintf(char **strp, const char *fmt, va_list ap) {
    int len = _vscprintf_so(fmt, ap);
    if (len == -1) return -1;
    char *str = malloc((size_t) len + 1);
    if (!str) return -1;
    int r = vsnprintf(str, len + 1, fmt, ap); /* "secure" version of vsprintf */
    if (r == -1) return free(str), -1;
    *strp = str;
    return r;}
#endif // vasprintf

#ifndef asprintf
int asprintf(char *strp[], const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    int r = vasprintf(strp, fmt, ap);
    va_end(ap);
    return r;}
#endif // asprintf
Run Code Online (Sandbox Code Playgroud)

用法

#include <stdio.h> /* needed for puts */
#include <stdlib.h> /* needed for free */
#include "asprintf.h"

int main(void) {
    char *b;
    asprintf(&b, "Mama %s is equal %d.", "John", 58);
    puts(b); /* Expected: "Mama John is equal 58." */
    free(b); /* Important! */
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

实例: rex ( MSVC · gcc · clang ) | 复制| tio.run | 键盘| ide1 ( gcc · clang · C99 )


P.P*_*.P. 3

asprintf()不是 C 标准函数。它是 glibc 提供的 GNU 扩展。因此它可以在 Linux 上运行。但其他 C 实现可能不提供它——您的库似乎就是这种情况。

您可以使用标准 C 函数malloc()snprintf().