Почему можно изменить строку, используя массив, но нельзя сделать то же самое с указателем?

Почему мы не можем создать изменяемую строку непосредственно через указатель на char, например, как в следующем коде: <pre><code class="cpp">char* hello = "hello"; // ошибка, перед char* должен быть const</code></pre>? Я новичок, и хотел бы понять, что стоит за этой ошибкой и какие причины ее обосновывают.
  • 9 февраля 2025 г. 9:34
Ответы на вопрос 2
В C и C++ строки, определенные как литералы, имеют статическое (неизменяемое) хранение. Это означает, что литералы строк, такие как `"hello"`, размещаются в области памяти, которая не предназначена для изменения. Попытка изменить содержимое такой строки может привести к неопределенному поведению вашей программы, включая возможное падение.

Когда вы пишете:

```cpp
char* hello = "hello"; // ошибка, перед char* должен быть const
```

в этом случае вы хотите присвоить указатель на строковый литерал переменной типа `char*`. Однако строка `"hello"` хранится в памяти только для чтения, и попытка изменить ее содержимое (например, присвоить `hello[0] = 'H';`) приведет к ошибке выполнения.

Чтобы разрешить этот код и избежать ошибок, вы можете использовать `const char*`, что означает, что указатель указывает на неизменяемые данные:

```cpp
const char* hello = "hello";
```

Это также информирует компилятор и других разработчиков о том, что указатель `hello` не должен изменять строку, на которую он указывает.

Если вы хотите создать изменяемую строку, вы можете делать это, используя массив символов:

```cpp
char hello[] = "hello"; // создается массив, в который копируется строка
```

В этом случае строка "hello" будет скопирована в автоматически выделенный массив `hello`, который можно изменять. Теперь вы можете изменять содержимое массива, например:

```cpp
hello[0] = 'H'; // теперь hello будет "Hello"
```

Итак, резюмируя:
- Строковые литералы в C/C++ являются неизменяемыми.
- Указатели на строки, объявленные как `char*`, не могут указывать на неизменяемые литералы, если они не объявлены с `const`.
- Если вам нужна изменяемая строка, используйте массив символов (`char[]`).
Потому что в Си++ — в отличие от Си — строковый литерал "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, где не вылетит
Похожие вопросы