Потому что в Си++ — в отличие от Си — строковый литерал
"hello"
имеет тип
const char[]
. Как дополнительную подсказку, что даже если система (скажем, DOS) не имеет разделения памяти по типам и позволяет менять такие литералы — Windows имеет и не позволяет.
РАЗ. У указателя и массива несколько разная семантика
char hello1[] = "hello"; // массив длины 6, в изменяемом сегменте или стеке,
// данные скопированы из литерала, который
// сидит в неизменяемом сегменте
const char* hello2 = "hello"; // указатель направлен прямо на литерал,
// и попытка изменить его под Windows — вылет
ДВА. Компилятор имеет право спрессовать два литерала в один, и смена одного, скорее всего, сменит и другой. Не могу проверить на Windows — говорил же, что запись в строковый литерал под Windows приведёт к вылету — но, скорее всего, так будет. Что-то вроде
char* hello1 = const_cast<char*>("hello");
char* hello2 = const_cast<char*>("hello");
hello1[1] = 'U'; // hello2 = "hUllo" в системах вроде DOS, где не вылетит