Почему этот код разворота строки C вызывает ошибку сегментации?
Я пытаюсь написать код, чтобы изменить строку на месте (я просто пытаюсь улучшиться при программировании на С и манипуляции с указателем), но я не могу понять, почему я получаю ошибка сегментации:
#include <string.h>
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, вызывающие ошибку сегментации. Я понимаю, что могут быть лучшие способы сделать это, но мне интересно узнать, что именно в моем коде вызывает ошибку сегментации.
Обновление. Я включил функцию вызова в соответствии с запросом.
Ответы
Ответ 1
Невозможно сказать только из этого кода. Скорее всего, вы передаете указатель, указывающий на недопустимую память, немодифицируемую память или какую-либо другую память, которая просто не может быть обработана так, как вы ее обрабатываете здесь.
Как вы называете свою функцию?
Добавлено: Вы передаете указатель на строковый литерал. Строковые литералы не изменяются. Вы не можете отменить строковый литерал.
Передайте указатель на изменяемую строку вместо
char s[] = "teststring";
reverse(s);
Это уже было объяснено здесь. "teststring"
- строковый литерал. Строковый литерал сам по себе является немодифицируемым объектом. На практике компиляторы могут (и будут) помещать его в постоянное запоминающее устройство. Когда вы инициализируете такой указатель
char *s = "teststring";
указатель указывает непосредственно в начале строкового литерала. Любые попытки изменить то, что указывает s
, считаются неудачными в общем случае. Вы можете прочитать его, но вы не можете писать в него. По этой причине настоятельно рекомендуется указывать только на строковые литералы с переменными-указателями на константы
const char *s = "teststring";
Но когда вы объявляете свой s
как
char s[] = "teststring";
вы получаете полностью независимый массив s
, расположенный в обычной модифицируемой памяти, который просто инициализируется строковым литералом. Это означает, что этот независимый модифицируемый массив s
получит свое начальное значение, скопированное из строкового литерала. После этого ваш массив s
и строковый литерал будут продолжать существовать как полностью независимые объекты. Литерал по-прежнему не модифицируется, а ваш массив s
модифицируется.
В принципе, последнее объявление функционально эквивалентно
char s[11];
strcpy(s, "teststring");
Ответ 2
Код может быть прерван по ряду причин. Вот те, которые приходят на ум
- s имеет значение NULL
- s указывает на константную строку, которая хранится в памяти только для чтения.
- s не завершено NULL
Я думаю, что 2 наиболее вероятным. Можете ли вы показать нам сайт вызова обратного?
EDIT
На основе вашего образца №2 определенно есть ответ. Строковый литерал в C/С++ не модифицируется. Правильный тип фактически const char*
, а не char*
. Что вам нужно сделать, это передать модифицирующую строку в этот буфер.
Быстрый пример:
char* pStr = strdup("foobar");
reverse(pStr);
free(pStr);
Ответ 3
Вы тестируете это что-то вроде этого?
int main() {
char * str = "foobar";
reverse(str);
printf("%s\n", str);
}
Это делает str строковым литералом, и вы, вероятно, не сможете его редактировать (segfaults для меня). Если вы определяете char * str = strdup(foobar)
, он должен работать нормально (для меня).
Ответ 4
Ваше выражение совершенно неверно:
char* s = "teststring";
"teststring" сохраняется в сегменте кода, который доступен только для чтения, например код. И, s является указателем на "teststring" , в то же время вы пытаетесь изменить значение диапазона памяти только для чтения. Таким образом, ошибка сегментации.
Но с:
char s[] = "teststring";
s инициализируется с помощью "teststring" , который, конечно же, находится в сегменте кода, но в этом случае в стеке выполняется дополнительная операция копирования.
Ответ 5
Какой компилятор и отладчик вы используете? Используя gcc и gdb, я бы скомпилировал код с флагом -g, а затем запустил его в gdb. Когда он segfaults, я просто сделаю backtrace (команда bt в gdb) и посмотрю, какая из них является причиной нарушения. Кроме того, я бы просто запустил код шаг за шагом, а "посмотрел" значения указателя в gdb и знал, где именно проблема.
Удачи.
Ответ 6
См. Вопрос 1.32 в списке часто задаваемых вопросов:
В чем разница между этими инициализациями?
char a[] = "string literal";
char *p = "string literal";
Моя программа сработает, если я попытаюсь присвоить новое значение p[i]
.
Ответ:
Строковый литерал (формальный термин для строки с двумя кавычками в источнике C) можно использовать двумя разными способами:
В качестве инициализатора для массива char, как и в объявлении char a[]
, он задает начальные значения символов в этом массиве (и, при необходимости, его размер).
В другом месте он превращается в неназванный, статический массив символов, и этот неназванный массив может храниться в постоянной памяти и поэтому не может быть изменен. В контексте выражения массив преобразуется сразу в указатель, как обычно (см. Раздел 6), поэтому второе объявление инициализирует p
чтобы указать на первый элемент без имени.
Некоторые компиляторы имеют переключатель, контролирующий, являются ли строковые литералы доступными для записи или нет (для компиляции старого кода), а некоторые могут иметь параметры, чтобы заставить строковые литералы формально обрабатываться как массивы const char
(для лучшего обнаружения ошибок).
(акцент мой)
См. Также " Назад к основам " Джоэла.
Ответ 7
Как некоторые из приведенных выше ответов, строковая память доступна только для чтения. Однако некоторые компиляторы предоставляют возможность компиляции с записываемыми строками. Например. с gcc
, версии 3.x поддерживаются -fwritable-strings
, но более новые версии этого не делают.
Ответ 8
Я думаю, strlen
не может работать, так как s не завершен NULL. Таким образом, поведение вашего для итерации не та, которую вы ожидаете.
Поскольку результат strlen будет превосходить длину s, вы будете записывать в память, где вы не должны быть.
Кроме того, s указывает на постоянную строку, хранящуюся в памяти только для чтения. Вы не можете его изменить. Попробуйте инициализировать с помощью функции gets, как это сделано в strlen example