Почему strcpy запускает ошибку сегментации с глобальными переменными?


7

Так что я получил некоторый код 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 приводит к ошибке сегментации. Почему это происходит? Очевидно, в моем понимании сферы есть ошибка.

  0

Поскольку я не думаю, что это действительно решит проблему, я просто прокомментирую, что strncpy настоятельно рекомендуется использовать для strcpy. 23 сен. 082008-09-23 18:36:46

  0

Josh Gagnon: На самом деле strncpy не устанавливает нулевой ограничитель, если длина входной строки> = буфер. strcpy совершенно безопасен, если вы знаете, что буфер достаточно большой. В противном случае используйте 'snprintf (buffer, buffer_len,"% s ", src)', поскольку snprintf всегда ставит нулевой ограничитель (просто убедитесь, что buffer_len> 0). 05 авг. 112011-08-05 06:40:39

  0

@Josh: Я предпочитаю 'strlcpy'. К сожалению, glibc не поддерживает его, поэтому я не получаю много шансов его использовать. Я полагаю, что я всегда мог бы сворачивать свою собственную реализацию и добавлять ее в свою личную библиотеку заголовков нулевой проверки malloc и связанных с файлами функций, но это все еще раздражает меня, что у многих версий Unix есть это, в то время как Linux обычно этого не делает. 05 авг. 112011-08-05 15:42:30

  0

На самом деле это почти завязало меня в последнем семестре колледжа, так как я тестировал программу для четвертого и финального класса домашней работы для одного из моих классов (что я уже пропустил одну из домашних заданий из-за того, что думал, что это должно произойти через три дня после это на самом деле было) удаленно и случайно вошло в систему на сервер Unix, а не на Linux, не заметив, и скомпилировало и запустило его просто отлично, но пришло время экзамена. Я получаю электронное письмо от одного из TA, в котором говорится: «Ваша программа не будет запускаться или компилироваться в тестовом поле [Linux] ». 05 авг. 112011-08-05 15:45:38

  0

(К счастью, я включил строки как для 'strlcat', так и для' strncat' [аналогичные различия между 'strlcpy' и' strncpy' в отношении того, как они работают и что реализует их] версии выполняемой операции, поэтому это просто вопрос комментирования одной строки и раскол линии после.) ... Не ожидал, что так много ящиков комментариев. 05 авг. 112011-08-05 15:47:57

16

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

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

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

  0

Как и ожидалось, замена порядка объявлений переменных делает программу segfault. Благодаря! 23 сен. 082008-09-23 19:58:04


8

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

temp = (char *)malloc(TEMP_SIZE); 

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

  0

Нет необходимости инициализировать память в temp - strcpy позаботится об этом, даже если она просто устанавливает начальный нулевой термин. 23 сен. 082008-09-23 18:38:36

  0

Кроме того, он должен быть по крайней мере strlen (path) +1, чтобы соответствовать нулевому члену, или результат Bad Things T (M). 23 сен. 082008-09-23 18:39:23

  0

Он означает, что вам не нужна строка «temp [0] = 0», так как strcpy() добавит для вас NULL-терминатор. 23 сен. 082008-09-23 18:45:32

  0

strcpy не заботится о том, что строка назначения указывает на то, что она достаточно большая, чтобы содержать результат. Темп [0] = 0 бессмыслен и даже если исходная строка пуста, по-прежнему будет задана strcpy. Если вы хотите очистить всю строку, вы должны использовать memset (или что-то еще) 23 сен. 082008-09-23 18:47:43

  0

Извините, неправильно поняли 'strcpy' для 'strcat'. Я исправил свой ответ 23 сен. 082008-09-23 18:49:59

  0

Не нужно указывать указатель, возвращаемый malloc - malloc возвращает указатель на void, и поэтому никакого приведения не требуется. Кроме того, литье может повредить вам, потому что это может скрыть проблемы с неправильным определением malloc (поскольку неявное определение malloc - это возвращение int) в этой строке кода. 04 июн. 092009-06-04 15:10:29


1

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

скопирован из человека странице зЬгсру:

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. 

9

переменная Температура не указывает на какой-либо хранения (памяти), и это не инициализирован.

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

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

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

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


1

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

// 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(), чтобы избежать переполнения буфера.

  0

Адам, это хорошая идея, чтобы все эти магические числа плавали вокруг? ;-) 23 сен. 082008-09-23 18:43:12

  0

, который все еще имеет ошибки, поскольку, если исходная строка имеет длину 256, то строки не имеют нулевого завершения. 23 сен. 082008-09-23 18:45:08

  0

Не нужно указывать указатель, возвращаемый malloc - malloc возвращает указатель на пустоту, и поэтому никакого приведения не требуется. Кроме того, литье может повредить вам, потому что это может скрыть проблемы с неправильным определением malloc (поскольку неявное определение malloc - это возвращение int) в этой строке кода. 04 июн. 092009-06-04 15:11:20

  0

@ Daniel Papasian: актер не нужен в C, но это необходимо на C++, и я обычно предпочитаю сделать мой C-код совместимым с C++. Использование неявно определенных функций без их объявления устарело в C, и они не должны использоваться в новом C-коде. Я всегда компилирую с -Wall, который включает -Wimplicit-function-declaration. 04 июн. 092009-06-04 15:28:08


3

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

  0

Я не знал о strdup. Благодарю. 23 сен. 082008-09-23 20:03:06


2

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

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

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


2

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

// 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 символов.

  0

При использовании wchar_t это не работает. Использование sizeof работает только с символами. Я знаю, это вопрос о символах, но я сталкиваюсь с множеством ошибок, когда люди используют sizeof с wchar_t. 23 сен. 082008-09-23 19:25:48