Delta_G:
If this works, then why can't I use the String class and say:
const PROGMEM String menu_1[] = {"Menu Item 1" , "Menu Item 2" , "Menu Item 3" };
Is it the difference between using char and char*?
I'm sorry if I'm getting too involved. I'm more interested in learning how this works right now than just finding a way to write working code.
The difference is subtle, for sure!
When you declare an array of char, you can initialize that array using string literal syntax, but no string literal is actually generated by the compiler. This is a feature of the C/C++ programming language. Thus, this code creates an ARRAY and initializes it:
PROGMEM prog_char const ps_HelloWorld[] = "Hello, World!";
The compiler will generate 14 bytes of initialized data section, initializing it to "Hello, World!\0" and generate a relocation symbol named ps_HelloWorld to mean "the address of this element in the data section." This symbol is marked with the "PROGMEM" attribute, and thus goes into flash in the linker.
Meanwhile, this code generates both a string literal (which is initialized data), AND a pointer (which is also data):
PROGMEM prog_char const *ps_HelloWorld = "Hello, World!";
This generates one initialized data section element with 14 bytes, containing "Hello, World!\n" for the string literal (because now it's a string literal initializing a pointer -- not a string literal syntax initializing an array!) This literal is not marked PROGMEM.
It also generates an actual initialized pointer, named ps_HelloWorld, and marks it as initialized with "the address of the string literal containing "Hello, World!\n". This pointer is marked PROGMEM, and occupies two bytes of the program memory at that point.
The string-literal-can-initialize-arrays syntax does carry through to structures:
struct MyString {
char data[17]; // remember space for terminating NUL!
};
PROGMEM MyString data[] = {
{ "first!" },
{ "second!" },
};
This generates an array of MyString. That array is marked PROGMEM. Each element in the array in turn contains an array of characters, which is initialized by a string literal syntax (but doesn't generate a string literal!) Because the top-level array is marked PROGMEM, each of the direct storage elements is placed in PROGMEM. Beware, though -- each element is now 17 bytes, no matter whether you use all of it or not!
The difference between array (with initializer syntax) and pointer is subtle, but very important, because it tells the compiler to allocate different chunks of memory.
A pointer is a chunk of memory that has size 2 bytes (on AVR). Referencing the symbol of type pointer is the same thing as "read the value in these two bytes."
A string literal is a chunk of memory that has size N bytes. Referencing this string literal results in a relocation record saying "substitute with a pointer value of wherever the linker places this chunk of data in memory."
An array is a chunk of memory that has a size that you determine. The array can be automatically sized and initialized based on string literal initializer syntax, but is not the same thing as a string literal. Referencing this array results in a relocation record saying "substitute with a pointer value of wherever the linker places this structure in memory."
The PROGMEM attribute tells the linker to put the chunk of memory in flash, rather than SRAM. (Actually, SRAM data is probably put in flash AND SRAM, because it needs to be copied into SRAM on boot ... but a good boot loader at least compresses the SRAM initialized data a bit if possible)
So, the main problem here is that you can only apply the PROGMEM attribute to symbol names that you declare, not directly to string literals.
The reason the String class doesn't work is that it takes a pointer as initializer. Pointers mean that string literals turn into real literals, not array-initialization-syntax, and thus get placed in the data section without the PROGMEM attribute.
If this syntax were legal, you could place string literals in PROGMEM and get a reference to them in regular RAM:
prog_char const *str = PROGMEM "string"; // doesn't work
But that doesn't work 
As you can see, this is all intimately tied into how the compiler generates storage and symbols and relocation records for your declarations, and how the linker interprets the attributes of those symbols and records.
I hope this helps, and doesn't somehow insult anyone.