Почему максимальный размер массива слишком велик?

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

Тем не менее, этот код не компилируется в gcc/Mingw:

#include <stdint.h>
#include <stddef.h>

typedef uint8_t array_t [SIZE_MAX];

ошибка: размер массива 'array_t' слишком велик

Я что-то не так понимаю в стандарте? Является size_t разрешено быть слишком большим для данной реализации? Или это еще одна ошибка в Mingw?


РЕДАКТИРОВАТЬ: дальнейшие исследования показывают, что

typedef uint8_t array_t [SIZE_MAX/2];   // does compile
typedef uint8_t array_t [SIZE_MAX/2+1]; // does not compile

Который случается так же, как

#include <limits.h>

typedef uint8_t array_t [LLONG_MAX];           // does compile
typedef uint8_t array_t [LLONG_MAX+(size_t)1]; // does not compile

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

4 ответа

Решение

Ограничение SIZE_MAX / 2 исходит из определений size_t и ptrdiff_t в вашей реализации, которые выбирают, что типы ptrdiff_t и size_t имеют одинаковую ширину.

C Стандартные предписания 1, что тип size_t не подписан, а тип ptrdiff_t подписан.

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

Таким образом, значение SIZE_MAX / 2 равно значению PTRDIFF_MAX. Если в реализации выбран максимальный размер объекта, равный SIZE_MAX, то ширина типа ptrdiff_t должна быть увеличена. Но гораздо проще ограничить максимальный размер объекта до SIZE_MAX / 2, тогда у типа ptrdiff_t должен быть больший или равный положительный диапазон, чем у типа size_t.

Стандарт предлагает эти 3 комментария 4 по теме.


(Цитируется по ISO / IEC 9899: 201x)

1 (7.19 Общие определения 2)
Типы
ptrdiff_t
который является целочисленным типом со знаком результата вычитания двух указателей;
size_t
который является целым типом без знака результата оператора size of;

2 (6.5.6 Аддитивные операторы 9)
Когда вычтены два указателя, оба должны указывать на элементы одного и того же объекта массива или один после последнего элемента объекта массива; Результатом является разница индексов двух элементов массива. Размер результата определяется реализацией, а его тип (целочисленный тип со знаком) ptrdiff_t определен в заголовке. Если результат не может быть представлен в объекте этого типа, поведение не определено.

3 (K.3.4 Целочисленные типы 3)
Чрезвычайно большие размеры объекта часто являются признаком того, что размер объекта был вычислен неправильно. Например, отрицательные числа выглядят как очень большие положительные числа при преобразовании в тип без знака, например size_t. Кроме того, некоторые реализации не поддерживают такие большие объекты, как максимальное значение, которое может быть представлено типом size_t.

4 (K.3.4 Целочисленные типы 4)
По этим причинам иногда полезно ограничивать диапазон размеров объектов для обнаружения ошибок программирования. Для реализаций, нацеленных на машины с большими адресными пространствами, рекомендуется, чтобы RSIZE_MAX был определен как меньший из размеров самого большого поддерживаемого объекта или (SIZE_MAX >> 1), даже если этот предел меньше, чем размер некоторых допустимых, но очень большие объекты Реализации, нацеленные на машины с небольшими адресными пространствами, могут захотеть определить RSIZE_MAX как SIZE_MAX, что означает, что нет размера объекта, который считается нарушением ограничения времени выполнения.

Диапазон size_t гарантированно будет достаточным для хранения размера самого большого объекта, поддерживаемого реализацией. Обратное неверно: вам не гарантируется возможность создания объекта, размер которого заполняет весь диапазон значений. size_t,

При таких обстоятельствах возникает вопрос: что SIZE_MAX стоять за? Самый большой поддерживаемый размер объекта? Или наибольшее значение, представимое в size_t? Ответ: это последнее, т.е. SIZE_MAX является (size_t) -1, Вы не гарантированно сможете создавать объекты SIZE_MAX байты большие.

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

  1. Разрешить объекты массива размера SIZE_MAX и сделать ptrdiff_t шире чем size_t, Он должен быть шире хотя бы на один бит. такие ptrdiff_t может вместить любую разницу между двумя указателями, указывающими на массив размера SIZE_MAX или меньше.

  2. Разрешить объекты массива размера SIZE_MAX и использовать ptrdiff_t той же ширины, что и size_t, Примите тот факт, что вычитание указателя может переполниться и вызвать неопределенное поведение, если указатели дальше, чем SIZE_MAX / 2 элементы друг от друга. Спецификация языка не запрещает такой подход.

  3. использование ptrdiff_t той же ширины, что и size_t и ограничить максимальный размер объекта массива SIZE_MAX / 2, такие ptrdiff_t может вместить любую разницу между двумя указателями, указывающими на массив размера SIZE_MAX / 2 или меньше.

Вы просто имеете дело с реализацией, которая решила следовать третьему подходу.

Это очень похоже на поведение, зависящее от реализации.

Я использую Mac OS, и с gcc 6.3.0 самый большой размер, с которым я могу скомпилировать ваше определение, SIZE_MAX/2; с SIZE_MAX/2 + 1 он больше не компилируется.

С другой стороны, ведьма Clang 4.0.0 самый большой SIZE_MAX/8, а также SIZE_MAX/8 + 1 брейки.

Просто рассуждения с нуля, size_t это тип, который может содержать размер любого объекта. Размер любого объекта ограничен шириной адресной шины (игнорируя мультиплексирование и системы, которые могут обрабатывать, например, 32- и 64-битный код, называют это "шириной кода"). Анологичный MAX_INT которая является наибольшим целочисленным значением, SIZE_MAX является наибольшим значением size_t, Таким образом, объект размером SIZE_MAX это все адресуемая память. Разумно, что реализация помечает это как ошибку, однако, я согласен, что это ошибка только в случае, когда фактический объект размещен, будь то в стеке или в глобальной памяти. (Призыв к malloc на эту сумму все равно не получится)

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

Тогда, если я правильно помню, вы ограничены верхним пределом общесистемных ресурсов. Это не границы, установленные стандартом C, а ОС / среда.

Проверьте ulimit -a выход. Вы также можете изменить ограничения, используя ulimit -s <size> для стека, пока определяемый вами массив хранится в стеке. В противном случае, для глобальных массивов, возможно, вам нужно проверить допустимый размер в.DATA или.BSS в соответствии с вашей ОС. Таким образом, это зависит от окружающей среды (или зависит от реализации).

Другие вопросы по тегам