Still Hard to C

The original Hard to C blog started life about 4 years ago on Blogger. It was meant as a place I could post about the experiences I was making while coding on my projects (mainly in C/C++).

Unfortunately it didn’t get updated due to a number of circumstances, although I have had plenty of moments along the past years where it could have been useful letting off steam about some compiler bug or C/C++ standard issue hitting me by surprise.

Recently I have been updating some code, which inevitably led to some new material, so I wanted to revive Hard to C.

I talked with Mouser and Gothi[c] at DonationCoder.com who graciously agreed to host the blog at this new location, I would like to take the opportunity to express my gratitude for their help.

I added the posts from the original blog here, and I hope some new ones will start to appear soon (when I’m done fiddling with WordPress that is hehe).

What Else Could Go Wrong?

JrDebugLogger is a very nice debug logging library. Much of it’s functionality is implemented through macros to allow it to be selectively left out when compiling. Along the way the author has had some interesting problems to solve, and this post is about one of them.

Assume we use the following macro:

to allow us to perform debug logging with a stream-like interface. We can then do:

which expands to:

Now if the compiler knows that debug_on is false, it can leave out all code related to the debug logging, since it knows it will never be called. If it does not know the value at compile time, the resulting code will contain a very fast check around the call, allowing debug logging to be turned on and off dynamically with little performance overhead.

There is, however, an insidious bug lurking in the corner, waiting to jump at the user. Can you spot the problem? Think about it for a minute or two before reading on.

Consider this use:

it expands to:

This is valid C++, and compiled without warnings on the three compilers I tried. But who does that else belong to?

Let’s see what the standard says:

“An else is associated with the lexically nearest preceding if that is allowed by the syntax.”

This is from the C99 standard (6.8.4.1p3), which was the most clear, however statements to the same effect are present in the C++ standards.

So the above is equivalent to:

which was of course not the intention.

So how can we solve this without giving up the nice properties of the if? The simple solution is to give the if in the macro its own else:

We now get the expansion:

and the compiler will correctly associate the users else with his if. So it is equivalent to:

Thanks to Jesse for the nice topic.

Loophole in Visual C++, Part 2

Here is a slightly more elaborate example:

This program goes through the entire range of the unsigned int type, performing some action for each. It shows the progress by calling a function to compute the ratio of count to the maximum possible value. Again, count is incremented in each step, and hence will reach the value zero at some point.

The program works as expected on the compilers I tried, except for cl.exe from VC7 and VC71 with the /O2 switch, which stop at 25%. In case you wondered about the starting point of 0x3fffffff, that’s the reason — no need to watch your machine chew it’s way through all integers up to 25%.

Looking at the code generated for the loop:

We see that it fails because the two instructions before the conditional jump have been reversed. Again it looks like the optimizer fails to recognize the importance of the increment to the loop.

Additional Trouble

2 plus 2 is 4, but does that generalize?

What is your immediate reaction to this little program?

If it was something along the lines of ‘depends’ then you’re either a raider of the standard, or you’ve just been around C/C++ for too long like me.

The type of an unsuffixed decimal integer constant is the first type from a list in which its value can be represented:

C89 - int, long int, unsigned long int
C99 - int, long int, long long int
C++ - int, long int

Now, the problem with the little program above is that if the int type is 16-bit, then 20000 + 20000 results in an overflow because the maximum value of a 16-bit int is 32767. We are guaranteed that computations involving unsigned operands cannot overflow, but there is no such guarantee for signed operands. So the addition may leave us in the land of undefined behaviour.

I compiled the above example with three DOS 16-bit compilers; Borland, Open Watcom and Digital Mars. None of the programs gave any output when run. Borland warned about the overflow, Open Watcom warned at -w2, Digital Mars did not warn.

What happens is that in the x86 two’s complement representation, 20000 + 20000 overflows and becomes -25536, which is not equal to 40000.

Writing portable, standard compliant C/C++ is not always easy .. and it can be Hard to C the problems.

Loophole in Visual C++, Part 1

Lets start this post by recalling what the gosp^H^H^H^Hstandard has to say about unsigned arithmetic:

“A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type.”

This is from the C89 draft (3.1.2.5p5), statements to the same effect are present in the C99 standard (6.2.5p9) and the C++ standards.

Now consider the following program:

Since count starts at zero and is incremented each time through the loop, the standard tells us it will wrap to zero when it reaches a result that cannot be represented by an unsigned int, making the program terminate. Compiling the program with various compilers gives the expected stream of increasing numbers.

However, if you compile it with cl.exe from Visual C++ using the /O2 switch (maximize speed) you get a somewhat surprising result; a single zero and the program exits. This goes for VC6, VC7 and VC71.

If you initialize count to one instead, the program works fine. So it looks like the optimizer fails to recognize the addition as changing the value of count, and thus optimizes away the loop.

I have not tested the various VC8 betas, so if you have any of them installed, feel free to try it out and post your results (just remember to compile from the command-line using cl.exe and /O2).