Почему является это строковое реверсирование C кодом, вызывающим отказ сегментации? [дубликат]

смотрите на модуль OS

29
задан Antti Haapala 1 October 2017 в 07:28
поделиться

7 ответов

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

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

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

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

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");
50
ответ дан 28 November 2019 в 01:19
поделиться

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

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

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

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

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

Краткий пример:

char* pStr = strdup("foobar");
reverse(pStr);
free(pStr);
9
ответ дан 28 November 2019 в 01:19
поделиться

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

char* s = "teststring";

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

Но с:

char s[] = "teststring";

s инициализируется "teststring", которая, конечно, находится в сегменте кода, но в этом случае происходит дополнительная операция копирования в стек.

2
ответ дан 28 November 2019 в 01:19
поделиться

Вы тестируете что-то вроде этого?

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

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

2
ответ дан 28 November 2019 в 01:19
поделиться

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

Удачи.

0
ответ дан 28 November 2019 в 01:19
поделиться

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

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

 char a [] = "строковый литерал";
char * p = "строковый литерал";

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

Ответ:

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

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

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

0
ответ дан 28 November 2019 в 01:19
поделиться

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

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

-1
ответ дан 28 November 2019 в 01:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: