12

I'm currently changing some libraries to use flash instead of RAM for string storage so that I do not run out of SRAM on a project.

Some strings in the library are declared in this manner:

const char *testStringA = "ABC";

This is different to how I normally see this done:

const char testStringB[] = "DEF";

However, I think that these two are equivalent when declared const and initialised in the declaration. Both work fine in code.

I attempted to move these to flash:

const prog_char *testStringC PROGMEM = "GHI";

I then found that this didn't work. It was producing gobbledegook when printed.

However, following the more usual pattern of:

const prog_char testStringD[] PROGMEM = "JKL";

works fine.

I can see in the disassembly:

00000068 <testStringC>:
  68:   04 01                                               ..

0000006a <_ZL11testStringD>:
  6a:   4a 4b 4c 00                                         JKL.

So it is clear the pointer and PROGMEM results in the string/array not being initialised.

Why is this?

Example code:

#include <avr/pgmspace.h>

const int BUFFER_LEN = 20;

const char *testStringA = "ABC";
const char testStringB[] = "DEF";
const prog_char *testStringC PROGMEM = "GHI";
const prog_char testStringD[] PROGMEM = "JKL";

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    char buffer[BUFFER_LEN];

    Serial.println(testStringA);
    Serial.println(testStringB);

    strncpy_P(buffer, testStringC, BUFFER_LEN);
    Serial.println(buffer);

    strncpy_P(buffer, testStringD, BUFFER_LEN);
    Serial.println(buffer);

    delay(1000);

}
0

2 Answers 2

7

Well, this question has been wisely answered in an answer to Stack Overflow question C: differences between char pointer and array.

Basically, what you're declaring as PROGMEM with,

const prog_char testStringD[] PROGMEM = "JKL";

is both the array and the memory it points to, that is, the elements of the array, both in current's scope stack. Whereas with:

const prog_char* testStringC PROGMEM = "GHI";

you declare a PROGMEM pointer to a constant string that may stay elsewhere in memory, but not declared as a PROGMEM string.

Though I did not test that, but you should try to declare:

const prog_char* testStringC PROGMEM = F("GHI");

to actually allocate the pointed string within the PROGMEM space. I guess it should be working, using Arduino's F() macro, which adds a lot of boilerplate code to actually have the same result as the array declaration.

As said in comments, if not in a global context, the PSTR() macro could be used instead of the F() macro.

Simpler is better: use the array declaration, not the pointer one!

Cf that other answer, the __flash qualifier is a third solution ;-)

3
  • I totally agree on the "simpler is better" - the array s far clearer. I just always get interested when something isn't immediately obvious. Mar 5, 2014 at 13:44
  • F() returns FlashStringHelper which is essentially the same, but using PSTR() works fine (as long as you bring the consts inside a function). Mar 5, 2014 at 13:51
  • indeed, I actually suggested first the PSTR() macro but changed to F() before submitting, because your consts are global ones in your Q, so I preferred to stick with the one that should work in both contexts.
    – zmo
    Mar 5, 2014 at 14:14
3

What this line:

const prog_char *testStringC PROGMEM = "GHI";

does is to write prologue code to copy the characters in the string to SRAM, and then initializes the pointer stored in flash to this SRAM location. You must load the pointer via normal means, and then dereference the pointer as usual.

const char *str = pgm_read_word(&testStringC);
Serial.println(str);

This line:

const prog_char testStringD[] PROGMEM = "JKL";

creates the array of characters in flash, allowing you to access it as expected.

0

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.