Home > front end >  When is it better to populate an array with fixed index macros vs by incrementing an index?
When is it better to populate an array with fixed index macros vs by incrementing an index?

Time:10-13

When would it be better to proceed this way

#define PREFIX_IDX 0
#define SUFFIX_IDX 1
#define ARRAY_DATA_SIZE 2

int data[ARRAY_DATA_SIZE ] = 0;

int main() {
  data[PREFIX_IDX] = 6;
  data[SUFFIX_IDX] = 19;
  return 0;
}

compared with

#define ARRAY_DATA_SIZE 2

int data[ARRAY_DATA_SIZE ] = 0;

int main() {
  uint8_t currIdx = 0;
  data[currIdx  ] = 6;
  data[currIdx  ] = 19;
  return 0;
}

I was told with small arrays it was better to use fixed macros, but I feel like it does not scale very well. What would be the general advice for this?

CodePudding user response:

You should avoid macros whenever possible, and it's possible in this case. If you are using at least C 11 (and you should be), you can use constexpr to declare your magic constants at compile time:

static inline constexpr auto PREFIX_IDX = 0u;
static inline constexpr auto SUFFIX_IDX = 1u;
static inline constexpr auto ARRAY_DATA_SIZE = 2u;

That said, you need to match your code to the semantics of what you're doing. This makes your code more readable. The size of the array doesn't matter. What matters is if you've attached different meanings to different elements of the array.

You've attached a sematic tag of "prefix" to data[0] and one of "suffix" to data[1]. Since elements of the array have magic roles, you should index them with your magic constants.

int data[ARRAY_DATA_SIZE];

int main() 
{
   data[PREFIX_IDX] = 6;
   data[SUFFIX_IDX] = 19;
   return 0;
}

Depending on how complex your actual code is, it might be better to forego the array entirely and use separate variables. However, I've had code where you have to treat things as the same type of thing in one place and as different types in another, so an array is definitely OK.

CodePudding user response:

Adding to what Spencer said (don't use #define):

Actually, it would be best to use an enumeration type. This idiom lets you have the benefits of named fields when you want to refer to a specific one, but also still lets you iterate over them if you want to process them all.

But, with a distinct enum type, you can wrap it in a class that implements operator[] and ensures that you use only those enumerations as subscripts, rather than arbitrary values.

Even if you don't do that, it serves to show that the names are related in a group, and it automatically increments them as you define them.


Also, int data[ARRAY_DATA_SIZE ] = 0; is confusing. You are initializing an array with an int, and the special value zero at that. This is not a pointer. A global array of ints will be zeroed anyway. So what is this for? Is it a mistake? Readers will wonder.


Of course, this could be an X-of-Y problem. If you are incrementing as with:

  uint8_t currIdx = 0;
  data[currIdx  ] = 6;
  data[currIdx  ] = 19;

the right answer is to use an aggregate to do it all in one, not increment the subscript at all.

  constexpr int vals[] = {6,19};
  std::ranges::copy (vals, data.begin());

if you used std::array rather than the primitive array syntax, and you were always going to supply the whole thing, then you can just use a single normal assignment.

  •  Tags:  
  • c
  • Related