Mark A. Donohoe
Mark A. Donohoe

Reputation: 30348

How do you define a char array as a constant?

C/C++ noob here. I've defined this in a header file...

typedef unsigned char BitChar[9]; // 8 data bytes (chars) and one width byte (char)

extern BitChar BitFont[];

and I have this in a cpp file...

BitChar BitFont[] = {
    B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,2, // 32 - Space
    B10000000,B10000000,B10000000,B10000000,B10000000,B00000000,B10000000,B00000000,1, // 33 - !
    ...
    B00000000,B00000000,B11100000,B11100000,B11100000,B00000000,B00000000,B00000000,3, // 127 - Unknown
};

It compiles and seemingly runs just fine. However, since it will never change, I thought it should be marked as a constant. How do I mark it as such? What I expected, adding 'const', throws compile errors so I'm stumped. Here's the error...

error: invalid initialization of reference of type 'unsigned char (&)[9]' from expression of type 'const unsigned char [9]' 

Upvotes: 5

Views: 1068

Answers (2)

mpflaga
mpflaga

Reputation: 2827

Please note that the CONST's will still consume RAM space. For large constant arrays you may want to consider placing them into program space (aka Flash or non-volatile space). Below is an example.

const uint8_t BitFont[] PROGMEM = {
    B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,2, // 32 - Space
    B10000000,B10000000,B10000000,B10000000,B10000000,B00000000,B10000000,B00000000,1, // 33 - !
    B00000000,B00000000,B11100000,B11100000,B11100000,B00000000,B00000000,B00000000,3, // 127 - Unknown
};
#define BITFONT_X_SIZE (sizeof(BitFont)/sizeof(BitFont[0]))

void setup() {
  Serial.println("");
  Serial.print(F("BitFont[] = "));
  for(int y = 0 ; y < BITFONT_X_SIZE ; y++) {
    Serial.print(pgm_read_byte_near( &(BitFont[1]) ) );
    Serial.print(F(","));
  }
  Serial.println("");
}

Note there are THREE things happening. First the PROGMEM macro is used by the avr-gcc to link this into program space. Second the pgm_read_byte_near function is used to read the pointer from program space. As it requires the use of a special opcode, to read program space.

Third and not directly related to your example, but similar is the F() function used in Serial.print()'s which likewise places the constant string into program space. Otherwise strings within the Serial.print consume static ram.


Alternatively you can create a matrix

#define BRICK_COLUMNS 9
const uint8_t BitFont[][BRICK_COLUMNS] PROGMEM = {
    {B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,2}, // 32 - Space
    {B10000000,B10000000,B10000000,B10000000,B10000000,B00000000,B10000000,B00000000,1}, // 33 - !
    {B00000000,B00000000,B11100000,B11100000,B11100000,B00000000,B00000000,B00000000,3} // 127 - Unknown
};
#define BITFONT_X_SIZE (sizeof(BitFont)/sizeof(BitFont[0]))

void setup() {
  Serial.println("");

  for(int x = 0 ; x < BITFONT_X_SIZE ; x++) {
    Serial.print(F("BitFont["));
    Serial.print(x);
    Serial.print(F("][y] = "));
    for(int y = 0 ; y < BRICK_COLUMNS ; y++) {
      Serial.print(pgm_read_byte_near ( &(BitFont[1]) ));
      Serial.print(F(","));
    }
    Serial.println("");
  }
  Serial.println("");
}

Upvotes: 1

AnT stands with Russia
AnT stands with Russia

Reputation: 320381

Just add const. This

extern const BitChar BitFont[];
...
const BitChar BitFont[] = {
    B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,2, // 32 - Space
    B10000000,B10000000,B10000000,B10000000,B10000000,B00000000,B10000000,B00000000,1, // 33 - !
    ...
    B00000000,B00000000,B11100000,B11100000,B11100000,B00000000,B00000000,B00000000,3, // 127 - Unknown
};

should work perfectly fine in C. (Assuming that your compiler knows what these B00000000 identifiers mean.)

This will also work perfectly fine in C++. The only potential for error in the C++ version is based on C++-specific properties of const. If the definition does not see the declaration, then you have to specify the explicit extern in the definition as well

extern const BitChar BitFont[] = {
    B00000000
    ...

because in C++ const objects have internal linkage by default. However, if the declaration already contains the extern and the definition can see the declaration, then that extern in the definition is optional.

The error message you quoted suggests that somewhere in your code you are trying to initialize a reference of type BitChar & (aka unsigned char (&)[9]) with a const-qualified BitChar array. This will not work, since it violates the basic rules of const-correctness. The reference has to become const-qualified as well, i.e. it has to change to const BitChar & (aka const unsigned char (&)[9]).

Upvotes: 9

Related Questions