How sprintf works with CString and std :: string

CString s = "test"; std::string ss = "test"; char z[100]; sprintf(z, "%s", ss.c_str()); // z = "test" : OK char z2[100]; sprintf(z2, "%s", ss); // z2 = "(null)" : OK. undefined behavior is expected char z3[100]; sprintf(z3, "%s", s); // z3 = "test" : How is this possible ?! 

Can someone explain how CString works correctly with sprintf ?

+6
source share
2 answers

This works because the first element of the CString class is a pointer to a char array. In fact, the only field in a CString is a pointer to a string array. This class uses some tricks to hide internal data (e.g. string length, reserved buffer size, etc.) by highlighting one large buffer and then leaving the only class pointer pointed to by the char array to get to these internal fields data, it shifts this pointer at a known offset.

What you need to do is call s.GetBuffer (0); or (LPCTSTR); but using it like

 sprintf(z2, "%s", ss); 

was resolved as designed by the creators of MFC, but of course it works under Windows on other platforms that it might crash.

[edit after comments]

your code will be more secure if instead of c-style casts like (LPCTSTR)s , you will use C ++ cast: static_cast<LPCTSTR>(s); . But very soon you will find out that your code gets ugly with all these static_cast-s, especially if your sprintf-s have a lot of options. This, as far as I remember (and, in my opinion) in design, C ++ style casts are intended for you to rethink your design so as not to use translations at all. In your case, instead of using sprintf, you should use std :: wstringstream (assuming you use the UNICODE assembly):

 #include<sstream> std::wostream & operator<< (std::wostream &out, CString const &s) { out << s.GetString(); return out; } int main(){ CString s = _T("test"); std::wstringstream ss; ss << s; // no cast required, no UB here std::wcout << ss.str(); return 0; } 
+7
source

This behavior of CString , apparently, is not officially supported by Microsoft (it relies on the details of the implementation of CString , which seem to be designed to work in cases similar to the one you specified, but may change in the future).

Note that the CString PCXSTR translation operator MSDN documentation :

 // If the prototype isn't known or is a va_arg prototype, // you must invoke the cast operator explicitly. For example, // the va_arg part of a call to swprintf_s() needs the cast: swprintf_s(sz, 1024, L"I think that %s!\n", (PCWSTR)strSports); 

This is actually a great game, as it is a C-style. I would use C ++ - the static_cast<PCWSTR>(string) style static_cast<PCWSTR>(string) or just CString::GetString() .

Another MSDN documentation page reads (emphasis mine):

Using CString Objects in Variable Arguments Functions

Some C functions accept a variable number of arguments. Well-known example of printf_s . Due to the way this type of function is declared, the compiler cannot be sure what type of arguments it is and cannot determine which conversion operation to perform on each argument. Therefore, you must use an explicit type when passing a CString object to a function that takes a variable number of arguments . To use the CString object in an argument variable, explicitly attach the CString string to the LPCTSTR string, as shown in the following example.

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

Again, I prefer C ++ - the static_cast<PCTSTR> style static_cast<PCTSTR> for the C-style styles used in the MSDN documentation. Or calling CString::GetString() would be very good.


See also this blog post: Big Brother helps you .

And another thread in StackOverflow that discussed this issue .

+5
source

All Articles