Получение ошибки сегментации - PullRequest
15 голосов
/ 29 апреля 2011

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

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

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

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

Ответы [ 5 ]

9 голосов
/ 29 апреля 2011

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

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

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

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

  4. Вы дважды вызываете free () по одному и тому же указателю, что является неопределенным поведением.

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

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

4 голосов
/ 29 апреля 2011

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

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

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

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

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

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

3 голосов
/ 29 апреля 2011
  1. Неопределенное поведение : попытка изменить строковый литерал
  2. Неопределенное поведение : запись после конца массива
  3. неопределенное поведение : передача free() указателя, не полученного через malloc()
  4. неопределенное поведение : передача free() неверный указатель
  5. Неопределенное поведение : несовместимость между спецификатором формата printf() и фактическими типами аргументов
  6. Неопределенное поведение : передача в free() неверный указатель

Неопределенное поведение может проявляться «ошибкой сегментации», но это ни в коем случае не является обязательным результатом.Все могло произойти:)

1 голос
/ 29 апреля 2011

3: "foo" является константной строкой, которая не выделяется динамически.

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

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