我编写了一个在linux上运行完美的C程序,但是当我在windows上编译它时,它给出了一个错误,说asprintf()是未定义的。它应该是stdio库的一部分,但似乎许多编译器不包含它。哪个编译器可以用于windows,这将允许我使用asprintf()函数?我已经尝试了多个编译器,到目前为止似乎都没有定义它。
答案 0 :(得分:11)
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;
}
这是来自内存,但它应该与您为Visual Studio运行时编写vasprintf
的方式非常接近。
使用_vscprintf
和_vsprintf_s
是Microsoft C运行时特有的奇怪之处,您不会在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;
}
这不是"便携式"写asprintf
的方法,但如果你唯一的目标是支持Linux + Darwin + Windows,那么这是最好的方法。
答案 1 :(得分:3)
asprintf()
基于此线程中@DietrichEpp和@MarcusSun的答案以及另一个帖子中针对MacOS / Linux的_vscprintf()
this collaborative implementation的答案。在GCC / Linux,MSVC / Windows,MinGW / Windows(通过Code :: Blocks的TDM-GCC)上测试。希望也可以在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
#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;
}
直播示例: rex(MSVC·gcc·clang)| repl.it | tio.run | Codepad | ide1(gcc·clang·C99)
答案 2 :(得分:3)
根据answer provided by 7vujy0f0hy,这是一个提供 asprintf , vasprintf 的头文件, vscprintf 适用于多个平台/编译器(GNU-C兼容编译器+ MSVC)。请注意,由于使用 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
<强> example.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;
}
使用在线编译器进行测试:
rextester C (gcc) | rextester C (clang) | rextester C (msvc)
答案 3 :(得分:1)
asprintf()
不是C标准函数。它是由glibc提供的GNU扩展。因此它适用于Linux。但是其他C实现可能无法提供它 - 这似乎是您的库的情况。
您可以使用标准C函数malloc()
和snprintf()
重写代码。
答案 4 :(得分:1)
此功能位于glibc库中,不受Windows支持。
据我所知,asprintf与sprintf类似,其中包含缓冲区分配。
在Windows中,最简单的方法可能是编写自己的实现。要计算要分配的缓冲区的大小,只需使用类似:
int size_needed = snprintf(NULL,0,“%s \ n”,“test”);
计算出大小后,只需分配缓冲区,调用snprintf格式化字符串并返回指针。
答案 5 :(得分:1)
对于具有更高版本的MSVC编译器(例如您正在使用VS2010)的用户或使用 C ++ 而不是C的用户,这很容易。您可以在此处的另一个答案中使用va_list
实现。很好。
如果您使用的是基于 GCC / like的编译器(如clang,cygwin,MinGW,TDM-GCC等),我应该已经有一个asprintf
不知道如果没有,您可以在此处的另一个答案中使用va_list
实现。
(也许适用于Turbo C,lcc和任何较旧的版本)
您不能。您必须:
自己猜测缓冲区大小。
制作一个足够大的缓冲区(这并不容易),然后可以获得正确的缓冲区大小。
如果您选择此选项,那么我将基于另一个答案中的va_list
实现对VC6 C语言进行方便的实现。
// #include <stdio.h> /* for _vsnprintf */
// No, you don't need this
#include <stdlib.h> /* for malloc */
#include <stdarg.h> /* for va_* */
#include <string.h> /* for strcpy */
// Note: If it is not large enough, there will be fine
// Your program will not crash, just your string will be truncated.
#define LARGE_ENOUGH_BUFFER_SIZE 256
int vasprintf(char **strp, const char *format, va_list ap)
{
char buffer[LARGE_ENOUGH_BUFFER_SIZE] = { 0 }, *s;
// If you don't initialize it with { 0 } here,
// the output will not be null-terminated, if
// the buffer size is not large enough.
int len,
retval = _vsnprintf(buffer, LARGE_ENOUGH_BUFFER_SIZE - 1, format, ap);
// if we pass LARGE_ENOUGH_BUFFER_SIZE instead of
// LARGE_ENOUGH_BUFFER_SIZE - 1, the buffer may not be
// null-terminated when the buffer size if not large enough
if ((len = retval) == -1) // buffer not large enough
len = LARGE_ENOUGH_BUFFER_SIZE - 1;
// which is equivalent to strlen(buffer)
s = malloc(len + 1);
if (!s)
return -1;
strcpy(s, buffer);
// we don't need to use strncpy here,
// since buffer is guaranteed to be null-terminated
// by initializing it with { 0 } and pass
// LARGE_ENOUGH_BUFFER_SIZE - 1 to vsnprintf
// instead of LARGE_ENOUGH_BUFFER_SIZE
*strp = s;
return retval;
}
int asprintf(char **strp, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = vasprintf(strp, format, ap);
va_end(ap);
return retval;
}
int main(void)
{
char *s;
asprintf(&s, "%d", 12345);
puts(s);
free(s);
// note that s is dynamically allocated
// though modern Windows will free everything for you when you exit
// you may consider free those spaces no longer in need in real programming
// or when you're targeting older Windows Versions.
return 0;
}
如果您想了解更多详细信息,例如为什么我们必须设置足够大的缓冲区大小,请参见下文。
snprintf
在C99中进入标准库,在VC6中不存在。您所拥有的只是一个_snprintf
,其中:
-1
(自变量),则返回count
。因此不能用于获取缓冲区大小。这似乎没有记录(请参阅Microsoft Docs)。但是_vsnprintf
在相同情况下具有特殊的行为,因此我想这里可能有某些事情,通过下面的测试,我发现我的假设是正确的。
是的,它甚至不返回它写的字符数,例如_vsnprintf
。只是一个-1
。
If buffer is a null pointer and count is nonzero, or if format is a null pointer, the invalid parameter handler is invoked, as described in Parameter Validation.
调用了无效的参数处理程序,这意味着您将遇到分段错误。此处测试代码:
#include <stdio.h>
int main(void)
{
char s[100], s1[100] = { 0 };
#define TEST(s) printf("%s: %d\n", #s, s)
TEST(_snprintf(NULL, 0, "%d", 12345678));
/* Tested, and segmentation Fault */
// TEST(_snprintf(NULL, 100, "%d", 12345678));
TEST(_snprintf(s, 0, "%d", 12345678));
TEST(_snprintf(s, 100, "%d", 12345678));
TEST(_snprintf(s1, 5, "%d", 12345678));
puts(s);
puts(s1);
return 0;
}
以及VC6编译器的输出:
_snprintf(NULL, 0, "%d", 12345678): -1
_snprintf(s, 0, "%d", 12345678): -1
_snprintf(s, 100, "%d", 12345678): 8
_snprintf(s1, 5, "%d", 12345678): -1
12345678
12345
支持我的假设。
我用s1
初始化了{0}
,否则将不终止为空。由于_snprintf
参数太小,count
不会这样做。
如果添加一些puts
,您会发现第二个_vsnprintf返回-1不会对s
写入任何内容,因为我们将0
作为count参数传递了。
请注意,当传入的count
参数小于要写入的实际字符串长度时,尽管_snprintf
返回-1,但实际上会将count
个字符写入缓冲区。 / p>
snprintf进入C99中的标准库,并且没有snprintf,_vsnprintf和__vscprintf:
asprintf.obj : error LNK2001: unresolved external symbol _vsnprintf
asprintf.obj : error LNK2001: unresolved external symbol __vscprintf
因此您不能在其中一个答案中使用va_list
实现。
实际上,VC6中有_vsnprintf
,请参见下面的3.。但是_vscprint
真的不存在。
_vsnprint
和_snprintf
:存在但不存在 实际上,存在_vsnprintf
。如果您尝试调用它,就可以做到。
您可能说,有一个矛盾,您刚刚说过unresolved external symbol _vsnprintf
。这很奇怪,但这是事实。如果您直接编写_vsnprintf
,则unresolved external symbol _vsnprintf
中的_vsnprintf
不是您的代码所链接的那个。
_snprintf
发生了同样的事情。您可以自己调用它,但是如果您调用snprintf
,则链接程序将抱怨没有_snprintf
。
更糟糕的是,你不能自己写:
size_t nbytes = snprintf(NULL, 0, fmt, __VA_ARGS__) + 1; /* +1 for the '\0' */
char *str = malloc(nbytes);
snprintf(str, nbytes, fmt, __VA_ARGS__);
那是因为:
snprintf
。snprintf
替换为_snprintf
并成功进行编译。但是,由于您通过了NULL
,因此会遇到细分错误。nbytes
以来,-1
也将是0
。并且size_t
通常是unsigned
,因此-1
会变得很大,就像x86机器中的4294967295一样,您的程序将在下一步malloc
中停止。 li>
您可以链接一个称为传统stdio定义或其他名称的库,但是我选择自己猜测缓冲区的大小,因为在我看来,这样做并不是很危险。
答案 6 :(得分:0)
根据LGPL许可,该库包含 asprintf()的实现。
“在项目中使用自由的最简单方法是将自由代码放到项目的源代码中。” 1
int asprintf(字符** resptr,常量字符*格式,...)
类似于sprintf,而是将指针传递给指针,而不是将指针传递给缓冲区。该函数将计算所需缓冲区的大小,使用malloc分配内存,并将指向已分配内存的指针存储在* resptr中。返回的值与sprintf返回的值相同。如果无法分配内存,则返回减一,并将NULL存储在* resptr中。
答案 7 :(得分:0)
LibreSSL 有自己的 BSD 许可实现
https://github.com/libressl-portable/portable/blob/master/crypto/compat/bsd-asprintf.c
@a4cc953
哈希:
/*
* Copyright (c) 2004 Darren Tucker.
*
* Based originally on asprintf.c from OpenBSD:
* Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef HAVE_ASPRINTF
#include <errno.h>
#include <limits.h> /* for INT_MAX */
#include <stdarg.h>
#include <stdio.h> /* for vsnprintf */
#include <stdlib.h>
#ifndef VA_COPY
# ifdef HAVE_VA_COPY
# define VA_COPY(dest, src) va_copy(dest, src)
# else
# ifdef HAVE___VA_COPY
# define VA_COPY(dest, src) __va_copy(dest, src)
# else
# define VA_COPY(dest, src) (dest) = (src)
# endif
# endif
#endif
#define INIT_SZ 128
int
vasprintf(char **str, const char *fmt, va_list ap)
{
int ret;
va_list ap2;
char *string, *newstr;
size_t len;
if ((string = malloc(INIT_SZ)) == NULL)
goto fail;
VA_COPY(ap2, ap);
ret = vsnprintf(string, INIT_SZ, fmt, ap2);
va_end(ap2);
if (ret >= 0 && ret < INIT_SZ) { /* succeeded with initial alloc */
*str = string;
} else if (ret == INT_MAX || ret < 0) { /* Bad length */
free(string);
goto fail;
} else { /* bigger than initial, realloc allowing for nul */
len = (size_t)ret + 1;
if ((newstr = realloc(string, len)) == NULL) {
free(string);
goto fail;
}
VA_COPY(ap2, ap);
ret = vsnprintf(newstr, len, fmt, ap2);
va_end(ap2);
if (ret < 0 || (size_t)ret >= len) { /* failed with realloc'ed string */
free(newstr);
goto fail;
}
*str = newstr;
}
return (ret);
fail:
*str = NULL;
errno = ENOMEM;
return (-1);
}
int asprintf(char **str, const char *fmt, ...)
{
va_list ap;
int ret;
*str = NULL;
va_start(ap, fmt);
ret = vasprintf(str, fmt, ap);
va_end(ap);
return ret;
}
#endif