View more

View more

View more

### Image of the Day Submit

IOTD | Top Screenshots

### The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.

# Your Worst "Gotchas" ever in programming

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

69 replies to this topic

### #21frob  Moderators

Posted 28 January 2013 - 04:56 PM

Debuggers not being trustworthy is a big problem.  This is especially true for people working on new game consoles.  Whenever a fancy new devkit comes along there are always times when the debugger will outright lie to you about the state of the hardware.  Also on new systems you will run across occasional compiler and optimizer bugs.  This can require extensive debugging at the assembly level, taking great care to ensure values were not optimized out, or moved to another area, or otherwise behaving in a rational but unexpected way.

It can take a lot of work, and when you finally do get the 'a-ha' moment discovering that it is a debugger or compiler error, you then get the follow-up event of how to work around it.

Hardware bugs.  They can happen with bad firmwares, or inconsistent documentation where it really isn't a bug, just an undocumented feature.  They happen more often when the processors get very hot.  That's exactly what happens during games with compute-intensive processing and gamers with overclocked chips.  Several games, like GuildWars, take steps to detect and notify users when it occurs.

These also happen on early firmwares of game consoles.  Since they are covered under NDA you don't often hear the horror stories.  When you have documentation that says a function does something, and it doesn't do it, you must spend hours trying to figure out if it is your code doing the wrong thing, or if it is the black box doing the wrong thing.  Then you wait for the next firmware update and hope your problem is fixed.

As for the general problems, I've seen all kinds of things:

Metaprogramming is just plain painful, don't submit it to the codebase.  Not only are there 'gotchas' all over the place, they fail spectacularly on data-driven development.  If you need to calculate Fibonacci numbers that are known at runtime, go ahead.  Otherwise either create a data table of pre-computed values or compute it at runtime.

Badly formed macros (#define) that include sequence points or don't properly place things in parenthesis, or even worse include complete statements, can be a source of frustration.  They are not expanded in the debugger, are not type safe, and just cause extra work.  I've spent many hours going through macro bugs.

Nested macros, especially nested macros that build complex structures should be avoided.  While it may be nice to have a few macros that convert a long list of labels into an enum, a corresponding string table listing the enum names, and a corresponding automatic registration at game startup, and so on... those macros really are a bad idea.  When they are fully debugged and have been used for several years they can be moderately useful, but there is a high cost to making them work correctly.

Check out my book, Game Development with Unity, aimed at beginners who want to build fun games fast.

Also check out my personal website at bryanwagstaff.com, where I occasionally write about assorted stuff.

### #22phantom  Members

Posted 28 January 2013 - 04:58 PM

Earlier today, while copying code from our old texture processing tool to the shiny new C# & C++/CLI hybrid I'd been making I was surprised when previously working code to parse a DDS file was no longer working correctly.

The code was a direct copy & paste job and the DDS was a known working one.

Debugger gave garbage for the DDS entries, yet looking at the source file in the hex editor everything checked out.

Old tool was 32bit.

New tool was 64bit.

DDS structure the code used had a void* hidden in it.

Data alignment hilarity ensured.

*sigh* That's a few hours of my life I won't get back...

(Aside note: If you copy data into a System.IO.MemoryStream object make sure you seek back to 0 again or you can't copy that data back out as it is EOF : that one only took me a minute to figure out...)

### #23phantom  Members

Posted 28 January 2013 - 05:11 PM

Debuggers not being trustworthy is a big problem.  This is especially true for people working on new game consoles.

Annoyingly not just new consoles; back in 2008 I was working on a PS2 game (using good old Codewarrior!) and my team leader called me over as he was having problems with a function which seemed to be working yet in a debug session a variable was clearly not showing anywhere near the correct value.

It was near the end of the day so I only quickly looked it over before he went home but I could see nothing wrong in the code.

I get in the next morning around 10am to find him, our lead and another senior programmer all staring at his monitor trying to figure out what is going on. I join them and for a while, when another programmer joined us, it had 5 of us looking at it all puzzled.

After a while they started looking at the assembly level at which point I took myself away to read over the PS2 assembly docs, as I didn't know it at that point, before returning to the collection of programmers and looking over the code.

After about 10mins and some heavy thinking I declared I had figured it out - the compiler, despite being in debug mode, had decided to optimise away the assignment to the variable, instead keeping it in a register to pass directly to the next function call. The debugger, on the other hand, was blissfully unaware of this fact and carried on regardless.

Net result; panic disappeared and my estimation went up in the eyes of our lead significantly (at this point I'd only been at the company about a month) so much so that he a) trusted me with some large refinements to our scripting system and b) often requested me on projects after that as he knew I knew my stuff

In fact that's not a bad tip for new people; do something to get yourself noticed when you join somewhere. Raising your stock early on is a very good move

### #24Schoening  Members

Posted 28 January 2013 - 05:37 PM

Worst for me would mean most obvious one:

Missing:  }

### #25MajorTom  Members

Posted 28 January 2013 - 06:29 PM

Just a quick addition, since I have little time.

Symptom code:

int i = 0;

printf( "i = %d", i );

Output:

"i = 1"

It took four senior coders to work out what was going wrong here. The issue was overflowing buffers -> memory corruption. Causing data to not be assigned the value it was assigned. That was a fun monday morning.

Saving the world, one semi-colon at a time.

### #26ChaosEngine  Members

Posted 28 January 2013 - 08:23 PM

semi-colon at the end of an if statement.

Why are you ignoring the state of the flag?? Why do you keep going into that code block? WHY??

Maybe the code isn't synced with the debugger! I'll rebuild. Nope, same problem. Stupid compiler/debugger/toolchain!

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight

### #27zalzane  Members

Posted 28 January 2013 - 09:56 PM

pretty much anything i've done involving c++ has been riddled with gotchyas, I hope I never have to use that language again

### #28Bacterius  Members

Posted 28 January 2013 - 10:20 PM

This took me back about 4 years, when I was working on my capstone uni project. Me and a friend were getting linker errors - something along the lines of " somevariable already defined in somefile.obj". No compilation errors - all our headers were #ifndef guarded, so we were scratching our heads for near four days.

The obvious thing, in retrospect, is that #ifndef guards only stop code declaration from happening twice. If you (as we did) put actual data in a header file (say, something like int someCount; that is not static), it doesn't matter if the .h file is guarded, when its included multiple times the linker will try allocate the variable again, and will fail, giving an error like above.

This happened to me as well, I was banging my head on the desk after looking over header guards for the n'th time, and then finally discovered what was going on with google's help (I was touching C++ for the first time). It was especially puzzling since I didn't have variables, but utility functions inside the header - doing so obviously worked for class methods, as they were inside the class declaration, but not for free functions. Took me something like three-four days to figure it out as well.

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

### #29BeanDog  Members

Posted 28 January 2013 - 10:22 PM

In PHP, when I realized 0 == "pizza".

### #30Oberon_Command  Members

Posted 28 January 2013 - 10:24 PM

In PHP, when I realized 0 == "pizza".

Is this the sort of thing that makes people say "TRWTF is PHP?"

### #31laztrezort  Members

Posted 28 January 2013 - 10:43 PM

POKE 649, 0

Full Disclosure: I had to look that one up, my memory is not that good (disables the keyboard on the C64)

The latest one that tripped me up was when learning the OpenTK API & expanding my OpenGL fu, I could not get my shader to draw anything. Tried everything (seemingly) obvious (disabled culling, depth testing, simplified the shader to bare minimum, spot checks for errors, ran through gDebugger, etc.)

After beating my head against it long into the night, the next morning before work I had a moment of clarity and realized the obvious - I was doing the view-proj multiplication in the wrong order. Yep, 10 seconds later 'twas fixed.

### #32szecs  Members

Posted 28 January 2013 - 10:49 PM

Another one: it took me three days with Visual Basic scripting for a NI software. Totally random crashes on my machine, unable to reproduce on others (but it was hard to try on other platforms), some emailing with the Support, when it turned out it was a bug in their software, my code was just fine.

The bug (memory corruption) was caused by a feature, that's behaviour was changed in the very version of the software I had. I guess they didn't debug it yet...

### #33Cornstalks  Members

Posted 29 January 2013 - 12:08 AM

I was using Racket the other day and searching a string with some regex, trying to find a backslash followed by a particular set of characters in a sub-capture group. I had the regex "\\(some|subcapture|group)" and it wouldn't catch things it should've matched, like "\a" (which, escaped would be like "\\a"). I figured out faster than I expected to that "\\(" actually means "a single escaped backslash that is escaping the parenthesis" and the correct regex I wanted was really "\\\\(some|subcapture|group)" (just to match something like "\a"). It wasn't long until all my regexes became a mess of backslashes...

Some recent ones for me have been working with Android, where everything is pretty much an asynchronous event system, but only certain functions can be called from certain threads (or perhaps two events can happen simultaneously and call the same function and cause conflicts), where I'd get various exceptions regarding thread conflicts or "not called from the main/GUI thread" exceptions. Grrrr.

I had a fun one the other month. We were working on a C project (most of the code was written by my boss so I wasn't familiar with it) that kept randomly crashing. It was so sporadic that I was guessing we were trashing memory somehow, but it was totally unclear where. I was the only one working with the code too, because my boss moved on to other stuff. Our investor/client wanted the prototype to show to other potential investors to raise capital, but the program was simply unstable. Anyway, I finally found the bug (after my boss had been digging through the code for hours): in one point, an array was being allocated with calloc (which takes the size of each element and the number of elements in an array to create) and reallocated with realloc (which just takes the total size in bytes, not the number of elements or the size of each element). The code was passing the number of elements to realloc (instead of the total number of bytes), as if it were calling calloc, so instead of growing the array it was shrinking it and we'd eventually trash the heap as the program went on. Honestly, I don't even think calloc should exist. Inconsistent calling semantics do no one any favors.

Ooooh, and another one. Signals. Everyone learns about using signals in C in school, and they always show how to use the signal() function to register callbacks. What everyone forgets to mention is that the signal() function is more or less deprecated and it is recommended to not use it because its effects vary across UNIX implementations. Plus, it's got undefined behavior in multithreaded apps. Well, our program was multithreaded and despite registering for callbacks, our program would still get killed from signals we should've been catching. It took some googling to find sigaction(), which is the "proper" way to do it that actually handles multithreaded programs. Screw you signal(), and may people stop teaching others about you.

Edited by Cornstalks, 29 January 2013 - 12:16 AM.

[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

### #34smr  Members

Posted 29 January 2013 - 12:09 AM

JSON requiring quotes around keys when JavaScript doesn't. Gets me once every couple of weeks.

### #35Sik_the_hedgehog  Members

Posted 29 January 2013 - 12:23 AM

In PHP, when I realized 0 == "pizza".

Is this the sort of thing that makes people say "TRWTF is PHP?"

Yes. Javascript has some funny ones too, and don't get me started on languages where null compared to anything is null (because all operations with null return null), resulting in hacks like isnull() and such. And in case you're curious about that piece of code: "pizza" is converted to an integer, and since it has no digits in it, it turns into 0 =/

As for gotchas: once I was filling a buffer using a pointer (i.e. *ptr++ = ...). Problem: the buffer got resized by calling realloc as needed as more data was processed. Guess what happened when the memory block was moved around when reallocated. It was so bad that the debugger couldn't tell where the error happened and valgrind outright crashed. Had to use printfs and place breakpoints around to figure out what line was crashing.

At least that was an easy fix: instead of doing *ptr++ = ...; do ptr[pos] = ...; pos++;

Don't pay much attention to "the hedgehog" in my nick, it's just because "Sik" was already taken =/ By the way, Sik is pronounced like seek, not like sick.

### #36alnite  Members

Posted 29 January 2013 - 08:05 PM

Just a quick addition, since I have little time.

Symptom code:

int i = 0;

printf( "i = %d", i );

Output:

"i = 1"

It took four senior coders to work out what was going wrong here. The issue was overflowing buffers -> memory corruption. Causing data to not be assigned the value it was assigned. That was a fun monday morning.

I was debugging a student's code that had a very similar problem.  He declared an array of size 4, and accessed index #4 later in the code.  Unbeknownst to him (and me), that rewrites the value of another variable rather than throw an exception.  Took me a couple hours and several couts to spot the issue.

### #37Sik_the_hedgehog  Members

Posted 29 January 2013 - 10:23 PM

It probably didn't throw an exception because it was technically writing to valid memory. It would only throw an exception when it tries to write to an address without any memory assigned. Same goes for reading.

And yeah, C arrays have that issue, it's done for performance reasons (not checking the size of the array every time it's accessed, which in turn involves knowing the size of the array too). Remember C was made when computers were pretty slow. It was probably better to do the checks only when needed (e.g. if you're handling a range of data you could do the check once outside the loop) than every time you accessed the data.

C++ vectors also have this catch, surprisingly: [] doesn't do boundary checks. To do boundary checks you need to use the at() member function. So e.g. instead of doing vector[4] you'd do vector.at(4). Fun stuff.

Don't pay much attention to "the hedgehog" in my nick, it's just because "Sik" was already taken =/ By the way, Sik is pronounced like seek, not like sick.

### #38Olof Hedman  Members

Posted 30 January 2013 - 05:32 AM

All of them made me a better programmer, and I wouldn't want to not have made them.

### #39BeanDog  Members

Posted 30 January 2013 - 11:46 AM

Yes. Javascript has some funny ones too

Yeah. Like (undefined = 4) evaluates to 4, but (null = 4) throws an error as you'd expect. Or the following:

function foo(a,b) {
return
a + b;
}


foo(3,4) returns undefined, because a semicolon is automatically inserted after the return keyword.

### #40BLM768  Members

Posted 30 January 2013 - 01:17 PM

When I was refactoring my OpenGL code to use OpenGL 3.2 and SDL 2, my code broke. All my GL calls were succeeding, but the screen stayed black. An OpenGL debugger told me that I had a 0 by 0 framebuffer, but I was completely sure that it was giving me a bogus value.

At least a month later:

Turns out I'd forgotten to initialize some class members in my constructor. My framebuffer was indeed 0 by 0. The fix was quite easy.

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.