Получение сбоя сегментации

Я видел много вопросов о получении ошибки сегментации в программе C здесь, в SO, и я подумал, что было бы здорово иметь ссылку на них здесь, вопрос с некоторыми случаями, вызывающими ошибку сегментации, Мой ответ опубликован ниже.

Как написано в некоторых ответах, поведение undefined для всех случаев, хотя многие люди встречают их как ошибка сегментации, поэтому этот вопрос касается того, что вызывает это "Симптом".

В приведенных ниже случаях я получаю ошибку сегментации при запуске программы, не могли бы вы определить, почему?

1)

char *str = "foo";
str[0] = 'b';   // << Segfault hre

2)

char str[] = "foo";
char *newStr = malloc(strlen(str));
strcpy(newStr, str);
free(newStr);   // << Segfault here

3)

char *str = malloc(4 * sizeof(char));
str = "foo";
free(str);      // << Segfault here

4)

char *str = malloc(4 * sizeof(char));
strcpy(str, "foo");
free(str);
if (str != NULL)
    free(str);      // << Segfault here

5)

char *str = "something and then foo";
printf("%s", str[19]);    // << Segfault here

6)

typedef struct {
    char *str;
}st;
...
st *s;
s = malloc(sizeof(st));
s->str = malloc(5);
free(s);
free(s->str);    // << Segfault here

Ответы

Ответ 1

Случай 1:
char *str = "foo"; назначить адрес строки в текстовом сегменте, который является только для чтения, и вы не можете записать его, как это делается во второй строке: str[0] = 'b';. Если вы хотите изменить текст, используйте char str[] = "foo";, который создаст массив символов в стеке и назначит его указателю на str.

случай 2:
strlen возвращает длину строки без '\0' chracter в конце, поэтому strlen("foo") = 3, а strcpy копирует строку, включая символ '\0', поэтому она копирует больше байтов, чем вы выделили.

случай 3:
Как и в случае 1, str = "foo"; присваивая адрес "foo" str, это означает, что вы потеряете адрес выделенной памяти, а str теперь содержит указатель на текстовый сегмент, который вы не можете free потому что он не в куче, а его постоянная память.

случай 4:
Функция free не присваивает NULL указателю, полученному как параметр (поскольку он не имеет его адреса, он не может этого сделать). И вы пытаетесь вызвать free в буфере, который уже был free d.

случай 5:
str[19] - char, а не указатель char, а "%s" ожидает строку, что означает char *. Этот char обрабатываемый как адрес на многих платформах является незаконным адресом. printf() не проверяет полученные аргументы.

случай 6:
Использование s->str после s free d неверно. Правильным использованием будет первый вызов free(s->str);, а затем вызов free(s);. Освободите внутреннюю выделенную память до free своего контейнера.

Ответ 2

Все ваши примеры вызывают поведение undefined, что может привести к сбою (или может показаться, что оно вообще не наносит вреда).

  • Вам не разрешено изменять строковый литерал. (см., например, здесь)

  • Вы забыли выделить хранилище для завершающего нулевого байта, do malloc(strlen(str) + 1);

  • Вы вызываете free() по указателю, который вы не получили из malloc (или аналогичных функций). Когда вы указываете указатель str на строковый литерал, вы также потеряли указатель на память, выделенную с помощью malloc и памяти утечки.

  • Вы вызываете free() дважды на том же указателе, который является undefined.

  • % s в строке формата printf сообщает printf, что аргумент представляет собой строку (a char *, указывающую на последовательность символов с nul-terminated). Вы передаете ей char, а не строку. Если вы хотите напечатать суффикс строки, используйте printf("%s", &str[19]);

  • Вы передаете недопустимый указатель на free(), вы уже освободили s, вы не можете разыгрывать его позже, когда вы делаете s->str. Отмените порядок освобождения: free(s->str); free(s);

Ответ 3

  • Undefined Поведение: попытка изменить строковый литерал
  • Undefined Поведение: запись за конец массива
  • Undefined Bahaviour: переход к free() указателю, который не получен через malloc()
  • Undefined Поведение: переход на free() недопустимый указатель
  • Undefined Поведение: несовместимости между спецификатором формата printf() и действительными типами аргументов
  • Undefined Поведение: переход на free() недопустимый указатель

Undefined Поведение может проявляться с помощью "ошибки сегментации", но это отнюдь не обязательный результат. Все могло бы произойти:)

Ответ 4

  • Вы не выделили какую-либо память. * str - это просто указатель.
  • Причина segfault в строке 2, вам нужно malloc strlen() + 1 из-за разделителя строк '\ 0'

Ответ 5

3: "foo" - это постоянная строка, не распределенная динамически.

Sidenote: Это также утечка памяти.