Mike
Mike

Reputation: 814

Declare array based on size of another array

I have an array of color codes, the size of which is known at compile time. I want to declare another array of the same size. But the code below throws an error.

I can, of course, declare the size as a global constant and then use that in the declaration of both arrays. But I don't want to keep adjusting the size constant when I add new colors. Is there a way to do this? (The variables are global.)

static const char *colors[] = {"#0000ff",
                                "#00ff00",
                                "#ff0000",
                                "#ffff00",
                                "#ff00ff",
                                "#00ffff",
                                "#ffffff",
                                "#000000",
                                "#ff8040",
                                "#c0c0c0",
                                "#808080",
                                "#804000"};

static const int NUM_COLORS = sizeof(colors) / sizeof(colors[0]);
static ColorButtons color_buttons[NUM_COLORS];

Upvotes: 4

Views: 165

Answers (5)

Lundin
Lundin

Reputation: 214300

Just to yet again propagate for X-macros - this is a perfect task for them. All you need to do is to maintain an X macro list at the top and then you can have string literals, values, item counting, look-up tables etc etc all at compile time. Quite powerful and maintainable, but at the expensive of readability for those not used at reading X macro code.

Example:

#include <stdio.h>
#include <stdint.h>

#define COLOR_LIST(X) \
/*  index  val    */  \
  X(0,     0000ff)    \
  X(1,     00ff00)    \
  X(2,     ff0000)    \

/* declare an array of strings with a certain format: */
#define COLOR_STRING(index, val) [index] = "#" #val,
static const char *color_str[] = { COLOR_LIST(COLOR_STRING) };

/* declare an enum to translate indices to values: */
#define COLOR_ENUM(index, val) color_##index = 0x##val,
typedef enum : uint32_t
{
  COLOR_LIST(COLOR_ENUM)
} color_t;

/* declare an enum for the sole purpose of counting items: */
#define COLOR_COUNT(index, val) color_count_##index,
typedef enum
{
  COLOR_LIST(COLOR_COUNT)
  NUM_COLORS
} color_count_t;


int main() 
{
  printf("Total colors: %d\n", NUM_COLORS);
  printf("[0] string: %s  value: %.6X\n", color_str[0], color_0);
  printf("[1] string: %s  value: %.6X\n", color_str[1], color_1);
  printf("[2] string: %s  value: %.6X\n", color_str[2], color_2);

  // ...or if you will
  #define COLOR_PRINT(index,val) printf("[" #index "] string: %s  value: %.6X\n", color_str[index], color_##index);
  COLOR_LIST(COLOR_PRINT)

}

Output:

Total colors: 3
[0] string: #0000ff  value: 0000FF
[1] string: #00ff00  value: 00FF00
[2] string: #ff0000  value: FF0000

Upvotes: 2

Vlad from Moscow
Vlad from Moscow

Reputation: 311048

In C opposite to C++ variables with the qualifier const are not part of compile-time constant expressions. And you may not define a variable length array with static storage duration either in a file scope or in a block scope with storage class specifier static.

Instead you could write for example

static enum { NUM_COLORS = sizeof(colors) / sizeof(colors[0]) };
static ColorButtons color_buttons[NUM_COLORS];

Another approach is the following

static ColorButtons color_buttons[sizeof(colors) / sizeof(colors[0])];
static const size_t NUM_COLORS = sizeof(colors) / sizeof(colors[0]);

though there are two times used the expression sizeof(colors) / sizeof(colors[0]).

Or if your compiler supports C23 then

constexpr size_t NUM_COLORS = sizeof(colors) / sizeof(colors[0]);
static ColorButtons color_buttons[NUM_COLORS];

Upvotes: 8

Zakk
Zakk

Reputation: 2063

To add to the previous answers:

#define ARRAY_SIZE(X) (sizeof X / sizeof X[0])

It's reusable also.

If you are not sure whether your array has any elements (aka can be a null pointer), you can return 0 in this case:

#define ARRAY_SIZE(X) (X == NULL ? 0 : sizeof X / sizeof X[0])
int  full[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int* empty  = NULL;
    
printf("full size = %u\n", ARRAY_SIZE(full));   // 10
printf("empty size = %u\n", ARRAY_SIZE(empty)); //  0

Upvotes: 2

dbush
dbush

Reputation: 224387

The size of a file scope array, if specified, must be an integer constant expression. A variable qualified with const does not qualify as such as expression (this differs from C++ which does allow this).

Instead of making NUM_COLORS a variable, make it a preprocessor symbol:

#define NUM_COLORS  (sizeof(colors) / sizeof(colors[0]))

The expression that this expand to is an integer constant expression, and can therefore be used as the size of an array.

The only thing you'll need to look out for is to make sure you don't redefine colors at a lower scope where you would also use NUM_COLORS.

Upvotes: 7

0___________
0___________

Reputation: 67721

static const int NUM_COLORS - const variables are not constant expressions required as size of the static storage duration arrays.

You need to create a macrodefintion:

#define NUM_COLORS (sizeof(colors) / sizeof(colors[0]))

Upvotes: 3

Related Questions