Why can I not use pointers instead of array with PROGMEM?
Well, this question has been wisely answered in an answer to Stack&nbsp;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 ;-)