C語言VC6中使用asprintf, snprintf的坑
這是我在Stack Overflow上面寫的一篇答案,很長,都可以做一個博文了。https://stackoverflow.com/questions/40159892/using-asprintf-on-windows/63317479#63317479
For VC6 C Users (not C++)
1.Explanation
You can't. You have to:
-
Guess a buffer size yourself.
-
Make a buffer that islarge enough(which is not easy), then you can get a correct buffer size. See below.
snprintf
enters the standard library in C99, and isnot presentin VC6. All you have is a_snprintf
, which:
- Returns
-1
if the number of characters to write is less than or equal to count`. So can't be used to get the buffer size.
This seems not documentated (seeMicrosoft 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
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.
Note that when thecount
argument passed in is smaller than the actual string length to write, though_snprintf
returns -1, it will actually writecount
characters into the buffer.
2.Write one yourself with va_list? 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 theva_list
implementation in one of the answers.
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 saidunresolved external symbol _vsnprintf
. This is weird, but it's true. The_vsnprintf
inunresolved 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 callsnprintf
, 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 an elegant 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.