Почему максимальный размер массива слишком велик?
У меня такое же впечатление, как этот ответ, что 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
подписано, реализации сталкиваются со следующими вариантами:
Разрешить объекты массива размера
SIZE_MAX
и сделатьptrdiff_t
шире чемsize_t
, Он должен быть шире хотя бы на один бит. такиеptrdiff_t
может вместить любую разницу между двумя указателями, указывающими на массив размераSIZE_MAX
или меньше.Разрешить объекты массива размера
SIZE_MAX
и использоватьptrdiff_t
той же ширины, что иsize_t
, Примите тот факт, что вычитание указателя может переполниться и вызвать неопределенное поведение, если указатели дальше, чемSIZE_MAX / 2
элементы друг от друга. Спецификация языка не запрещает такой подход.использование
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 в соответствии с вашей ОС. Таким образом, это зависит от окружающей среды (или зависит от реализации).