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.