Используется ли неинициализированная переменная в качестве src для неопределенного поведения memcpy в C?

void foo(int *to)
{
  int from;
  memcpy(to, &from, sizeof(from));
}
22
Tor Klingberg 28 Окт 2015 в 17:20

3 ответа

Лучший ответ

Комитет C предложил ответ на отчет о дефекте 451: нестабильность неинициализированные автоматические переменные:

Ответ на вопрос 3 заключается в том, что библиотечные функции будут демонстрировать неопределенное поведение при использовании с неопределенными значениями.

Вопрос в дефекте искал исключение для memcpy и fwrite, если это действительно так, говоря:

[...] Тот факт, что кто-то хочет иметь возможность копировать неинициализированные байты заполнения в структуры с использованием memcpy без неопределенного поведения, является причиной того, что использование значения неинициализированного объекта не является неопределенным поведением. Похоже, это предполагает, что fwrite структуры с неинициализированными байтами заполнения не должен демонстрировать неопределенное поведение.

Эта часть ответа на предложение, похоже, направлена ​​на решение проблемы неинициализированного заполнения:

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

Мы видим форму отчета о дефекте 338: C99, кажется, исключает неопределенное значение от неинициализированного регистра, это в некоторой степени отклонение от прошлых ожиданий. В нем, среди прочего, говорится:

[...] Я считаю, что намерение исключить тип unsigned char из представлений ловушек состояло в том, чтобы позволить использовать их для копирования (через memcpy) произвольной памяти в случае, если эта память может содержать представления ловушек для некоторых типов. [.. .]

Сообщение в блоге Чтение неопределенного содержания также может быть undefined хорошо описывает эволюцию чтения неопределенных значений в C и дает больше смысла в изменениях, о которых я упоминал выше.

Стоит отметить, что это отличается от C ++, где чтение неопределенного значения из узкого символа без знака не является неопределенным поведением и отчет о дефектах 240 отмечает это различие:

Комитет C занимается аналогичной проблемой в своем DR338. Согласно этому анализу, они планируют применить подход, практически противоположный тому, который описан выше, путем дополнения описания своей версии преобразования lvalue-to-rvalue. CWG не считает, что доступ к беззнаковым символам все еще может быть затруднен, если они выделены в регистре, и необходимо пересмотреть предлагаемое разрешение в этом свете. См. Также выпуск 129.

14
Community 23 Май 2017 в 10:31

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

(Поскольку выделение пространства, а не инициализация переменных - это не UB .)

3
ameyCU 29 Окт 2015 в 03:28

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

В других ответах есть цитаты из стандарта C о том, что неопределенное поведение возникает, когда значение неинициализированной переменной «используется». Что, очевидно, не применимо, если вы не используете значение. Есть еще одно упоминание в стандартном неопределенном поведении C11 при копировании / назначении неинициализированных переменных:

6.3.2.1p2

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

Это также не влияет на ваш код, потому что адрес from берется, когда вы звоните memcpy

Другой важной частью стандарта C11 является 6.2.6.1 .

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

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

5
Manos Nikolaidis 28 Окт 2015 в 21:31