It's my understanding that a char array declared with the PROGMEM keyword is supposed to be saved only to (flash) ROM, not (S)RAM. Since the Arduino has no way of differentiating a pointer to ROM from a pointer to RAM, we have to use the memcpy_P() functions to get data out of program memory. memcpy() can't access ROM, and a variable declared with PROGMEM isn't supposed to be in RAM at all; yet I've found that both memcpy()andmemcpy_P() can be used to retrieve a PROGMEM'd string later on. What's going on here, and how can I place data specifically in ROM or RAM with certainty?
Example sketch:
/* where PROGMEM is in the declaration is irrelevant */
//PROGMEM const char string[] = "This string is saved in ROM";
//const PROGMEM char string[] = "This string is saved in ROM";
const char PROGMEM string[] = "This string is saved in ROM";
//const char string[] PROGMEM = "This string is saved in ROM";
void setup() {
Serial.begin(9600);
while (!Serial); // let Serial finish initializing
char buffer[64];
// read string from RAM
memcpy(buffer, string, sizeof(string));
Serial.println(buffer);
// read string from ROM
memcpy_P(buffer, string, sizeof(string));
Serial.println(buffer);
}
void loop() {}
Serial output:
This string is saved in ROM
This string is saved in ROM
If I remember correctly memcpy used with (AVR-)GCC is quite smart and will likely notice and take into account the segment in which the data is stored, hence it will work (in the same way when you use memcpy for type punning it will likely not duplicate nor move data)
if you write you own basic memcpy(); then it will fail (in the same way as if you try to print string directly)
Using memcpy_P ensures that your code is portable across different microcontroller platforms and compilers so you don't have to rely on this working or not and you provide clarity and Intent for whoever reads the code.
You really do need to be careful of the compiler optimizations. I have seen questions where someone has declared an array in PROGMEM, then in test code print out a single element of the array, without using the specific commands needed to access PROGMEM. The test code works, so they assume the coding is correct, but as soon as they expand the use of the array, or try to print an element that is not known at compile time, the code fails.
optimization could possibly be also part of the puzzle but I doubt so in the code I posted as I access the progmem address both through memcpy and directly (and memcpy works and not the direct addressing)