When you wrote
char letter;
printf("%p\n", &letter);
you declared a variable called letter.  It has a well-defined location (or address).  The only thing we don't know is which char value is in it -- that's either indeterminate or undefined, depending on who you ask.  So if you had tried to do printf("%c\n", letter), that might have gotten you into trouble, because that would try to print the undefined/indeterminate value.
But when you wrote
char *letter1;
printf("%p\n", letter1); //program crashes
that's completely different.  letter1 is a variable of type pointer-to-char.  As before, it has a well-defined location, and an indeterminate initial value.  But what's confusing here is that the value it doesn't have is (or would be) also an address.
If you wrote
printf("%p\n", &letter1);
you'd print the address of letter1, and as I said, that's well-defined.  But you tried to print
printf("%p\n", letter1);
and there you try to print the address in letter1, which is a much bigger problem.
(I wouldn't expect an actual crash, though -- in practice I'd merely expect a "random value".  I wouldn't expect a crash unless you tried to do printf("%c\n", *letter1).)
One more thing: Taking the address of an uninitialized variable can't be undefined, because plenty of well-defined programs do just that!
Taking an address of an uninitialized variable and passing it to a function can be a good way of assigning a value to a variable.  If you have a function that returns a value "by reference", you're probably going to pass it the address of a variable, and it will often be uninitialized, like this:
char *p;
int n = strtol("23skidoo", &p, 10);
printf("%d %s\n", n, p);
Footnote: I wrote that the initial value was "either indeterminate or undefined, depending on who you ask", and that alludes to a tremendous subtlety which I only learned about a couple of days ago, which is that the indeterminacy/undefinedness of the initial values of local variables like these can evidently depend on whether they do or might have their addresses taken.  There's sort of a Heisenberg -- or maybe Schrödinger -- uncertainty principle here, where the behavior depends on how closely you attempt to observe it.  If your program actually did crash when you tried to print the value of letter1, it might not crash if you changed it to printf("%p %p\n", &letter1, letter1);.