User Name Password Register
DaniWeb IT Discussion Community
All
What is DaniWeb IT Discussion Community?
You're currently browsing the Software Development category of DaniWeb, a massive community of 383,425 software developers, web developers, Internet marketers, and tech gurus who are all enthusiastic about making contacts, networking, and learning from each other. In fact, there are 2,617 IT professionals currently interacting right now! Registration is free, only takes a minute and lets you enjoy all of the interactive features of the site.
Please support our Software Development advertiser:
Sep 9th, 2006, 5:47 pm
In a number of text and other references, the author stops short of recommending good practice (a blackbird, perhaps?), and plenty of code and its programmers suffer for it. I took a stab at explaining the basics once before, but I felt like revisiting it again. Since I abandoned the use of a #define macro to define an array size, I find I don't even miss it and my code is to me simpler and easier to follow, as well as being more correct. I just want to proselytize a bit more.

I must mention, however that I am against this type of macro:
#define SIZE 100
I am wholeheartedly in favor of something like this:
#define CPU_SPEED   100000
#define TIMER_BASE ((CPU_SPEED)/1000)
But as you see, that's different from delcaring an array size.

Now as much as we'd all love to believe that the software documentation we are supposed to write is supposed to tell us how everything works, these docs go largely unread and we all head straight to the source code; after all, this is always the actual current state of the code.

What happens in larger projects, at least in my experience, is that this little "shortcut" gets put in some header somewhere. Now it wouldn't be bad if it were just one simple lookup, but too often in my experience it's been too much of a hunt. Let's say I've got a line of code that I am examining:
   Rx.Buffer[Rx.Index++] = uart0;
   if ( Rx.Index >= BUFFER_SIZE )
   {
      Rx.Index = 0;
   }
This all looks dandy, but I have to do some searches to verify that this is doing what is intended. First I look up Rx.
RXTYPE Rx;
Okay. What's RXTYPE? (And don't get me started on typedefs, that'll be an article on its own someday.)
typedef struct
{
   unsigned char Buffer[RX_BUFFER_SIZE];
   int index;
} RXTYPE;
Okay, now what's RX_BUFFER_SIZE?
#define RX_BUFFER_SIZE 40 // must be smaller than normal
Okay, does that match? One more lookup... What was the question again? Oh, yeah.
#define BUFFER_SIZE 100 // used for most buffers
Aha! There's the bug.

What's a simple fix that is guaranteed to be correct?
   Rx.Buffer[Rx.Index++] = uart0;
   if ( Rx.Index >= sizeof Rx.Buffer / sizeof *Rx.Buffer )
   {
      Rx.Index = 0;
   }
What did the #define by me? A lot of indirection and hunting through code. But now with the use of sizeof for a replacement, I don't actually need to look anywhere, I can tell by the idiom sizeof x / sizeof *x that it is correct for an array. I don't even need to care how big the actual array is.

So as the places in the code that once had these magic macros disappear, you're left with about two places left where the macro exists: the definition itself and the array definition. Now why in the world should these two related values be separate?

Hm. No reason, delete the macro and put the actual size at the array definition. Now, less code, more correct, less hunting: easier to maintain code.

But feel free to ignore my advice and take the macro approach. Many feel quite comfortable with what I just railed against. But the next time you are searching through a half-dozen or more files chasing a bug like this, do have a distant memory of reading this article. And if this is the case, spread the news.
This blog entry was written by Dave Sinkula. It has received 3,274 views, 5 comments, and 6 linkbacks. 1 voter has rated this entry 5 out of 5 stars.
AddThis Social Bookmark Button
All Recent Tags

Comments (Newest First)
iMalc | Newbie Poster | Jul 28th, 2007
I prefer to use the following macro:

#define countof(x) (sizeof(x) / sizeof(x[0]))
Dave Sinkula | long time no c | Sep 12th, 2006
You can't use sizeof to initialise the size of an array, after all sizeof what?
Sizeof that array? But that's not available early enough (before array initialisation).
This doesn't work?
   char source[] = "hello world";
   char destination[sizeof source / sizeof *source];
When you start going down the trail, they begin to disappear.

But that's not available early enough (before array initialisation).
Ah, but if you declare it with a "magic number", which would still very much be a "magic number" even through a potentially irrelevant #define, this argument disappears as well. If you know the type, you know the size.
jwenting | duckman | Sep 12th, 2006
You can't use sizeof to initialise the size of an array, after all sizeof what?
Sizeof that array? But that's not available early enough (before array initialisation).

There'll always be a place for symbolic constants of some sort, and in C those are #define's.
In C++ they're const, in Java they're static final.

A #define should of course exist in a headerfile with logical structure, maybe something like constant_declarations.h, and not be sprinkled around the code.

But you're effectively stating in your original post that symbolic constants are too difficult to use and should be abandoned, which is quite untrue.
Dave Sinkula | long time no c | Sep 10th, 2006
No, I take it you didn't understand my point. Like I mentioned, just because it's been done for 30 years doesn't mean that it's the best.

If you take a look at where a #define is used for array size, it would be better served to use the sizeof construct instead. And perhaps passed parameters. And then you're left with two disjoint locations for the #define, or a single location where it makes most sense. In fact, the sizeof construct helps remove the "magic" as well because it exactly preserves the true relationship without hiding it in a secondary symbol.
jwenting | duckman | Sep 10th, 2006
So your advice is to go against what has been found to be best practice over the last 30 years and just hardcode magic numbers everywhere, and to abandon type definitions and put everything in one massive sourcefile because that way you'd not have to search different files for your type definitions.

Not going to fly I'm happy to say.
Post Comment

Only community members can start a blog or comment on blog entries. You must register or log in to contribute.

DaniWeb Software Development Marketplace

Related Blog Entries
Related Forum Threads
All times are GMT -4. The time now is 8:33 pm.
Forum system based on vBulletin Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
©2003 - 2008 DaniWeb® LLC