Почему Маллоку не всегда нужны указатели на символ? - PullRequest
0 голосов
/ 01 ноября 2011

Почему работает следующий код:

char *p;

p="hello";

printf("%s\n",p);

Пока этот код не работает:

char *p;

strcpy(p,"hello");

printf("%s\n",p);

Я знаю, что добавление p = malloc (4);во втором примере код заработал бы, но это как раз мой вопрос.Почему malloc нужен во втором примере, а не в первом?

Я искал похожие вопросы по SO, но никто не отвечает на этот вопрос точно.

Ответы [ 6 ]

5 голосов
/ 01 ноября 2011

p - указатель.Вам нужно указать на что-то .В первом случае

 p = "hello";

указывает p на тот строковый литерал, который находится где-то в памяти вашей программы во время выполнения.

Во втором случае вы не выполнялиp указывает на что-либо, поэтому все, что смотрит на то, на что указывает p, недопустимо.

p = malloc(some_size);

заставляет p указывать на часть (неинициализированной) памяти, которая может содержать some_sizeсимволы.Если вы зарезервировали достаточно, вы можете сделать что-то вроде strcpy(p, "hello"), потому что p указывает на допустимую область памяти, поэтому копирование в память, на которую указывает p, можно.Обратите внимание, что some_size должен быть не меньше того, что вы хотите скопировать в него, включая символ '\0'.

Обратите внимание, что выполнение:

p = "hello";
strcpy(p, "bye");

будет недопустимымпотому что "hello" может храниться как в постоянной памяти, так что вы не можете перезаписать его.

0 голосов
/ 01 ноября 2011

Я обнаружил, что в подобных ситуациях полезна картинка.

Давайте скомбинируем два приведенных выше фрагмента:

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).
0 голосов
/ 01 ноября 2011

Поскольку значение, которое имеет p, будет случайным, и любые попытки скопировать строку приведут к сбою. Используя malloc, вы гарантируете, что значение p будет нормальным для копирования (если буфер достаточно большой)

0 голосов
/ 01 ноября 2011
strcpy(p,"hello");

Чтобы строковый литерал hello был скопирован , p должен указывать на правильную ячейку памяти, которая в данном случае не является.

0 голосов
/ 01 ноября 2011

p="hello"; назначает адрес строкового литерала "hello" для p, тогда как scanf требует места для размещения отсканированного ввода, поэтому вам необходимо выделить для него некоторую память (статическую, динамическую или автоматическую) .

0 голосов
/ 01 ноября 2011

"hello" является строковым литералом и будет жить в вашей выходной программе. Память выделяется компилятором во время компиляции в том же месте, что и фактический код *

То есть тип "hello" будет const char[6], который автоматически конвертируется в char *. (Использование const char* вместо char* для строковых литералов - хорошая привычка)

Во втором случае p не инициализируется при вызове strcpy, поэтому результат не определен. p=malloc(4); недостаточно , чтобы исправить это. Строка «hello» состоит из 6 символов - 5 от самого слова hello плюс '\0' для завершения строки.

* На самом деле в современных системах это не совсем так, это около в том же месте, что и код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...