Почему этот код разворота строки C вызывает ошибку сегментации?

Я пытаюсь написать код, чтобы изменить строку на месте (я просто пытаюсь улучшиться при программировании на С и манипуляции с указателем), но я не могу понять, почему я получаю ошибку сегментации :

#include  void reverse(char *s); int main() { char* s = "teststring"; reverse(s); return 0; } void reverse(char *s) { int i, j; char temp; for (i=0,j = (strlen(s)-1); i < j; i++, j--) { temp = *(s+i); //line 1 *(s+i) = *(s+j); //line 2 *(s+j) = temp; //line 3 } } 

Это линии 2 и 3, которые вызывают ошибку сегментации. Я понимаю, что могут быть лучшие способы сделать это, но мне интересно узнать, что именно в моем коде вызывает ошибку сегментации.

Обновление : я включил функцию вызова по запросу.

8 Solutions collect form web for “Почему этот код разворота строки C вызывает ошибку сегментации?”

Невозможно сказать только из этого кода. Скорее всего, вы передаете указатель, указывающий на недопустимую память, немодифицируемую память или какой-либо другой вид памяти, который просто не может быть обработан так, как вы его обрабатываете.

Как вы называете свою функцию?

Добавлено: Вы передаете указатель на строковый литерал. Строковые литералы не изменяются. Вы не можете отменить строковый литерал.

Вместо этого передайте указатель на изменяемую строку

 char s[] = "teststring"; reverse(s); 

Это уже было объяснено здесь. "teststring" – строковый литерал. Строковый литерал сам по себе является немодифицируемым объектом. На практике компиляторы могут (и будут) помещать его в постоянное запоминающее устройство. Когда вы инициализируете такой указатель

 char *s = "teststring"; 

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

 const char *s = "teststring"; 

Но когда вы объявляете свои s как

 char s[] = "teststring"; 

вы получаете полностью независимый массив s расположенный в обычной модифицируемой памяти, который просто инициализируется строковым литералом. Это означает, что этот независимый модифицируемый массив s получит свое начальное значение, скопированное из строкового литерала. После этого ваш массив s и строковый литерал продолжают существовать как полностью независимые объекты. Литерал по-прежнему не модифицируется, а ваш массив s модифицируется.

В основном последнее заявление функционально эквивалентно

 char s[11]; strcpy(s, "teststring"); 

Код может быть прерван по ряду причин. Вот те, которые приходят на ум

  1. s – NULL
  2. s указывает на константную строку, которая хранится в памяти только для чтения
  3. s не завершено NULL

Я думаю, что 2-й вариант наиболее вероятен. Можете ли вы показать нам сайт обратного вызова?

РЕДАКТИРОВАТЬ

На основе вашего образца №2 определенно есть ответ. Строковый литерал в C / C ++ не модифицируется. Собственный тип – фактически const char* а не char* . Что вам нужно сделать, это передать модифицирующую строку в этот буфер.

Быстрый пример:

 char* pStr = strdup("foobar"); reverse(pStr); free(pStr); 

Испытываете ли вы это что-то подобное?

 int main() { char * str = "foobar"; reverse(str); printf("%s\n", str); } 

Это делает str строковым литералом, и вы, вероятно, не сможете его редактировать (segfaults для меня). Если вы определяете char * str = strdup(foobar) он должен работать нормально (для меня).

Ваше заявление полностью неверно:

 char* s = "teststring"; 

«teststring» хранится в сегменте кода, который доступен только для чтения, например, код. И, s является указателем на «teststring», в то же время вы пытаетесь изменить значение диапазона памяти только для чтения. Таким образом, segmentation fault.

Но с:

 char s[] = "teststring"; 

s инициализируется с помощью «teststring», который, конечно, находится в сегменте кода, но в этом случае происходит дополнительная операция копирования в стек.

Какой компилятор и отладчик вы используете? Используя gcc и gdb, я бы скомпилировал код с флагом -g, а затем запустил его в gdb. Когда он segfaults, я просто сделаю backtrace (команда bt в gdb) и посмотрю, какая из них является причиной нарушения. Кроме того, я бы просто запустил код шаг за шагом, а «посмотрел» значения указателя в gdb и знаю, где именно проблема.

Удачи.

Как некоторые из приведенных выше ответов, строковая память доступна только для чтения. Однако некоторые компиляторы предоставляют возможность компиляции с записываемыми строками. Например, с версиями gcc , 3.x поддерживаются -fwritable-strings но более новые версии этого не делают.

См. Вопрос 1.32 в списке часто задаваемых вопросов:

В чем разница между этими инициализациями?

 char a[] = "string literal"; char *p = "string literal"; 

Моя программа сработает, если я попытаюсь присвоить новое значение p[i] .

Ответ:

Строковый литерал (формальный термин для строки с двумя кавычками в источнике C) можно использовать двумя разными способами:

В качестве инициализатора для массива char, как и в объявлении char a[] , он задает начальные значения символов в этом массиве (и, при необходимости, его размер).

В другом месте он превращается в неназванный, статический массив символов, и этот неназванный массив может храниться в постоянной памяти и поэтому не может быть изменен . В контексте выражения массив преобразуется сразу в указатель, как обычно (см. Раздел 6), поэтому второе объявление инициализирует p чтобы указать на первый элемент неименованного массива.

Некоторые компиляторы имеют переключатель, контролирующий, являются ли строковые литералы доступными для записи или нет (для компиляции старого кода), а некоторые могут иметь параметры, чтобы заставить строковые литералы формально обрабатываться как массивы const char (для лучшего обнаружения ошибок).

( акцент мой )

См. Также « Назад к основам » Джоэла .

Я думаю, strlen не может работать, так как s не завершен NULL. Таким образом, поведение вашего для итерации не та, которую вы ожидаете. Поскольку результат strlen будет превосходить длину s, вы будете записывать в память, где вы не должны быть.

Кроме того, s указывает на постоянную строку, хранящуюся в памяти только для чтения. Вы не можете его изменить. Попробуйте инициализировать с помощью функции gets, как это сделано в примере strlen

Interesting Posts

Что такое копирование и как он оптимизирует идиому копирования и свопинга?

Требуется ли для маршрутизатора 2,4 ГГц и 5 ГГц специальная Wi-Fi-карта?

Как проверить, отображается ли один символ в строке?

Предоставление разрешению конкретному пользователю доступа к файлу NFS

Webview избегает предупреждения безопасности из игры Google при реализации onReceivedSslError

PowerPoint открывается на втором экране, но у меня нет ни одного

короткий вызов формулы для многих переменных при построении модели

Установите надстрочный индекс и индекс в форматированном тексте в wpf

Aero Glass / Прозрачные границы окон в Windows 8?

Изменена новая вкладка Chrome, но я хочу ее вернуть

Как проверить, не является ли текущий stream не основным streamом

Какие символы НЕ допускаются в именах полей MongoDB?

Получить текущий URL-адрес из приложения Windows Forms

Java Swing: как я могу реализовать экран входа в систему, прежде чем показывать JFrame?

Как я могу отключить сетевое устройство на Mac?

Давайте будем гением компьютера.