Получение сбоя сегментации
Я видел много вопросов о получении ошибки сегментации в программе 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: Это также утечка памяти.