0

I have a class which has a const char * property:

    class A
    {
    public:
        const PROGMEM char* text;
    };

    void setup()
    {
        // A a{"Hello World!"};
        // A a{PSTR("Hello World!")};

        A a;
        a.text = PSTR("Hello World!");

        Serial.begin(9600);
        delay(30);
        Serial.println(a.text);
    }

    void loop()
    {
    }

I’d like to spare some RAM using PROGMEM.

How to initialize a object then?

Obviously, it can’t be like this:

    A a{PSTR("Hello World!")};

This will do:

    A a;
    a.text = PSTR("Hello World!");

However, I need to pass string to the constructor.

2
  • does a.text = F("Hello World!"); do what you want?
    – dandavis
    Feb 22, 2020 at 21:03
  • 1
    @dandavis, it does not. you've missed that a.text is not const __FlashStringHelper* - it's const char*
    – zhekaus
    Feb 22, 2020 at 21:59

2 Answers 2

2

It is a shame that gcc only supports the __flash qualifier in C mode, not in C++, so we have to use PROGMEM instead. Unlike __flash, which qualifies a variable just like const, the PROGMEM attribute only has effect when allocating room for a variable. Once the allocation is done, the compiler forgets about the attribute. In particular, a declaration such as

const PROGMEM char* text;

does not allocate flash space, so it generates the warning

warning: ‘__progmem__’ attribute ignored [-Wattributes]
     const PROGMEM char* text;
                         ^

You can thus forget the attribute when declaring a pointer, as there is no such thing as a “pointer to PROGMEM”. You just use a const char * instead.

Now, the second issue is that, as far as Serial.println() is concerned, the pointer above is a plain const char *, so it will interpret it as an address in RAM, and print garbage. If you want Serial.println() to know you are giving it an address in flash, you should provide it with a const __FlashStringHelper* pointer.

Here is the solution I propose. Tested on an Uno-compatible board:

class A
{
public:
    A(const char* s)
        : text(reinterpret_cast<const __FlashStringHelper *>(s)) {}
    const __FlashStringHelper* text;
};

void setup()
{
    A a{PSTR("Hello World!")};
    Serial.begin(9600);
    Serial.println(a.text);
}

void loop(){}

Edit: After seeing the last version of Juraj’s answer, I must say that I agree with him. Since we are using the Arduino API, it makes more sense for the constructor to take a const __FlashStringHelper*, and for the caller to use the F() macro.

1
  • your solution works fine! thank you very much!
    – zhekaus
    Feb 22, 2020 at 21:56
3

const char* text; is a pointer to constant not a constant pointer (char * const text is a constant pointer). So you can assign a pointer to a constant char array to const char* text; even a pointer to an array in PROGMEM.

The compiler doesn't know the difference between a PROGMEM pointer and a pointer in SRAM. It is on you to work in code with a pointer to PROGMEM the right way.

so remove PROGMEM from const PROGMEM char* text;


add constructor to initialize an object.

A(const char* _text) {
  text = _text;
}

and then

A a(PSTR("Hello World!"));

EDIT:

you could use Arduino's F() macro and __FlashStringHelper type, because it is supported by Serial print and co.

class A
{
public:
  A(const __FlashStringHelper* _text) {
    text = _text;
  }
  const __FlashStringHelper* text;
};

void setup()
{
    A a(F("Hello World!"));

    Serial.begin(9600);
    delay(30);
    Serial.println(a.text);
}

void loop()
{
}

__FlashStringHelper type is trick to distinguish PROGMEM strings from char arrays in SRAM..

4
  • This is not an answer on my question. How to store strings in flash memory? Strings must be passed to the contructor.
    – zhekaus
    Feb 22, 2020 at 16:48
  • 1
    it is unclear what is what you don't know. I enhanced the answer
    – Juraj
    Feb 22, 2020 at 17:25
  • Hum... Your solution won't let use Serial.println. And added user-defined constructor does not any difference here.
    – zhekaus
    Feb 22, 2020 at 22:03
  • @zhekaus, yes, your question combined 3 problems. Edgar got them all, but the true solution is to use F macro, not PSTR, if possible. I enhanced the answer
    – Juraj
    Feb 23, 2020 at 6:18

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.