Я обнаружил, что в подобных ситуациях полезна картинка.
Давайте скомбинируем два приведенных выше фрагмента:
char *p = "Hello";
char *q;
strcpy(q, "goodbye");
printf("p = %s, q = %s\n", p, q);
Вот гипотетическая карта памяти, показывающая отношения междуp
, q
и строки "Hello"
и "goodbye"
:
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
"Hello" 0x00080000 'H' 'e' 'l' 'l'
0x00080004 'o' 0x00 0x?? 0x??
"goodbye" 0x00080008 'g' 'o' 'o' 'd'
0x0008000C 'b' 'y' 'e' 0x00
...
p 0x01010000 0x00 0x08 0x00 0x00
q 0x01010004 0x?? 0x?? 0x?? 0x??
"Hello"
и "goodbye"
являются строковыми литералами, которые являются массивами char
(const char
в C ++), хранятся таким образом, что они активны в течение всей жизни программы.Литерал "Hello"
сохраняется начиная с адреса 0x00080000, а литерал "до свидания" сохраняется начиная с адреса 0x00080008.
p
и q
являются указателями на char
со степенью auto
, что означает, что они существуют только в течение времени жизни блока, в котором они объявлены.В этом случае они расположены по адресам 0x01010000 и 0x01010004 соответственно (в этом примере мы предполагаем 32-битные указатели).
Когда вы пишете char *p = "Hello";
, выражение массива "Hello"
преобразуется в выражение указателя, значением которого является местоположение первого элемента массива, и это значение указателя копируется в p
, каквидно на карте памяти выше.
Когда вы пишете char *q;
, начальное значение q
является неопределенным 1 , как указано байтовыми значениями 0x??
.Это значение может соответствовать или не соответствовать адресу для записи;Скорее всего, это не так.В общем, когда вы пишете strcpy(q, "goodbye");
, вы пытаетесь скопировать содержимое строкового литерала "goodbye"
в произвольное место в памяти.Чаще всего это приведет к ошибке времени выполнения.
Если вы выделите буфер для строки, буфер должен быть достаточно длинным, чтобы хранить всю строку плюс терминатор 0;выделение только 4 байтов недостаточно, потому что тогда ваша строка будет перетекать в память, которой вы не «владеете», что может привести к засорению чего-то важного (технически, поведение не определено, то есть может произойти все что угодно).Таким образом, вам не нужно выделять память для указателя (это уже сделано, когда вы объявляете указатель), вы должны выделить память для того, на что указывает указатель.
Если мы изменим наш фрагмент кода на что-то вроде
char *p = "Hello";
char *q;
q = malloc(10);
strcpy(q, "goodbye");
printf("p = %s, q = %s\n", p, q);
, тогда наша карта памяти будет выглядеть примерно так:
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
"Hello" 0x00080000 'H' 'e' 'l' 'l'
0x00080004 'o' 0x00 0x?? 0x??
"goodbye" 0x00080008 'g' 'o' 'o' 'd'
0x0008000C 'b' 'y' 'e' 0x00
...
p 0x01010000 0x00 0x08 0x00 0x00
q 0x01010004 0x40 0x00 0x00 0x00
...
<dynamic> 0x40000000 'g' 'o' 'o' 'd'
0x40000004 'b' 'y' 'e' 0x00
0x40000008 0x?? 0x??
В этом случае malloc
выделяет 10 байтов памяти, начиная с 0x40000000, и копирует этот адрес в q
.Вызов strcpy
затем копирует содержимое строкового литерала "до свидания" в это место.
1 Если бы
q
был объявлен как
static
или в области видимости файла (вне какой-либо функции), то он был бы инициализирован равным 0 (0x00000000).