vsnprintf and gcc

2020-06-29 10:58发布

问题:

I have the following statement:

vsnprintf(target, size - 1, "%ls_%ls", str16_1, str16_2);

Why does this fail on gcc?

I used this on Windows like this:

vsnprintf(target, size - 1, "%S_%S", str16_1, str16_2);

and it's working as expected. On gcc documentation I found that %S is synonym with %ls, but I must not use it. I tried also with %S, but is not working. I use this in a function with variable argument list. Is it possible to not work because I change the format variable that I pass to va_start? I must search %S and replace it with %ls in format variable.

The function is something like:

void f(const char* format, ...){
  char* new_format = format with %S replaced with %ls;
  va_list argptr;
  va_start(args, format);
  vsnprintf(str, size-1, new_format, argptr);
}

I checked and new_format is correct.

Thank you!

回答1:

Try using snprintf, the reason being vsnprintf. vsnprintf takes an argument of type va_list, not a literal variadic argument list. For example:

va_list ap;
va_start (ap, first_arg_in_this_function);
vsnprintf (buf, size, format_str, ap);
va_end (ap);

Whereas with sprintf:

snprintf (buf, size, format_str, x, y);

Use v*printf when...

  1. Making wrappers around printf style functions
  2. Variadic macros are not an option

Otherwise just use *printf



回答2:

Your use of va_start is incorrect. In this statement:

va_start(args, new_format);

you are not referring to the format parameter of the f() function. The second argument to va_start() must refer to a parameter in the formal parameter list of the function. Anything else is likely undefined behaviour.

The compiler uses the named formal parameter in va_start() to determine where to start looking for the variable argument list in the function call. It doesn't automatically know where you put ... in the argument list (perhaps you might expect that it should, but that's not how it works).



回答3:

I looked up %ls for vsnprintf and found that this is the format specifier for printing/formatting a string of wide characters i.e. wide_t *p = L"Hello world!";

It took a bit of playing and googling wide character usage in C++ (I liked the following page: http://www.linux.com/archive/feed/51836), but I think I figured out your problem.

If you pass in a char string to %ls then it doesn't expand, but if you pass in a wchar_t string to %ls then it prints.

Consider the following example code I based on your information:

#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <wchar.h>


char       str[100];


void f (const char    *format,
        ...)
{
    va_list    args;

    va_start(args, format);
    vsnprintf(str, sizeof(str), format, args);
    va_end(args);
}




int
main ()
{
    char    *p1 = "1234";
    char    *p2 = "abcd";

    wchar_t    *pw1 = L"9876";
    wchar_t    *pw2 = L"wxyz";



    f("%d_%d", 120, 199);
    printf("numbers: %s\n", str);


    f("%s_%s", p1, p2);
    printf("char*: %s\n", str);


    f("%ls_%ls", p1, p2);
    printf("wide char* with char* input: %s\n", str);


    f("%ls_%ls", pw1, pw2);
    printf("wide char* with wide char* input: %s\n", str);


    return (0);
}

I compiled this with g++.

make newtest.exe
g++ -g -c -MD -Wall -Werror  newtest.cxx
g++ -o newtest.exe newtest.o -lc -lrt

Compilation finished at Thu Jul 29 08:54:57

Output is below:

[SUSE10.1]:201> newtest.exe
numbers: 120_199
char*: 1234_abcd
wide char* with char* input: 
wide char* with wide char* input: 9876_wxyz


回答4:

Use snprintf.



回答5:

What is your valist type? The correct type for variable list arguments is va_list with an underscore, no?



回答6:

Because I use this on Mac I found a work-around:

How to "pass on" a variable number of arguments to NSString's +stringWithFormat:

It seems that vsnprintf can't handle 16 bits string. Maybe because wchar_t isn't 16 bits.



标签: c++ c gcc printf