Доказательство того, что "int * p = malloc (1); p [0]" - это поведение undefined

Я пытаюсь убедить (со ссылкой на конкретные части стандарта C99) коллегу, что следующее поведение undefined:

int *p = malloc(1);
p[0] = 0;

Но я не могу найти конкретные детали в стандарте, которые явно гарантируют, что это undefined. Я ищу конкретные шаги в стандарте, которые приводят к этим выводам: undefined поведение. Это преобразование из void * в int * в первую строку? Назначение во второй строке?

Единственная релевантная часть, которую я могу найти о malloc, заключается в том, что она возвращает правильно выровненный указатель (7.20.3):

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

Я попробовал grepping для пробела в норме, но слишком много шума из-за пробела и других лексических проблем.

Ответ 1

Добавление из 7.20.3.3 Функция malloc в вашу цитату:

Функция malloc выделяет пространство для объекта, чей размеруказанный по размеру и значение которого неопределенно.
Функция malloc возвращает либо нулевой указатель, либо указатель на выделенное пространство.

Таким образом, существует 2 возможных источника поведения undefined, один из них перезаписывается (размер int гарантированно составляет 16 бит или более, но вы выделяете только 1 байт, который составляет 8 бит почти для всех систем) буфер, и во-вторых, возможно удаление ссылок на нуль-указатель.

От 6.5.2.1 Подстрока массива, p[0] = 0 эквивалентна *p = 0. Тип *p - это int, поэтому он заполняет биты sizeof(*p) * CHAR_BIT 0, которые могут не относиться к выделенному буфере, вызывающему UB.

В первой строке кода (присваивание) не существует поведения undefined, UB, если любой из них будет во второй строке (удаление ссылок).

Но на машинах, где CHAR_BIT велико, а sizeof(int) - 1, это будет корректное поведение для случаев, когда malloc не имеет return нулевой указатель.

Ответ 2

int *p = malloc(1);
p[0] = 0;

Это поведение undefined, потому что вы выделили 1 байт и выше вы пытаетесь записать четыре байта (предполагается, что int - это четыре байта). Это верно, если sizeof(int) > 1.

Ответ 3

6.5.3.2 Операторы адресов и указателей

...

Семантика

Унарный и оператор дает адрес своего операнда. Если операнд имеет тип '' type, результат имеет тип '' указатель на тип. Если операнд является результатом унарного * оператора, ни тот оператор, ни оператор и вычисляется, и результат выглядит так, как если бы оба были опущены, за исключением того, что ограничения для операторов все еще применяются, и результат не является значением lvalue. Аналогично, , если операнд является результатом a [], ни оператор, ни унарный *, который подразумевается [], и результат выглядит так, как будто & оператор был удален, а оператор [] был заменен на + оператор. В противном случае результатом будет указатель на объект или функцию обозначенный его операндом.

Унарный * оператор обозначает косвенность. Если операнд указывает на функция, результат - обозначение функции; если он указывает на объект, результатом будет lvalue, обозначающий объект. Если операнд имеет тип '' указатель на тип, результат имеет тип типа ''. Если неверное значение было присвоено указателю, поведение унарный * оператор undefined.

Оператор [] является подразумеваемым оператором * на указателе. Значение, присвоенное указателю, недопустимо для int, пока sizeof( int ) > 1.

Поведение undefined.

И NULL является недопустимым указателем, поэтому это также охватывает malloc() возврат NULL.

Ответ 4

Цитаты из стандарта:

J.2, Undefined поведение: поведение Undefined в следующих случаях:... Индекс массива выходит за пределы диапазона, даже если объект, по-видимому, доступен с данным индексом

6.2.5, Типы, 20: Тип массива описывает смежно выделенный непустой набор объектов.

Пока sizeof(int) > 1, ваш malloc(1) не выделял непустой набор объектов, поэтому размер массива, как выделено, равен нулю, а с p[0] вы получаете доступ к индексу, который выходит за пределы диапазона. Что и требовалось доказать.

Ответ 5

Код *p покрывается (по крайней мере, другие разделы также могут его покрывать). 6.3.2.1/1:

Значение lvalue является выражением (с типом объекта, отличным от void), которое потенциально обозначает объект; если lvalue не назначает объект при его оценке, поведение undefined.

Определение "объект":

область хранения данных в среде исполнения, содержимое которой может представлять значения

lvalue *p обозначает sizeof(int) байты пробела, однако имеется только 1 байт памяти, который может представлять значения (другими словами, нераспределенное пространство не может быть частью объекта). Итак, если sizeof(int) > 1, то *p не обозначает объект.


Для фактического кода в вопросе p[0]: это эквивалентно *(p+0). Мне непонятно, из 6.5.6/8, вызывает ли p + 0 UB или нет. Но это спорный вопрос, потому что, даже если это не вызывает UB, deferencing результат делает, как показано выше; поэтому p[0] вызывает UB в любом случае.

Ответ 6

malloc(1)

возвращает адрес в 1-байтовый большой буфер.

An int больше, чем 1 байт, вообще говоря.

Таким образом, присвоение значения int одному байтовому большому буферу - UB.

Указатели, возвращаемые malloc, не должны быть введены в c, так как они безопасно и автоматически продвигаются к правильному типу указателя при использовании.