4

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];
6
  • 2
    This should be good: #define NUM_COLORS (sizeof colors / sizeof colors[0]) Commented Jan 13 at 19:19
  • Does this work? static ColorButtons color_buttons[sizeof(colors) / sizeof(colors[0])];; then use WeatherVanes approach. Otherwise I'd try X-macros (or better the undef-free variant). Commented Jan 13 at 19:21
  • 1
    It doesn't work because NUM_COLORS must be known at compile-time. You can use macros instead: #define ARRAY_SIZE(X) (sizeof X / sizeof X[0]). Commented Jan 13 at 19:22
  • color_buttons[sizeof(colors) / sizeof(colors[0])] will work. Even if it's a static const, NUM_COLORS is still an integer variable, not a true compile-time "constant" that can be relied upon to allocate memory. You will get either an explicit storage size of ‘color_buttons’ isn’t constant complaint, or a refusal to declare a variably sized array in that context. Commented Jan 13 at 19:22
  • 3
    "But the code below throws an error." - please post the error, verbatim. Commented Jan 13 at 19:23

5 Answers 5

8

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];
Sign up to request clarification or add additional context in comments.

1 Comment

Ah, the old enum trick! I don't find a need for that very often.
7

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.

2 Comments

This is what I went with. It's simple and it works. Thanks for all the help. I learned some things from all of the responses.
The only thing I had to do was cast the result to int, since I'm using NUM_COLORS later on in a for loop.
3

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]))

Comments

2

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

Comments

2

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

4 Comments

What are color_0, color_1, and color_2? They're undeclared vars.
Oh, this compiles with g++, but not gcc. I think this might not be usable for me, and others, who don't use C++?
@Mike color_0 is an enumeration constant with integer value 0x0000ff and so on. The color_##index = 0x##val, macro expands to for example color_0 = 0x0000ff,
@Mike This compiles just fine in gcc. The enum declaration is C23 so you need a fairly recent version or otherwise compile with -std=c23.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.