I was reading PC Assembly Language by Paul Carter recently. He discussed a very common bug, particularly among new programmers. I have a fascination with non-obvious bugs, especially when their roots extend all the way down into the guts of the system. To those experienced in C, this may not seem obscure at all, but in the "old days" of Comp Sci 142, it would have been an easy mistake to make--and many have.

 char ch;
 while ((ch = fgetc(fp)) != EOF)
 {
  printf("%c",ch);
 }

What's the bug? I actually think this makes a great interview question. Let's look at the assembly output on an x86 platform:
00FD1437  movsx       ecx,byte ptr [ch]
00FD143B  cmp         ecx,0FFFFFFFFh
00FD143E  je          wmain+9Eh (0FD145Eh)

The variable ch is a byte, but EOF is defined as a signed integer of -1 (or 0xFFFFFFFF). The compiler knows that it needs to convert this signed byte into a signed integer, and chooses an appropriate instruction (MOVSX). A value of -1 should still be -1 after this conversion. If the value was simply padded out to 32 bits with 0s, the resulting number would no longer be negative. It would in fact simply be 0x000000FF or 255.

MOVSX stands for (Move with Signed Extend). It pads the value out to 32 bits with 1s since it is negative (essentially copying the most significant bit). In this case, the result is a value of 0xFFFFFFFF. The comparison evaluates to true anytime there is a 0xFF character in the file being read. The loop ends prematurely. This may be a rare enough occurrence to perhaps drive a poor college student mad.

If the ch variable is changed to be an unsigned char, the problem gets even worse. The compiler emits the MOVZX instruction, which always pads the value with 0s, regardless of the most significant bit. The expression can never evluate true because even if fgetc() returns EOF, it is first cast into a char by taking the lowest byte of the low word of the EAX register (AL), then padded back to 32 bits with 0s. Even at the end of the file, the expression will be comparing 0x000000FF to 0xFFFFFFFF.

011B1437  mov         byte ptr [ch],al
011B143A  movzx       ecx,byte ptr [ch]
011B143E  cmp         ecx,0FFFFFFFFh
011B1441  je          wmain+0A1h (11B1461h)

There are of course much better tools to interact with files (which also prevent these types of mistakes) but I still find this trivia fun to explore.