Reputation: 278
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.
Upvotes: 11
Views: 17672
Reputation: 13826
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
Upvotes: 0
Reputation: 1087
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.
(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.
snprintf
enters the standard library in C99, and is not present in VC6. All you have is a _snprintf
, which:
-1
if the number of characters to write is less than or equal to count
(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
.
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.
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.
_vsnprint
& _snprintf
: Present but AbsentActually, _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
.
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:
snprintf
in VC6.snprintf
with _snprintf
and compile it successfully. But since you passed NULL
, you will get a segmentation fault.nbytes
will be -1
since you passed 0
. And size_t
is usually unsigned
, so -1
will become a large number, like 4294967295 in an x86 machine, and your program will stop in the next malloc
step .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.
Upvotes: 2
Reputation: 931
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.
Upvotes: 3
Reputation: 2955
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)
Upvotes: 18
Reputation: 9119
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.
(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
#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)
Upvotes: 7
Reputation: 213378
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.
Upvotes: 18
Reputation: 21
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.
Upvotes: 2
Reputation: 121397
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()
.
Upvotes: 3