Разница между массивом и указателем

Вчера у меня была небольшая проблема с самодельной функцией "strcpy". Теперь это работает, но я немного смущен!

char* a = "Hello, World!"; //Works
char b[] = "Hello, World!"; //Works also

strcpy(a, "Hello!"); //Segmentation fault
strcpy(b, "Haha!!"); //Works..

В чем разница? Почему указатель char вызывает «ошибку сегментации»?

Почему это вообще работает? :

char* a = "Haha"; //works
a = "LOL"; //works..

person Normal People Scare Me    schedule 22.05.2013    source источник
comment
Прочтите этот раздел часто задаваемых вопросов по C: c-faq.com/aryptr/aryptr2.html   -  person Claudio    schedule 22.05.2013
comment
Я удалил тег C++, так как речь идет о подмножестве C (и даже там вы не стали бы реализовывать strcpy самостоятельно).   -  person Benjamin Bannier    schedule 22.05.2013
comment
@honk да, это имеет смысл   -  person Normal People Scare Me    schedule 22.05.2013
comment
Прочтите: Разница между char *str и char str[] и как они хранятся в памяти?   -  person Grijesh Chauhan    schedule 12.10.2013


Ответы (2)


char* a = "Hello, World!";

дает вам указатель на строковый литерал. Строковый литерал может существовать в памяти только для чтения, поэтому его содержимое нельзя изменить.

char* a = "Haha"; //works
a = "LOL"; //works..

изменяет указатель a так, чтобы он указывал на другой строковый литерал. Он не пытается изменить содержимое любого строкового литерала, поэтому это безопасно/правильно.

char b[] = "Hello, World!"

объявляет массив в стеке и инициализирует его содержимым строкового литерала. Память стека доступна для записи, поэтому совершенно безопасно изменять содержимое этой памяти.

person simonc    schedule 22.05.2013
comment
поэтому я не могу изменить даже один символ в указателе char, верно? - person Normal People Scare Me; 22.05.2013
comment
@NormalPeopleScareMe Верно. Он указывает на строку constant. - person ; 22.05.2013
comment
Ну, это неопределенное поведение, поэтому оно может работать, а может и нет. Мой совет — всегда предполагать, что этого не произойдет. - person Imre Kerr; 22.05.2013
comment
@NormalPeopleScareMe Это зависит от того, на что он указывает. Вы не можете изменить содержимое указателя на строковый литерал, но вы можете изменить содержимое указателя на строку, выделенную в стеке или куче. - person simonc; 22.05.2013
comment
Спасибо вам, ребята. Симонк, ты мне очень помог! - person Normal People Scare Me; 22.05.2013
comment
Компиляция с -Wwrite-strings заставит gcc помнить, насколько постоянными являются строковые литералы: он выдаст вам предупреждение об этом коде. - person Medinoc; 22.05.2013

В вашем первом примере, поскольку вы пытаетесь записать в память только для чтения, на которую указывает a, вы получите ошибку сегментации. Если вы хотите использовать указатели, выделите память в куче, используйте и удалите ее после ее использования. Где b — это массив символов, инициализированный фразой «Hello, World!».

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

person ZoomIn    schedule 22.05.2013
comment
Я считаю этот ответ очень полезным. Я не знал, что строковый литерал находится в постоянной памяти. Также я нашел учебник, как использовать указатели на символы в сочетании с strcpy, что вызвало здесь ошибку сегментации, и они выделяли память, и я не мог проследить, почему они это сделали. Спасибо. - person Normal People Scare Me; 22.05.2013