I have written a C program which works perfectly on linux, but when I compile it on windows, it gives me an error saying that asprintf() is undefined. It should be a part of the stdio library but it seems that many compilers do not include it. Which compiler can I use for windows which will allow me to use the asprintf() function? I have tried multiple compilers and none seem to define it so far.
The asprintf()
function is not part of the C language and it is not available on all platforms. The fact that Linux has it is unusual.
You can write your own using _vscprintf
and _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;
}
This is from memory but it should be very close to how you would write vasprintf
for the Visual Studio runtime.
The use of _vscprintf
and _vsprintf_s
are oddities unique to the Microsoft C runtime, you wouldn't write the code this way on Linux or OS X. The _s
versions in particular, while standardized, in practice are not often encountered outside the Microsoft ecosystem, and _vscprintf
doesn't even exist elsewhere.
Of course, asprintf
is just a wrapper around 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;
}
This is not a "portable" way to write asprintf
, but if your only goal is to support Linux + Darwin + Windows, then this is the best way to do that.
va_copy
/va_end
in your vasprintf
implementation. Once _vscprintf
returns, ap
is no longer valid, so you need to save a copy for the call to _vsprintf_s
. (Unless I'm totally mistaken.) –
Uncut va_copy()
to be less portable on Windows because no definition for it was provided prior to VS2013. VS2012 and VS2010 are still fairly common in the wild. –
Austronesia Based on the answer provided by 7vujy0f0hy, here is a header file providing asprintf, vasprintf and vscprintf for multiple platforms/compilers (GNU-C-compatible compilers + MSVC). Please note that this requires C99 due to the use of va_copy. Refer to the links below to test a slightly modified version of this code using online compilers.
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;
}
Test using online compilers:
rextester C (gcc) | rextester C (clang) | rextester C (msvc)
Multiplatform implementation of asprintf()
Based on answers by @DietrichEpp and @MarcusSun in this thread and this collaborative implementation of _vscprintf()
for MacOS/Linux in another thread. Tested on GCC/Linux, MSVC/Windows, MinGW/Windows (TDM-GCC via Code::Blocks). Should hopefully work on Android too.
Header file
(Presumably named 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
Usage
#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;
}
Live examples: rex (MSVC · gcc · clang) | repl.it | tio.run | Codepad | ide1 (gcc · clang · C99)
asprintf()
is not a C standard function. It's a GNU extension provided by glibc. Hence it works on Linux. But other C implementations may not provide it -- which appears to be the case with your library.
You can instead rewrite your code using standard C functions malloc()
and snprintf()
.
GNU libiberty
Licensed under the LGPL this library includes an implementation of asprintf().
“Possibly the easiest way to use libiberty in your projects is to drop the libiberty code into your project’s sources.”1
int asprintf (char **resptr, const char *format, ...)
Like sprintf, but instead of passing a pointer to a buffer, you pass a pointer to pointer. This function will compute the size of the buffer needed, allocate memory with malloc, and store a pointer to the allocated memory in *resptr. The value returned is the same as sprintf would return. If memory could not be allocated, minus one is returned and NULL is stored in *resptr.
This function is in glibc libary and not supported by Windows.
As far as I knew, asprintf is similiar to sprintf with buffer allocation in it.
In windows, the simplest way is probably to write your own implementation. To calculate the size of buffer to be allocated, just use something like:
int size_needed = snprintf(NULL, 0, "%s\n", "test");
Once the size is calcuated, just allocate buffer, call snprintf to format string and return pointer.
For those with a higher version of MSVC compiler (like you're using VS2010) or those using C++ instead of C, it's easy. You can use the va_list
implementation in another answer here. It's great.
If you're using a GCC-based/-like compiler (like clang, cygwin, MinGW, TDM-GCC etc.), there should be one asprintf
already, I don't know. If not, you can use the va_list
implementation in another answer here.
An VC6 C Implementation (not C++)
Yes, VC6 is too old, and all of the other answers don't support VC6.
(maybe for Turbo C, lcc and any older ones too)
You can't. You have to:
Guess a buffer size yourself.
Make a buffer that is large enough (which is not easy), then you can get a correct buffer size.
If you choose this, I have make a handy implementation for VC6 C language, based on the va_list
implement in another answer.
// #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;
}
If you want to know more details, like why we have to set a large enough buffer size, see below.
1. Explanation
snprintf
enters the standard library in C99, and is not present in VC6. All you have is a _snprintf
, which:
- Returns
-1
if the number of characters to write is less than or equal tocount
(the argument). So can't be used to get the buffer size.
This seems not documentated (see Microsoft Docs). But _vsnprintf
has special behavior in the same situation, so I guess there may be something here and with the test below I found my assumption correct.
Yes, it even doesn't return the number of characters it has written, like _vsnprintf
. Just a -1
.
- This one is documentated:
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.
invalid parameter handler is invoked means you will get a segmentation fault.
Test code here:
#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;
}
And the output with VC6 compiler:
_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
which supports my assumption.
I initialized s1
with {0}
, otherwise it will not be null-terminated. _snprintf
doesn't do that, since the count
argument is too small.
If you add some puts
, you will find that second _vsnprintf returns -1 doesn't write anything into s
, since we passed 0
as the count argument.
Note that when the count
argument passed in is smaller than the actual string length to write, though _snprintf
returns -1, it will actually write count
characters into the buffer.
2. Using vscprintf? No way!
snprintf enters the standard library in C99, and there is no snprintf, _vsnprintf and __vscprintf:
asprintf.obj : error LNK2001: unresolved external symbol _vsnprintf
asprintf.obj : error LNK2001: unresolved external symbol __vscprintf
So you can't use the va_list
implementation in one of the answers.
Actually, there is _vsnprintf
in VC6, see 3. below. But _vscprint
is really absent.
3. _vsnprint
& _snprintf
: Present but Absent
Actually, _vsnprintf
is present. If you try to call it, you can make it.
You may say, there is a contradictory, you just said unresolved external symbol _vsnprintf
. This is weird, but it's true. The _vsnprintf
in unresolved external symbol _vsnprintf
is not the one your code links to if you writes _vsnprintf
directly.
Same thing happens on _snprintf
. You can call it yourself, but you if you call snprintf
, the linker will complain that there is no _snprintf
.
4. Get the buffer size to write by passing 0 argument as count like we do in *nix? No way!
What's worse, you can't write this for yourself:
size_t nbytes = snprintf(NULL, 0, fmt, __VA_ARGS__) + 1; /* +1 for the '\0' */
char *str = malloc(nbytes);
snprintf(str, nbytes, fmt, __VA_ARGS__);
That's because:
- As explained above, there is no
snprintf
in VC6. - As explained above, you can replace
snprintf
with_snprintf
and compile it successfully. But since you passedNULL
, you will get a segmentation fault. - Even if for some reason your program not crashed,
nbytes
will be-1
since you passed0
. Andsize_t
is usuallyunsigned
, so-1
will become a large number, like 4294967295 in an x86 machine, and your program will stop in the nextmalloc
step .
5. Maybe a better solution
You can link a library called legacy stdio definitions or something else, but I choose to guess the buffer size myself, since in my case it is not very dangerous to do that.
LibreSSL has its own implementation that is BSD licensed
https://github.com/libressl-portable/portable/blob/master/crypto/compat/bsd-asprintf.c
@ a4cc953
hash:
/*
* Copyright (c) 2004 Darren Tucker.
*
* Based originally on asprintf.c from OpenBSD:
* Copyright (c) 1997 Todd C. Miller <[email protected]>
*
* 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
© 2022 - 2024 — McMap. All rights reserved.
stdio
library. Read the manual on linux, you should be careful if you want your code to be portable.asprintf()
can be implemented with a combination ofsnprintf()
s, one to predict the string length, the other one to actually "print" the string into the allocated space. – Virchow