Smiley's Jan 09 Article

Go To Last Post
16 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Smiley

Re the mention of the "bug" due to commenting out a semicolon...

I make a habit of doing this

// "something" is a relational or volatile
while (something) 
      ;
// or
while (something)
    { };
// rather than
while (something);

Just so it's perfectly clear to me/others. Of course, it doesn't affect what code is generated.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Misra C suggests always using braces - especially with 'if'.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The coding standard I (try to) adhere to also says that if/while/do/etc. should always be followed by a braced statement block as it's far to easy to write:

if (flag_set)
   set_output_high;

and later amend this to be:

if (flag_set)
   set_output_high;
   light_LED;

when what you really meant was:

if (flag_set) {
   set_output_high;
   light_LED;
}

so write:

if (flag_set) {
   set_output_high;
}

in the first place, ready for later additions within the conditional block. The code generated is identical but it's "safer" (which is why MISRA dictate this kind of thing)

Cliff

PS use of lint/splint is also a good idea as it tends to spot this kind of thing.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is the section that Steve is referring to:

Quote:

Statements and Blocks
Expressions such as PORTD = ~i or j -= 128 become statements when they are followed by a semicolon.

PORTD = ~i;
j -= 128;

The semicolon terminates the statement.

Tale of a bug:
I wrote the following statement:

while(QuarterSecondCount < 17600);
QuarterSecondCount = 0;

Then decided that the 17600 wait count was too long so I changed it to 2200:

while(QuarterSecondCount < 2200)//17600);
QuarterSecondCount = 0;

But I wanted to remember the 17600 in case I ever needed it again, so I commented it out and added the new value. Do you see a problem here?

Well, what I meant to say was:

while(QuarterSecondCount < 2200);
QuarterSecondCount = 0;

Which is two statements, the first waits while an interrupt running in the background increments QuarterSecondCount, and once that is finished the QuarterSecondCount is set to zero. What the compiler saw was:

while(QuarterSecondCount < 2200)
QuarterSecondCount = 0;

But the compiler doesn’t see anything following the // comment delimiter. See the problem yet?

Well how about the equivalent statement:

while(QuarterSecondCount < 2200) QuarterSecondCount = 0;

I had accidentally ‘commented out’ the terminating semicolon from the first statement. The compiler doesn’t know about the line breaks, all it sees is a single statement, which says that while QuarterSecondCount is less than 2200, set QuarterSecondCount to 0. So each time the interrupt incremented QuarterSecondCount, this statement set it back to zero. One lousy semicolon gone and everything changes!

This is the kind of bug, that after spending X amount of time locating, you carefully hide it from your boss lest she think you are stupid or careless or both. Fortunately, I am my own boss, so I’ve learned to live with my stupid and careless employee. (I fired myself once, but that just didn’t work out.)


I wrote this when discussing Statements and Blocks to show the reader that bugs can often be very dumb and hard to find. I hope that seeing an experienced programmer admit to doing something this dumb will help the reader feel more confident and empowered when looking for bugs in their own code.

And I try to do like K&R, example:

whlie ((c = getchar()) != EOF)
       putchar(c);

But when I made the error, I was being lazy.

Thanks for pointing this out, especially now that I see that I didn't mention how the QuarterSecondCount gets incremented which could make for an even dumber bug.

Smiley

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to avoid such a problem, I use a macro:
#define donothing ;
so when I see
while(condition)
donothing; // Yeah, it's two semis, but so what

I see I really want to do nothing.

--Rich

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wish K&R had taken Pascal's block begin/end keywords. Or maybe I wouldn't wish so having to type too much. The original K&R was written in the era of folks typing on pre-VT100 terminals and the like, so typing and editing were a PITA.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Or maybe I wouldn't wish so having to type too much

If they had you could always:

#define { begin
#define } end

Just as frustrated Pascal programmers can do this the other way round in C

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

RiJoRi wrote:
// Yeah, it's two semis, but so what

Maybe something like this then:

#define doNothing() do { } while (0)
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ezharkov wrote:

Maybe something like this then:

#define doNothing() do { } while(0)

That would be "DoNothingForever"!

My macro can be used in while() loops, do-while() loops, and for() loops: with the test outside the macro, you have control over how much "nothing" is done. :wink:

--Rich

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

That would be "DoNothingForever

Look again.

It's the standard way to do nothing - for example if you have WinAVR go to the \winavr\avr\include\avr directory and simply "grep while *.h" - have you ever seen so many while(0)'s ?

If you want to know why this is done, read:

http://c2.com/cgi/wiki?TrivialDo...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
That would be "DoNothingForever"!

No it wouldn't.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

TrivialDoLoop - and while(0), for my meager brain, wastes too many brain cycles trying to figure out Why The Hell Would That Code Be There?

Hard enough to cope with the arcane macros that so many people write, then a year later, they recode to eliminate them as too esoteric.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't really get the "TrivialDoLoop" thing. Read the article but ... hum.
Is it used like this:

;some impromptu PIC code 
main:
	movwf TXREG			;send work reg stuff via UART
	m1:
	btfss TXSTA, TRMT	;is trasmit shift reg empty?
	bra m1:				 ;if not ask again

uhm ... "TrivialDoLoop" is simply to wait for some interrupt to happen, right?
If yes why would one want to use it? If I have code to be executed when an interrupt occouts I ... well place it in the interrups-routine.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

wastes too many brain cycles trying to figure out Why The Hell Would That Code Be There?

Then read the link I gave above - it explains exactly why the do{}while(0) construct is often used in header file macros. (so you don't get caught in the semi-colon trap)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I saw the worth of the do{} while (0) for those (Arcane) macros, to prevent misuse when a macro is invoked by someone who didn't know or want to know how the macro is coded.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
who didn't know or want to know how the macro is coded.
..or more to the point, shouldn't need to know.