char s[4] = "ABC";

ってのは、4bytes のメモリをスタックに確保して、
その中身を 'A' 'B' 'C' '\0' で初期化してる。
s 単体だと当然その先頭ポインタだから、スタック内を指してる。
実体がスタックにあるから書き換え可能。

char* s = "ABC";

だと、リテラル専用の領域(書き換え禁止なメモリ領域)にある
'A' 'B' 'C' '\0' な並びの先頭ポインタを引っ張ってきてるだけ。
ポインタ s の指す先は書き換え禁止な領域なので、
s[0] = 'D'; なんてやると一般保護違反になる。