Click here to Skip to main content
15,352,714 members
Please Sign up or sign in to vote.
5.00/5 (2 votes)
Please run the code below.
<br />int main(int argc, char* argv[])<br />{<br />	int ii = 20;<br />	char tmp[32] = "loveing";<br />	printf("[%d]hello[%s]\n", tmp); //no integer fill the %d format<br /><br />	char szData[52] = {0};<br />	sprintf(szData, "[%d]saying [%s]\n",ii);//no string fill the %s format<br />	printf("%s", szData);<br />	return 0;<br />}

The compiler don't complain in the compile phase, but when you press F5 to debug, 'unhandle exception' prompt. while not debug, run it at once, you just get wield strings or output.

We all use some sort of printf or CString::Format in the code, we are not careful sometimes. So how to handle this problem or check out the bugs in the exist project?
Updated 15-Oct-10 11:36am

Static code analysis tools like Lint and the the one that ships with VS2010 Ultimate, might help you there, but I would say experience is your best friend. With experience you know which constructs to avoid, and which needs extra care. Format specifiers floating around inside a string of characters is one such thing. The example you are giving is maybe not the most deceitful one. Imagine what can happen if the format string itself comes from an extern source, like a language resource DLL, which can be translated by someone else. In this case it's almost impossible to protect yourself.

Use streams wherever possible, which is the safe alternative. And if you need C-style format strings, make sure they are covered in a code review, if written by an unexperienced programmer.

Edit: Missed the date on this one. Why did you bump it Yusuf?
If you're using C++ there's no reason to use printf or the whole god awful family of C relics. Use a stream. Then you get type safety and you won't try reading return addresses off the stack to interpret as strings. Consider rewriting along the lines of:

int main()
    int d = 20;
    const std::string s( "Good Looking!" );

    std::cout << "[" << d << "] Hello " << s << std::endl;

You'll find the code's as fast, probably faster and you've less chance of messing it up as the compiler will tell you.



PS: Missed the date as well... Talking to the dead isn't normally my thing.
Paul Michalik 17-Oct-10 4:20am
Added upvote since of all proposed solutions this is the only one I'd really recommend. Though I appriciate the effort invested by @Joe, I'm sorry to say that, don't do it. You just replace one possible source of errors with another one.
Sorry for the long post, but here's my solution (there is a companion wide string set of functions as well.) The DT... is my library prefix.

There are similar looking functions in Microsoft safe string library, but I was able to cause every one of them to throw exceptions in common scenarios. This set of code had to run in a DLL which logged errors so it simple couldn't throw exceptions (though I suppose it could in very fringe cases.)

<br />#include <span class="code-keyword"><stdarg.h></span><br /><br />inline<br />char* DTSafeStrCopyLen(LPSTR pDst, LPCSTR pSrc, int len)<br />{<br />	if (pDst)<br />	{<br />		if (pSrc && len > 0)<br />		{<br />			while (len && *pSrc)<br />			{<br />				*pDst++ = *pSrc++;<br />				len--;<br />			}<br />		}<br />		*pDst = 0;<br />	}<br />	return pDst;<br />}<br /><br />inline<br />int DTSafeStrCopyLen2(LPSTR pDst, LPCSTR pSrc, int len)<br />{<br />	return DTSafeStrCopyLen(pDst, pSrc, len) - pDst;<br />}<br /><br />#pragma warning(disable:4702) // unreachable code<br /><br />int DTSafeFormatStringV(LPSTR pBuffer, int bufferLen, LPCSTR pFormat, va_list args)<br />{<br />	if (!pBuffer || bufferLen <= 0)<br />		return -1;<br /><br />	int returnLen = 0;<br /><br />	if (pFormat && *pFormat)<br />	{<br />#if _MSC_VER >= 8<br />		__try<br />#else<br />		try<br />#endif<br />		{<br />			returnLen = _vsnprintf(pBuffer, bufferLen, pFormat, args);<br />			if (returnLen < 0)<br />			{<br />				pBuffer[bufferLen - 1] = 0;<br />				returnLen = bufferLen - 1;<br />			}<br />		}<br />#if _MSC_VER >= 8<br />		__except (EXCEPTION_EXECUTE_HANDLER)<br />#else<br />		catch (...)<br />#endif<br />		{<br />			returnLen = DTSafeStrCopyLen2(pBuffer, "!exception thrown during formatting: \"", bufferLen);<br />			returnLen += DTSafeStrCopyLen2(&pBuffer[returnLen], pFormat, bufferLen - returnLen); <br /><br />			if (returnLen < bufferLen - 1)<br />			{<br />				pBuffer[returnLen++] = '"';<br />				pBuffer[returnLen] = 0;<br />			}<br />		}<br />	}<br />	else<br />	{<br />		pBuffer[0] = 0;<br />	}<br /><br />	return returnLen;<br />}<br /><br />int DTSafeFormatString(LPSTR pBuffer, int bufferLen, LPCSTR pFormat, ...)<br />{<br />	if (!pBuffer || bufferLen <= 0)<br />		return -1;<br /><br />	int returnLen = 0;<br /><br />	if (pFormat && *pFormat)<br />	{<br />		va_list args;<br />		va_start (args, pFormat);<br />		returnLen = DTSafeFormatStringV(pBuffer, bufferLen, pFormat, args);<br />		va_end (args);<br />	}<br />	else<br />	{<br />		pBuffer[0] = 0;<br />	}<br /><br />	return returnLen;<br />}<br />

Perform Code Analysis with your code with
switch with your Visual C++ compiler. You need to have Team System or the Ultimate version to do that.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900