Character strings in Flash

This is kind of interesting (to me anyway). I’ve seen recommended multiple times on this forum to use this construct to place character strings in Flash:

const char * const dog1 PROGMEM = "Hello    World";

Well, on an Uno-compatible board anyway, it doesn’t do that:

const char * const dog1 PROGMEM = "Hello    World";

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println(dog1);
  memcpy((uint8_t *)dog1, (const uint8_t *)"Goodbye", 7);
  Serial.println(dog1);
}

void loop() {
}

Producing Serial Monitor output:

Hello    World
Goodbye  World

Besides being able to programmatically change the string at runtime, another clue is that the ‘Serial.println(dog1);’ actually works. It shouldn’t since dog1 is a ‘char *’, so the overloaded version of ‘println()’ that pulls strings from Flash isn’t being compiled. That requires type ‘__FlashStringHelper *’.

Something I don’t understand is that if I increase the size of the string by 10 bytes:

const char * const dog1 PROGMEM = "          Hello    World";

then, the compiler reports a 10 byte increase in BOTH RAM and Flash usage.

Finally, I think this definitely puts the string in Flash:

const char dog[] PROGMEM = "Hello    World";
__FlashStringHelper *dog2 = (__FlashStringHelper *) dog;

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println(dog2);
}

void loop() {
}

Changing the string length only changes the reported Flash usage.

So, no question here. Just thought others might also find it interesting.

I suspect the first works because the address of the string, which is in RAM and copied from FLASH, is a link-time constant. The compiler knows how to get the value (RAM address) without fetching the contents of the PROGMEM pointer so it does that. If the compiler had generated code to fetch the address it would have fetched it from RAM using the FLASH address of the pointer and used that garbage to display the wrong string of bytes.