How can CString be passed to format string %s?

class MyString
{
public:
    MyString(const std::wstring& s2)
    {
        s = s2;
    }

    operator LPCWSTR() const
    {
        return s.c_str();
    }
private:
    std::wstring s;
};

int _tmain(int argc, _TCHAR* argv[])
{
    MyString s = L"MyString";
    CStringW cstring = L"CString";
    wprintf(L"%s\n", (LPCWSTR)cstring); // Okay. Becase it has an operator LPCWSTR()
    wprintf(L"%s\n", cstring); // Okay, fine. But how?        
    wprintf(L"%s\n", (LPCWSTR)s); // Okay. fine.
    wprintf(L"%s\n", s); // Doesn't work. Why? It prints gabage string like "?."
    return 0;
}

How can CString be passed to format string %s?

By the way, MSDN says(it's weird)

To use a CString object in a variable argument function
Explicitly cast the CString to an LPCTSTR string, as shown here:

CString kindOfFruit = "bananas";
int      howmany = 25;
printf( "You have %d %s\n", howmany, (LPCTSTR)kindOfFruit ); 

CString is specifically designed such that it only contains a pointer that points to the string data in a buffer class. When passed by value to printf it will be treated as a pointer when seeing the "%s" in the format string.

It originally just happened to work with printf by chance, but this has later been kept as part of the class interface.


This post is based on MS documentation long since retired, so I cannot link to their promise that they will continue to make this work.

However, before adding more downvotes please also read this blog post from someone sharing my old knowledge:

Big Brother helps you


    wprintf(L"%s\n", (LPCWSTR)cstring); // Okay. It's been cast to a const wchar_t*.
    wprintf(L"%s\n", cstring); // UNDEFINED BEHAVIOUR
    wprintf(L"%s\n", (LPCWSTR)s); // Okay, it's a const wchar_t*.
    wprintf(L"%s\n", s); // UNDEFINED BEHAVIOUR

The only thing you can pass to this function for %s is a const wchar_t*. Anything else is undefined behaviour. Passing the CString just happens to work.

There's a reason that iostream was developed in C++, and it's because these variable-argument functions are horrifically unsafe, and shoud never be used. Oh, and CString is pretty much a sin too for plenty of reasons, stick to std::wstring and cout/wcout wherever you can.


CString has a pointer as the first member:

class CStringA
{
      char* m_pString;
};

Though it is not char* (even for ANSI CString), it is more or less the same thing. When you pass CString object to any of printf-family of functions (including your custom implementation, if any), you are passing CString object (which is on stack). The %s parsing causes it it read as if it was a pointer - which is a valid pointer in this case (the data at very first byte is m_pString).


Generally speaking it's undefined behavior. According to this article Visual C++ just invokes the conversion from CString to a POD type to cover you - that is permissible implementation of undefined behavior.