Сбой или «ошибка сегментации», когда данные копируются / сканируются / считываются в неинициализированный указатель

Этот вопрос предназначен для использования в качестве справочного материала по всем часто задаваемым вопросам природы:

Почему при копировании / сканировании данных по адресу, на который указывает неинициализированный указатель, возникает таинственный сбой или «ошибка сегментации»?

Например:

char* ptr;
strcpy(ptr, "hello world"); // crash here!

или же

char* ptr;
scanf("%s", ptr); // crash here!
 Lundin31 мая 2016 г., 17:22
@Olaf Действительно, так что тогда вы голосуете за их вопросы сегментации как дубликаты со ссылкой на этот вопрос. Я пропускал вопрос часто задаваемых вопросов как этот навсегда; наконец-то дошли руки записать.
 barak manos31 мая 2016 г., 17:22
Вам, вероятно, следует изменить название, если вы хотите, чтобы этот вопрос был прочитан теми, кто испытывает эту проблемудо они публикуют это здесь.
 too honest for this site31 мая 2016 г., 17:25
@Lundin: я ценю ваши усилия. Если это подразумевается как дублирующее резюме, я с вами. Но на самом деле я бы предпочел, чтобы они нашли это сами, прежде чем отправлять. Но тогда это, вероятно, все равно желаемое за действительное, поскольку начинающие склонны считать, что их проблема уникальна Итак, +1, и я буду иметь в виду, чем - спасибо!
 barak manos31 мая 2016 г., 17:24
Да, я только что понял это после того, как вы опубликовали другой комментарий выше.
 Lundin31 мая 2016 г., 17:23
@barakmanos Цель состоит в том, чтобы использовать этот пост в качестве «канонического дубликата» для часто задаваемых вопросов. Я действительно не ожидаю, что новички найдут это самостоятельно.
 too honest for this site31 мая 2016 г., 17:21
Проблема в том, что OP даже не знают, что указатель не инициализирован, а в том, что там волшебным образом появляется объект, как только вы объявляете / определяете (они тоже это путают) указатель.

Ответы на вопрос(3)

Это происходит потому, что у вас естьне выделено память дляуказатель char* ptr , В этом случае вы должныдинамически распределять память для указателя.

Две функцииmalloc() а такжеcalloc() может быть использован дляdynamic memory allocation.

Попробуйте этот код: -

  char* ptr;
  ptr = (char *) malloc(sizeof(char)*50); // allocate space for 50 characters.
  strcpy(ptr, "hello world");

Когда использование*ptr более не забудьтеосвободить память выделено для*ptr . Это может быть сделано с помощьюfree() функция.

  free(ptr);  // deallocating memory.

Размердинамически распределяемая память можно изменить с помощьюrealloc().

  ptr = (char *)realloc(ptr, sizeof(char)*100);// allocate space for 0 characters.

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

Указатели указывают только на область памяти. Вы создали указатель, но еще не связались с ячейкой памяти.strcpy хочет, чтобы вы прошли два указателя (первый не должен быть постоянным), которые указывают на два символьных массива, как эта подпись:

char * strcpy ( char * destination, const char * source );
пример использования:
char* ptr = malloc(32);  
strcpy(ptr, "hello world");
char str[32];  
strcpy(str, "hello world");

Вы можете попробовать следующий фрагмент кода, чтобы прочитать строку до появления символа новой строки (* вы также можете добавить другие пробельные символы, такие как"%[^\t\n]s"(вкладка, новая строка) или же"%[^ \t\n]s" (пробел, табуляция, новая строка)).

char *ptr = malloc(32);
scanf("%31[^\n]", ptr);

(В реальной жизни не забудьте проверить возвращаемое значение изscanf()!)

Решение Вопроса

Указатель - это специальный тип переменной, который может содержать только адрес другой переменной. Он не может содержать никаких данных. Вы не можете «копировать / хранить данные в указателе» - это не имеет никакого смысла. Вы можете установить указатель только для указания на данные, расположенные в другом месте.

Это означает, что для того, чтобы указатель имел смысл, он всегда должен указывать на допустимое место в памяти. Например, он может указывать на память, выделенную в стеке:

{
  int data = 0;
  int* ptr = &data;
  ...
}

Или память выделяется динамически в куче:

int* ptr = malloc(sizeof(int));

Всегда указывать на использование указателя до его инициализации. Это еще не указывает на действительную память.

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

/*** examples of incorrect use of pointers ***/

// 1.
int* bad;
*bad = 42;

// 2.
char* bad;
strcpy(bad, "hello");

Вместо этого вы должны убедиться, что указатель указывает на (достаточно) выделенную память:

/*** examples of correct use of pointers ***/

// 1.
int var;
int* good = &var;
*good = 42;

// 2.
char* good = malloc(5+1); // allocates memory for 5 characters and 1 terminator
strcpy(good, "hello");

Обратите внимание, что вы также можете установить указатель так, чтобы он указывал на четко определенное «нигде», позволяя ему указывать наNULL, Это делает этонулевой указатель, который является указателем, который гарантированно не указывает ни на одну допустимую память. Это отличается от того, чтобы указатель оставался полностью неинициализированным.

int* p1 = NULL; // pointer to nowhere
int* p2;        // uninitialized pointer, pointer to "anywhere", cannot be used yet

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

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

Дальнейшее чтение:

Указатели, указывающие на неверные данные
Как получить доступ к локальной переменной из другой функции, используя указатели?
Можно ли получить доступ к памяти локальной переменной вне ее области?

Ошибка сегментации и причины
Что такое ошибка сегментации?
Почему я получаю ошибку сегментации при записи в строку, инициализированную "char * s", но не "char s []"?
В чем разница между char s [] и char * s?
Окончательный список распространенных причин ошибок сегментации
Что такое ошибка шины?

 alk31 мая 2016 г., 17:41
"указатель на «где угодно»" отлично! :-)
 too honest for this site31 мая 2016 г., 17:26
Читает ли он содержимое указателя переменной с адресной шины? ;-) (И, например, PCIe использует формат пакета / команды, а не классический механизм адресации / шины данных). Так же были конструкции ОЗУ (помните RAMBUS?) Во всяком случае, я в порядке с ответом, должно хватить для начинающих. Просто оставьте комментарии, если они заинтересованы, чтобы углубиться в это.
 Lundin31 мая 2016 г., 17:18
Подобные ошибки очень часто пишутся как начинающими, так и не понявшими, что такое указатели или как они работают. Поэтому обратите внимание, что цель этой вики-сообщества состоит в том, чтобы держать объяснения на базовом уровне. Если вы хотите оставить более сложные ответы со ссылками на стандарт C и т. Д., Пожалуйста, отправьте другой ответ на вопрос.
 WhozCraig31 мая 2016 г., 19:25
@ Олаф честно, не уверен, как это вообще относится к моему комментарию
 too honest for this site31 мая 2016 г., 19:16
@WhozCraig: я не согласен частично.int (*p)[10] = malloc(sizeof(*p)); это совершенно справедливо и типичная идея.
 Lundin31 мая 2016 г., 17:25
@Olaf Пожалуйста, держите здесь основные вещи :) Это предназначено для начинающих. Хотя ... если адрес является данными, то почему у ЦП есть и адресная шина, и шина данных?
 M.M01 июн. 2016 г., 07:12
«Все эти примеры приводят к сбоям программы или другим видам неожиданного поведения» - иногда это работает «как положено», люди задают вопросы, спрашивающие, почему их недопустимый указатель имеет доступне падение, когда они думали, что
 too honest for this site31 мая 2016 г., 17:22
«Он не может содержать никаких данных». - Хм, на самом деле адресявляется его данные.
 too honest for this site31 мая 2016 г., 19:34
@WhozCraig: «Этовсегда ошибка использовать указатель до того, как он был инициализирован. "это правда, но" использовать "не ограничивается только разыменованием«.
 WhozCraig31 мая 2016 г., 17:43
Обратите внимание на последний пример: не нужно разыменовывать указатель, чтобы продемонстрировать неопределенное поведение. Как и все остальное, неопределенный контент (случай значения дляp2) по самой своей природе вызывает неопределенное поведение, когда дажеоценивалитем более продвигая безумиеразыменовании, Да, это другая проблема, но тесно связана. В итоге, утверждение «Всегда указывать на использование указателя до его инициализации». верно, но «использование» не ограничивается только разыменованием.

Ваш ответ на вопрос