Archive for March, 2011

The N_ELEMENTS macro

Friday, March 18th, 2011 Nigel Jones

Many years ago I came across a simple macro that has proven to be quite useful. Its usual definition looks something like this:

#define N_ELEMENTS(X)           (sizeof(X)/sizeof(*(X)))

Its nominal use is to determine the number of elements in an incomplete array declaration. For example

void foo(void)
{
 uint8_t bar[] = {0, 1, 2, 3, 4};
 uint8_t    i;

 /* Transmit each byte in bar[] */
 for (i = 0; i < N_ELEMENTS(bar); ++i)
 {
  txc(bar[i]);
 }
}

Clearly this is quite useful. However, once you have this macro in your arsenal you will eventually run into a conundrum. To illustrate what I mean consider the following code:

#define BUF_SIZE    (5)
void foo(void)
{
 uint8_t bar[BUF_SIZE] = {0, 1, 2, 3, 4};
 uint8_t    i;

 /* Transmit each byte in bar[] */
 for (i = 0; i < BUF_SIZE; ++i)
 {
  txc(bar[i]);
 }
}

This uses the classic approach of  defining a manifest constant  (BUF_SIZE) and then using it to define the array size and also as the loop limit. The conundrum is this: is one better off using N_ELEMENTS in this case as well. In other words, is the following better code?

#define BUF_SIZE    (5)
void foo(void)
{
 uint8_t bar[BUF_SIZE] = {0, 1, 2, 3, 4};
 uint8_t    i;

 /* Transmit each byte in bar[] */
 for (i = 0; i < N_ELEMENTS(bar); ++i)
 {
  txc(bar[i]);
 }
}

This code is guaranteed to operate on every element of the array bar[] regardless of what is done to the array declaration. For example:

#define BUF_SIZE    (5)
void foo(void)
{
 uint8_t bar[BUF_SIZE + 1] = {0, 1, 2, 3, 4, 5};
 uint8_t    i;

 /* Transmit each byte in bar[] */
 for (i = 0; i < N_ELEMENTS(bar); ++i)
 {
  txc(bar[i]);
 }
}

In this case I have changed the array declaration. The code that uses N_ELEMENTS would still work while the code that used BUF_SIZE would have failed. So from this perspective the N_ELEMENTS code is more robust. However I don’t think the N_ELEMENTS based code is as easy to read. As a result I have oscillated back and fore over the years as to which approach is better. My current view is that the N_ELEMENTS approach is indeed the better way. I’d be interested in your opinion.