Thread: Unexplained "unhandled exception"

  1. #1
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582

    Unexplained "unhandled exception"

    This "unhandled exception" case is unexplained. The behavior of a loop is unexplained as well.

    Code:
    void DrawBMPFont(char Text[1024], short StartX, short StartY, short width, int XBorder, int YBorder)
    {
    	... // notes and local variables
    	if (Text[1023] != 0) // if last possible character is not null
    	{
    		Text[1023] = 0; // the null character must be enforced.
    	}
    	... // the rest of the function
    }
    The place where I set Text[1023] to zero is where the "unhandled exception" notice comes from. Text[1024] is a string supporting 1023 main characters and an extra for the null character. In case I use a string that is unexpectedly too long, I set Text[1023] to zero to ensure I get the null character. When defining an array, the highest value that can be used is one less than that defined. That is:

    int sample[12];

    I can use sample[11] just fine but not sample[12]. Why is it giving the "unhandled exception" notice here? The array is 1024 long and the one accessed is within the range at 1023. If it was 1024, I can understand that, but this is 1023....

    If I comment the if statement out, another strange problem occurs.

    Code:
    		while ((Text[ArrayIndex] != TextSpace) || (Text[ArrayIndex] != 0x0A) || (Text[ArrayIndex] != 0)) // loop until a space or line feed is encountered
    		{
    			PosX += Font.CharWidth[Text[ArrayIndex]]; // add the character's width
    			ArrayIndex++;
    			LoopCount[0]++; // debugging only
    			
    			if (LoopCount[0] >= 10000000) // ten million loops is way too many
    			{
    				sprintf(DebugDetails, "Warning:  an infinite loop in the first loop is present!"); // for debugging
    				MessageBox(hwnd, DebugDetails, "Debug Results", MB_OK);
    				break; // terminate the function regardless of the function's success
    			}
    		}
    
    		if (LoopCount[0] >= 10000000)
    		{
    			break; // break the main loop
    		}
    This loop is going on infinitely, one reason for the LoopCount stuff - to break the loop in case it's infinite making it easier to close the program. The string being passed to the function is "SampleString" without spaces. That's just 12 characters and the last one would be Text[11] for the array. By the twelfth loop, using Text[12], the string has been terminated and the null character is present. I'm also surprised I'm not getting the "unhandled exception" notice as the array index keeps increasing for each loop and it goes far beyond 1023 as the value. Accessing Text[9999999] when only 1024 is present will definitely cause problems, but strangely it isn't. What's going on here? In case you're wondering, it's to display a bitmap font, as a TGA image. This is basic C stuff and thus not Windows-related.

  2. #2
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by ulillillia View Post
    Why is it giving the "unhandled exception" notice here? The array is 1024 long
    Says who? Just because it's declared char Text[1024] in the argument list doesn't make it so.

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    We need more code, ideally something small which is complete.

    Say
    Code:
    int main ( ) {
      DrawBMPFont("Samplestring", 0, 0, 0, 0, 0);
      return 0;
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    Seems like I need to post the full function, again.

    Code:
    void DrawBMPFont(char Text[1024], short StartX, short StartY, short width, int XBorder, int YBorder)
    {
    	/* This function draws bitmap font text to the screen.
    	
    	Rules:
    	*  The last character's numerical value must be zero.  If it's not zero, it should be set prior to processing to prevent infinite loops.
    	*  Wordwrap occurs only when the space character is encountered.
    	*  Wordwrap should never occur when a "\n" is encountered.  Instead, the row should advance and the x position reset.
    	*  Strings are processed until a space is encountered.  When encountered, wordwrap is checked.
    	*  If there's enough space, the word is placed on the next line.  If not, text wraps to the next line below.
    	*  The X and Y parameters set the position of the upper left corner where text is to be drawn to.
    	*  The width parameter specifies how wide of an area to cover before wordwrap is used.
    	*  The border parameters are used to set padding for easier use and reading
    	*  Processing should continue until the null character is found, from which it stops.
    	
    	*/
    	
    	short PosX = 0; // initialize start positions
    	short PosY = 0;
    	short VSpacing = (short)FontInfo.biHeight; // the vertical spacing
    	unsigned short ArrayIndex = 0; // text array position
    	unsigned short StartArrayIndex; // first character of the current word
    	unsigned short EndArrayIndex; // first character of the next word
    	int DrawXPosition = 0;
    	int DrawYPosition = 0;
    	int SourceXWidth = 2;
    	int DrawXWidth = 2;
    	int SourceXPosition = 0;
    	int LoopCount[3] = {0, 0, 0}; // debugging only
    	
    	width = width + 2 - XBorder*2; // gives the actual width.  +2 for the spacing between characters XBorder*2 because there are two sides
    	SelectObject(HDCBitmap, FontBMPHandle);
    	
    	/*
    	if (Text[1023] != 0) // if last possible character is not null
    	{
    		Text[1023] = 0; // the null character must be enforced.  // Unhandled exception - Huh!?
    	}
    	*/
    	
    	while (Text[ArrayIndex] != 0) // loop until the null character is reached
    	{
    		StartArrayIndex = ArrayIndex; // copy the starting point in the string
    		
    		while ((Text[ArrayIndex] != TextSpace) || (Text[ArrayIndex] != 0x0A) || (Text[ArrayIndex] != 0)) // loop until a space, line feed, or the null character is encountered // an infinite loop, but how?
    		{
    			PosX += Font.CharWidth[Text[ArrayIndex]]; // add the character's width
    			ArrayIndex++;
    			LoopCount[0]++; // debugging only
    			
    			if (LoopCount[0] >= 10000000) // ten million loops is way too many
    			{
    				sprintf(DebugDetails, "Warning:  an infinite loop in the first loop is present!"); // for debugging
    				MessageBox(hwnd, DebugDetails, "Debug Results", MB_OK);
    				break; // terminate the function regardless of the function's success // doesn't seem to execute
    			}
    		}
    
    		if (LoopCount[0] >= 10000000) // to break the main loop
    		{
    			break; // break the main loop // doesn't execute either
    		}
    		
    		EndArrayIndex = ArrayIndex; // copies the current array position to mark the end of a word
    		
    		if (PosX > width) // if too long to display in the given width
    		{
    			PosY += VSpacing; // go down a row
    			PosX = 0; // and return to the leftmost position
    		}
    
    		else // if space remains
    		{
    			if (Text[ArrayIndex] != 0x0A) // if a linefeed is not used
    			{
    				PosX += Font.CharWidth[Text[ArrayIndex]]; // include the space character
    			}
    		}
    
    		if (Text[ArrayIndex] == 0x0A) // the linefeed
    		{
    			PosY += VSpacing; // go down a row
    			PosX = 0; // and return to the leftmost position
    		}
    		
    		ArrayIndex = StartArrayIndex; // reset the array index to where the word started at, now for drawing
    		DrawYPosition = (int)StartY+(int)PosY+YBorder; // the Y position to draw to doesn't change
    		
    		while (ArrayIndex < EndArrayIndex) // repeat until the end of the word is reached
    		{
    			DrawXPosition = (int)StartX+(int)PosX+XBorder; // this one is dynamic and sets the X position to draw to
    			DrawXWidth = (int)Font.CharEndPos[Text[ArrayIndex]]-(int)Font.CharEndPos[Text[ArrayIndex-1]]; // set the width of the area to draw to
    			SourceXPosition = (int)Font.CharEndPos[Text[ArrayIndex-1]]; // the start drawing point is the ending point of previous character
    			AlphaBlend(HDCBack, DrawXPosition, DrawYPosition, DrawXWidth, FontInfo.biHeight, HDCBitmap, SourceXPosition, 0, DrawXWidth, FontInfo.biHeight, AlphaUsage); // draw the anti-aliased character
    			PosX += Font.CharWidth[Text[ArrayIndex]]; // add the character's width
    			ArrayIndex++;
    			
    			LoopCount[1]++;
    			
    			if (LoopCount[1] >= 10000000) // ten million loops is way too many
    			{
    				sprintf(DebugDetails, "Warning:  an infinite loop in the second loop is present!"); // for debugging
    				MessageBox(hwnd, DebugDetails, "Debug Results", MB_OK);
    				break; // terminate the function regardless of the function's success
    			}
    		}
    
    		if (LoopCount[1] >= 10000000)
    		{
    			break; // break the main loop
    		}
    
    		if (Text[ArrayIndex] == 0) // the null character is encountered
    		{
    			break; // terminate the loop
    		}
    
    		LoopCount[2]++;
    
    		if (LoopCount[2] >= 10000000) // ten million loops is way too many
    		{
    			sprintf(DebugDetails, "Warning:  an infinite loop in the main loop is present!"); // for debugging
    			MessageBox(hwnd, DebugDetails, "Debug Results", MB_OK);
    			break; // terminate the function regardless of the function's success
    		}
    	}
    }
    
    // this function is used in this function:
    void DrawScenery()
    {
    	... // the rest of the scenery
    	DrawBMPFont("SampleString", 640, 0, 160, 1, 1); // string to display, x and y positions to draw to, the width for wordwrap, and x and y borders for "padding"
    	... // swapping the front and back buffers 
    }
    The font is a TGA file with alpha channel (for anti-aliasing). The character widths vary. The font is set up like this (as it seems I need it):

    Code:
    struct FontBase
    {
    	short CharWidth[128];
    	short CharEndPos[128];
    	short CharHeight;
    };
    
    unsigned char FontData[70400]; // The mountains background
    LPVOID FontDataPointer;
    BITMAPINFO FontMainInfo;
    BITMAPINFOHEADER FontInfo;
    LPBITMAPINFOHEADER FontInfoPointer;
    HBITMAP FontBMPHandle;
    struct FontBase Font;
    
    void FillFontStructure(unsigned short Character, unsigned short width, unsigned short ArrayIndex)
    {
    	Font.CharWidth[Character] = width;
    	Font.CharEndPos[ArrayIndex] = Font.CharEndPos[Character-1] + width;
    }
    
    void SetUpFont()
    {
    	unsigned short ArrayIndex = 0;
    	Font.CharHeight = FontInfo.biHeight;
    	
    	Font.CharEndPos[0x1F] = 0; // initialize end position of a character before the space, for usage in the text drawing function
    	FillFontStructure(TextSpace, 4, ArrayIndex);
    	FillFontStructure(TextExclamation, 4, ArrayIndex);
    	FillFontStructure(TextQuote, 8, ArrayIndex);
    	FillFontStructure(TextNumber, 12, ArrayIndex);
    	FillFontStructure(TextDollarSign, 10, ArrayIndex);
    	FillFontStructure(TextPercent, 12, ArrayIndex);
    	FillFontStructure(TextAmpersand, 11, ArrayIndex);
    	FillFontStructure(TextApostrophe, 4, ArrayIndex);
    	FillFontStructure(TextLeftParenthesis, 6, ArrayIndex);
    	FillFontStructure(TextRightParenthesis, 6, ArrayIndex);
    	... // and so on, for each of the ASCII characters
    }
    Last edited by ulillillia; 04-19-2007 at 08:40 AM. Reason: First code tag left out

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    The problem is that setting Text[1023] is a completely bogus operation. The data you passed it is just a string (a LITERAL, by the way, which means you couldn't modify any of it anyway), which is nowhere near 1024 characters long. So accessing Text[1023] is illegal.

    Just because you've declared the parameter as Text[1024] doesn't mean it somehow gets magically morphed into a 1024-byte object when it gets passed.

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > Font.CharWidth[Character] = width;
    > Font.CharEndPos[ArrayIndex] = Font.CharEndPos[Character-1] + width;
    Why isn't this Character, to match the other array index?

    > if (Text[1023] != 0)
    Given that you're calling the function with a string constant, then
    a) there is no [1023] element of that string
    b) trying to write zero into it "just in case" will at best just trash some memory, somewhere in the middle, corrupt your code and at worst just result in an immediate segfault.

    All I can suggest is you step through the code one line at a time with the debugger, and put ArrayIndex into a watch window, and see what happens.

    I notice that you have nested while loops on the same condition. Are you sure you're not just skipping the \0 because of some faulty index arithmetic (like incrementing when already at the \0 position?)
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  7. #7
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    Well, I didn't know it would be otherwise unlimited as it seems to go out to 10 million without it. The string I'm passing to the function has just 12 characters with a thirteenth one being the null character. Using my DebugTest variable (the only way I know how to debug), I see that the result of Text[12] is indeed 0. The result of Text[11], before this, is 103 which corresponds to the lowercase "g" and exactly as expected. Text[10] shows 110 as the value, the lowercase "n". For the string "SampleString", it's exactly expected. I think I may have found the cause and it's a common mistake I made when using my previous tool. Look at the condition of the first nested while loop carefully.

    while ((Text[ArrayIndex] != TextSpace) || (Text[ArrayIndex] != 0x0A) || (Text[ArrayIndex] != 0))

    I'm encountering the null character which makes the third part false. But, since the null character is not a space nor a linefeed, the other two conditions are true. Since "OR" is used, and that at least one of the statements is true, the loop would just continue on forever no matter if spaces or linefeeds are encountered. It's a common mistake I make. For if statements, "OR" would indeed be used, but for loops, it should be the opposite intention for if's, meaning that "AND" would be used.

    I don't know how to get the debugger to step through code one line at a time. I pretty much don't know how to use it (Visual C++ 2005 Express). The DebugTest things I mention here and there are the only way I know of of checking values. Once I get text displayed on the screen, it'll be much easier to monitor variables without even needing the debugger, good for those beta testing things.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Sleep() causing unexplained reduction in cpu load
    By sajanphilip in forum C++ Programming
    Replies: 4
    Last Post: 02-26-2008, 02:58 AM
  2. Unexplained Segfault
    By crepincdotcom in forum C Programming
    Replies: 6
    Last Post: 08-24-2006, 04:19 PM
  3. An "throwing exception" issue
    By viniciusMarques in forum Windows Programming
    Replies: 3
    Last Post: 06-02-2004, 10:38 AM
  4. "Microsoft C++ exception"?!?
    By SMurf in forum Windows Programming
    Replies: 1
    Last Post: 02-02-2002, 04:24 PM