Почему strcpy вызывает ошибку сегментации с глобальными переменными? - PullRequest
7 голосов
/ 23 сентября 2008

Итак, у меня есть код C:

#include <stdio.h>
#include <string.h>

/* putting one of the "char*"s here causes a segfault */
void main() {
  char* path = "/temp";
  char* temp;
  strcpy(temp, path);
}

Это компилирует, запускает и ведет себя так, как выглядит. Однако если один или оба символьных указателя объявлены как глобальные переменные, strcpy приведет к ошибке сегментации. Почему это происходит? Очевидно, в моем понимании области видимости есть ошибка.

Ответы [ 8 ]

16 голосов
/ 23 сентября 2008

Как упоминалось в других постерах, корень проблемы в том, что temp не инициализирован. Когда она объявлена ​​в стеке как автоматическая переменная, она будет содержать любой мусор, находящийся в этом месте памяти. Очевидно, что для компилятора + CPU + OS, которую вы используете, мусор в этом месте является допустимым указателем. Strcpy «преуспевает» в том, что он не работает по умолчанию, но на самом деле он скопировал строку в произвольное место в другом месте памяти. Такая проблема повреждения памяти вселяет страх в сердца программистов на C везде, поскольку ее чрезвычайно трудно отлаживать.

Когда вы перемещаете объявление временной переменной в глобальную область, оно помещается в раздел BSS и автоматически обнуляется. Попытки разыменования * temp затем приводят к segfault.

Когда вы перемещаете * путь к глобальной области видимости, тогда * temp перемещается вверх на одну позицию в стеке. Мусор в этом месте, по-видимому, не является действительным указателем, поэтому разыменование * temp приводит к segfault.

9 голосов
/ 23 сентября 2008

Переменная temp не указывает на какое-либо хранилище (память) и неинициализирована.

если temp объявлен как char temp[32];, тогда код будет работать независимо от того, где он объявлен. Однако существуют другие проблемы с объявлением temp с фиксированным размером, подобным этому, но это вопрос для другого дня.

Теперь, почему происходит сбой при объявлении глобально, а не локально. Удача ...

При локальном объявлении значение temp исходит из того значения, которое когда-либо находилось в стеке в это время. К счастью, он указывает на адрес, который не вызывает сбой. Тем не менее, это уничтожающая память, используемая кем-то другим.

Когда объявлено глобально, на большинстве процессоров эти переменные будут храниться в сегментах данных, которые будут использовать нулевые страницы по требованию. Таким образом, char *temp выглядит так, как если бы оно было объявлено char *temp=0.

8 голосов
/ 23 сентября 2008

Вы забыли выделить и инициализировать темп:

temp = (char *)malloc(TEMP_SIZE);

Просто убедитесь, что TEMP_SIZE достаточно велик. Вы также можете рассчитать это во время выполнения, а затем убедиться, что размер достаточно (должен быть не менее strlen (путь))

3 голосов
/ 23 сентября 2008

Как уже упоминалось выше, вы забыли выделить место для темп. Я предпочитаю от strdup до malloc+strcpy. Он делает то, что вы хотите сделать.

2 голосов
/ 23 сентября 2008

Я хотел бы переписать первый фрагмент Адама как

// Make temp a static array of 256 chars
char temp[256];
strncpy(temp, sizeof(temp), path);
temp[sizeof(temp)-1] = '\0';

Таким образом, вы:

1. don't have magic numbers laced through the code, and
2. you guarantee that your string is null terminated.

Второй момент заключается в потере последнего символа вашей исходной строки, если его длина> = 256 символов.

2 голосов
/ 23 сентября 2008

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

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

Глобальные переменные постоянно терпят неудачу, потому что они обычно устанавливаются на определенные шаблоны, которые указывают на не отображенную память. Попытка разыменования приводит к немедленной ошибке (что лучше - если оставить ее позже, ошибку будет очень трудно отследить).

1 голос
/ 23 сентября 2008

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

// Make temp a static array of 256 chars
char temp[256];
strncpy(temp, 256, path);

// Or, use dynamic memory
char *temp = (char *)malloc(256);
strncpy(temp, 256, path);

Кроме того, используйте strncpy() вместо strcpy(), чтобы избежать переполнения буфера.

1 голос
/ 23 сентября 2008

Важная часть к сведению:
строка назначения dest должна быть достаточно большой, чтобы получить копию.
В вашей ситуации у temp нет выделенной памяти для копирования.

Скопировано из справочной страницы strcpy:

DESCRIPTION
   The  strcpy()  function  copies the string pointed to by src (including
   the terminating '\0' character) to the array pointed to by  dest.   The
   strings  may not overlap, and the destination string dest must be large
   enough to receive the copy.
...