8

When you build an app on Windows using TCHAR support, %s in _tprintf() means char * string for Ansi builds and wchar_t * for Unicode builds while %S means the reverse.

But are there any format specifiers that always mean char * string no matter if it's an Ansi or Unicode build? Since even on Windows UTF-16 is not really used for files or networking it turns out to still be fairly often that you'll want to deal with byte-based strings regardless of the native character type you compile your app as.

hippietrail
  • 15,848
  • 18
  • 99
  • 158

4 Answers4

7

The h modifier forces both %s and %S to char*, and the l modifier forces both to wchar_t*, ie: %hs, %hS, %ls, and %lS.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Is this specific to MSVC? I just noticed this warning from gcc on Ubuntu 10: `warning: use of ‘h’ length modifier with ‘s’ type character` – hippietrail Apr 28 '11 at 02:43
  • No, it is not specific to MSVC, some other compilers support the `h` and `l` modifiers for the `s` type as well. I guess gcc is not one of them. – Remy Lebeau Apr 28 '11 at 09:26
  • I have just tried this with gcc (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2 -- it works, sort of. – 18446744073709551615 Apr 12 '12 at 07:38
  • 2
    According to [this interesting page](https://www.securecoding.cert.org/confluence/display/seccode/FIO47-C.+Use+valid+format+strings), %hs is not a valid modifier in general and is considered insecure. I know that it works for MSVC with and without unicode, but clang generates a [-Wformat] warning, complaining that "length modifier 'h' results in undefined behavior or no effect with 's' conversion". – normanius Nov 28 '14 at 23:10
  • Again, it appears to be compiler specific. Some compilers support it (BCC, VC++), some do not (GCC). – Remy Lebeau Nov 29 '14 at 03:49
1

This might also solve your problem:

_TCHAR *message;
_tprintf(_T("\n>>>>>> %d") TEXT(" message is:%s\n"),4,message);
user1202136
  • 11,171
  • 4
  • 41
  • 62
RKum
  • 758
  • 2
  • 12
  • 33
0

I found, that '_vsntprintf_s' uses '%s' for type TCHAR and works for both, GCC and MSVC. So you could wrap it like:

int myprintf(const TCHAR* lpszFormat, va_list argptr) {
    int len = _vsctprintf(lpszFormat, argptr); // -1:err
    if (len<=0) {return len;}
    auto* pT = new TCHAR[2 + size_t(len)];
    _vsntprintf_s(pT, (2+len)*sizeof(TCHAR), 1+len, lpszFormat, argptr);
    int rv = printf("%ls", pT);
    delete[] pT;
    return rv;
}
int myprintf(const TCHAR* lpszFormat, ...) {
    va_list argptr;
    va_start(argptr, lpszFormat);
    int rv = myprintf(lpszFormat, argptr);
    va_end(argptr);
    return rv;
}



int main(int, char**) { return myprintf(_T("%s"), _T("Test")); }
KungPhoo
  • 516
  • 4
  • 18
0

You can easily write something like this:

#ifdef _UNICODE
#define PF_ASCIISTR    "%S"L
#define PF_UNICODESTR  "%s"L
#else
#define PF_ASCIISTR    "%s"
#define PF_UNICODESTR  "%S"
#endif

and then you use the PF_ASCIISTR or the PF_UNICODESTR macros in your format string, exploiting the C automatic string literals concatenation:

_tprintf(_T("There are %d ") PF_ASCIISTR _T(" over the table"), 10, "pens");
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299