1. 程式人生 > 實用技巧 >C語言VC6中使用asprintf, snprintf的坑

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:

  1. Guess a buffer size yourself.

  2. Make a buffer that islarge enough(which is not easy), then you can get a correct buffer size. See below.

snprintfenters the standard library in C99, and isnot presentin VC6. All you have is a_snprintf, which:

  1. Returns-1if 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_vsnprintfhas 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.

  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.

Note that when thecountargument passed in is smaller than the actual string length to write, though_snprintfreturns -1, it will actually writecountcharacters 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_listimplementation in one of the answers.

3._vsnprint&_snprintf: Present but Absent

Actually,_vsnprintfis 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_vsnprintfinunresolved external symbol _vsnprintfis not the one your code links to if you writes_vsnprintfdirectly.

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:

  1. As explained above, there is nosnprintfin VC6.
  2. As explained above, you can replacesnprintfwith_snprintfand compile it successfully. But since you passedNULL, you will get a segmentation fault.
  3. Even if for some reason your program not crashed,nbyteswill be-1since you passed0. Andsize_tis usuallyunsigned, so-1will become a large number, like 4294967295 in an x86 machine, and your program will stop in the nextmallocstep .

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.