Frequently done wrong - don’t also do it!
February 19th, 2008
I’d like to tell you about tiny mistakes I often mention when reading code. All of them are not dramatic but they stay in your code for years and one time something goes wrong they’re hard to discover. There are many things that can quickly be done wrong - that’s the reason why I restrict my post to C++ / MFC related mistakes.
- A widespread misbelief is that this usage of
_tprintfand CString is correct: - CString.Format & printf
- Wrong cast from
void* - Errors due
gets()
CString strWord = _T("another"); _tprintf(_T("One string placed in %s"), strWord);
There’s only one reason why this doesn’t report an error:
CString has size of 4 byte and points on a TCHAR array.
This will fail when inheriting an own string class from CString class in which we override a virtual method from CString class. You’ll get problems when CString grows up from 4 to 8 bytes because it’s now holding two pointers: One to a vtable and one to the TCHAR array. _tprintf doesn’t know what to with the object on the stack and might crash or just report nonsense.
As you can see a small change in your project can lead to lots of errors which are difficult to reproduce.
How to do it right? There are three solutions - it’s just a manner of taste which one you prever:
CString strWord = _T("another"); _tprintf(_T("One string placed in %s"), static_cast<LPCTSTR>(strWord));
or
_tprintf(_T("One string placed in %s"), strWord.GetString());
or
_tprintf(_T("One string placed in %s"), strWord.GetBuffer(0)); strWord.ReleaseBuffer();
First solution is a static cast to an LPCTSTR (which is simply a char*).
Second is the usage of CString::GetString() method which returns a PCTSTR.
Third is save for unicode: CString::GetBuffer() returns a TCHAR* or for unicode WCHAR*
Forgetting to call CString::ReleaseBuffer() can cause problems very difficult to debug as it releases the lock on CString’s inner buffer.
When using CString.Format with Unicode strings, please be aware that if you are using %s the class expects the parameter to be UNICODE, this means this does not work -
CString strString; strString.Format (_T("Output: %s"), myClass.GetOutput(var, VARIABLE)) ;
For correct programming your options are:
CString strString; strString.Format (_T("Output: %s"), CString(myClass.GetOutput(var, VARIABLE))) ;
or for non-MFC usage of A2T makro:
CString strString; USES_CONVERSION; strString.Format (_T("Output: %s"), A2T(myClass.GetOutput(var, VARIABLE))) ;
Again be careful to use the correct character set with printf.
You often see a unsave cast from void* to another type that is often not just done that way because of lazyness but caused by nescience of probably occuring errors.
char c = 0; char* p = &c; void* q = p; int* pp = q;
This works but is dangerous because when decrementing *pp memory will get overriden even thougth pp is an integer.
*pp--; //dangerous!
Doing a c-style or C++ cast avoids big problems ’cause it explecit tells the compiler what type will be written into the other.
int* pp = (int*)q;
int* pp = static_cast<int*>(q);
It’s very common to assign the result of a cast to a suitable pointer:
In C++, use the typesafe new instead of malloc() operator because it can’t alloc the wrong size of memory:
typedef std::complex<double> d; d* q = new d(1,2); // will throw bad_alloc if memory is exhausted if (*q == 1) { // go on... }
new throws a bad_alloc exception if a memory error occurs.
Using the gets() function is bad! Nevertheless you often come accross it’s usage…Fortunatelly it just concerns console apps.
You’d just use this function in exceptional cases - actually it should no more get used apart from having a really good reason to do the opposite. It does not know how many characters can be safely stored in the string passed to it. Thus, if too many are read, memory will be corrupted. Many security bugs that have been exploited on the Internet use this fact! Use the fgets() function instead (and read from stdin).
All those mistake are not just things beginners do. Also lots of other programmers, even experienced ones, make rather large error in their reasoning. Those mistakes are for the reason that a compiler can’t detect them difficult to detect. They may run in your project for years without complaining.
Of course there are many more things that can be do wrong without any knowledge of it but having heard of some frequently done wrong things might help to prevent them. At least I hope so!





