Const char foo[] = "gives me an error"

I am having trouble initializing a string in a class (foo at the end of the code below). This is my header file...

#ifndef A2I_CHESS_H 
#define A2I_CHESS_H

#include <Arduino.h>
#include <HTTPClient.h>
#include <A2I_commands.h>
#include <Apple2Idiot.h>

/* Apple II <-> ESP Commands */
#define CHESS_GET_AI_MOVE     20
#define CHESS_GET_GAME_STATUS 22
#define CHESS_GET_BOARD       23
#define CHESS_MAKE_MOVE       21

/* Responses */
#define CHESS_INVALID_MOVE    123
#define CHESS_VALID_MOVE      124

#define MAX_GAME_SIZE 110 * 4       // This is probably not enough, but it's fine for development.
                                    // https://chess.stackexchange.com/questions/2506/what-is-the-average-length-of-a-game-of-chess
                                    // times four because one move is "e7e5"

class Chess {

    public:
        byte appId = APP_CHESS;     // This is "registered" with A2I_commands.h which is part of Apple2Idiot.h
                                    // This id is sent from the Apple to the ESP to tell the esp what app
                                    // is currently active.  The main loop of the ESP sketch then knows to use
                                    // this class to respond to incoming commands from the Apple.


        char game_string[MAX_GAME_SIZE];    // This is probably not enough, but it's fine for development.

        char game_status[25];

        void init(Apple2Idiot *a2ip, HTTPClient *httpp);
        byte handleCommand(byte command);
        byte validateMove(String move_string);

    private:
    
        Apple2Idiot *a2i;
        HTTPClient *http;

        const char api_entry_point[32] = "http://chess-api.herokuapp.com";
        const char foo[] = "hello world";
    
};  
    
#endif

... and here is the error ...

Arduino: 1.8.15 (Linux), Board: "ESP32 Dev Module, Disabled, Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS), 240MHz (WiFi/BT), QIO, 80MHz, 4MB (32Mb), 921600, None"

In file included from /home/equant/projects/apple_ii/apple2idiot/examples/chess/a2i_chess.cpp:4:0:
a2i_chess.h:46:28: error: initializer-string for array of chars is too long [-fpermissive]
         const char foo[] = "hello world";
                            ^
In file included from /home/equant/projects/apple_ii/apple2idiot/examples/chess/chess.ino:14:0:
a2i_chess.h:46:28: error: initializer-string for array of chars is too long [-fpermissive]
         const char foo[] = "hello world";
                            ^
/home/equant/projects/apple_ii/apple2idiot/examples/chess/a2i_chess.h: In constructor 'Chess::Chess()':
a2i_chess.h:23:7: error: initializer-string for array of chars is too long [-fpermissive]
 class Chess {
       ^
/home/equant/projects/apple_ii/apple2idiot/examples/chess/chess.ino: At global scope:
/home/equant/projects/apple_ii/apple2idiot/examples/chess/chess.ino:25:25: note: synthesized method 'Chess::Chess()' first required here 
 Chess chess_app = Chess();
                         ^
Multiple libraries were found for "WiFi.h"
 Used: /home/equant/.arduino15/packages/esp32/hardware/esp32/1.0.6/libraries/WiFi
 Not used: /home/equant/bin/arduino-1.8.15/libraries/WiFi
exit status 1
initializer-string for array of chars is too long [-fpermissive]

I've seen this syntax for initializing a string used in tutorials and in these forums. Is there something about the context of this happening in a header, or a class or under "private:" that makes this illegal? Thanks for any help.

The problem seems to occur because the flexible array (no size specified) is in a class.

I don't know what the proper solution is, but you could always declare the array with an explicit size, particularly as it is a const so its contents will not change

1 Like

That IS the proper solution.

1 Like

OK, but why the inconsistent behaviour between declaring a flexible array in a class or struct compared to as a normal global or local variable, particularly when declared as const ?

Rather amusingly, if you declare the flexible array in a struct anywhere than as the last struct member you get this error message

flexible array member 'test::anArray' not at end of 'struct test'

then if you move it to the end of the struct as suggested you get

initializer for flexible array member 'char test::anArray []'

instead :grinning:

Don't know. It's just one of those things where I shrug and comply.

Thanks for the help to both of you. It's good to learn this. I wish the compiler would do the counting for me, considering how much better a computer is at counting, but oh well, I guess I can do the counting this time. :wink:

Or use a pointer:
const char *foo = "hello world";

Then you need to be careful when writing operator=(), the copy constructor, etc as a trivial (shallow) copy probably won't do what you think / want.

Can you write a small sketch to demonstrate the potential problem?

class myClass {
  public:
    const char *foo = "hello world";
};

myClass myObj1;
myClass myObj2 {myObj1};

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println((uint16_t)myObj1.foo, HEX);
  Serial.println((uint16_t)myObj2.foo, HEX);
}

void loop() {
}

Output:

117
117

The 'foo' member of myObj1 and myObj2 both point to the same memory location rather than their own character string as you might expect with copy-construction.

But is that necessarily a problem? String literals are eternal and immutable anyway, so I think that this would be the desired behavior for the copy constructor of such a class.
If it were a non-const pointer, you could come across some unpleasant surprises, but for a member of const char *, I think this shouldn't be an issue.

In my experience, the compiler optimizes string literals by only making one copy of each unique string. If you add two lines:

void setup()
{
  Serial.begin(115200);
  delay(1000);
  Serial.println((uint16_t)myObj1.foo, HEX);
  Serial.println((uint16_t)myObj2.foo, HEX);
  Serial.println((uint16_t)"hello world", HEX);
  Serial.println((uint16_t)"hello world", HEX);
}

You will see that 117 gets displayed four times.

Yes, I was thinking more of a pointer to non-constant data as @ PieterP mentioned.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.