Defining string arrays

char * strings[] is an array of pointers. When you initialize it as

char * strings[] = { "John", "Paul", NULL};

the strings John Paul are string literals. They are constants that exist somewhere in the code or Read only memory. What is done is to copy the pointer to the string literal John into the strings[0] and so on. i.e.

strings[0] --> holds a pointer to "John". 
strings[1] --> holds a pointer to "Paul"

Note that the string literals should not be modified by your program. If you do, it is undefined behaviour.

In case of char ** strings This is a pointer to a pointer. It is a single memory location and cannot hold many pointers on its own. So, you cannot initialize it as below.

char ** strings = { "John", "Paul", NULL};  // error

However, a pointer to pointer can be used along with dynamic memory allocation (malloc,calloc etc) to point to an array of strings.

char string[] = "John";

In this case, you have a char array into which the string literal is copied. This step is done by the compiler, generally in the start up code before the main starts.

char * string = "Paul";

Here you have a char pointer which points to a string literal.

Difference between the above two statements is that in the case of a char array, you can modify the elements of string but you cannot in the second case.