Почему максимальный размер массива "слишком большой"?
Я под тем же впечатлением, что и ответ, что size_t
стандарт всегда гарантирует, что он будет достаточно большим, чтобы содержать максимально возможный тип данной системы.
однако этот код не компилируется на gcc/Mingw:
#include <stdint.h>
#include <stddef.h>
typedef uint8_t array_t [SIZE_MAX];
ошибка: размер массива 'array_t' слишком велик
Я что-то недопонимаю в стандарте здесь? Это size_t
разрешено быть слишком большим для данной реализации? Или это еще одна ошибка в Mingw?
EDIT: дальнейшие исследования показывают, что
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, потому что установка максимально допустимого размера на основе целочисленного типа со знаком не имеет никакого смысла.
5 ответов:
предел SIZE_MAX / 2 исходит из определений size_t и ptrdiff_t в вашей реализации, которые выбирают, что типы ptrdiff_t и size_t имеют одинаковую ширину.
C стандартные мандаты1 что тип size_t без знака и тип ptrdiff_t подписан.
результат разницы между двумя указателями, всегда будет2 иметь тип 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
который является целочисленным типом без знака результата sizeof оператор;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
он больше не компилируется.С другой стороны, ведьма лязг 4.0.0 самый большой из них
SIZE_MAX/8
иSIZE_MAX/8 + 1
разрывы.
просто рассуждая с нуля,
size_t
- Это тип, который может держать размер любого объекта. Размер любого объекта ограничен шириной адресной шины (игнорируя мультиплексирование и системы, которые могут обрабатывать, например, 32 и 64-битный код, называют это "шириной кода"). Anologous toMAX_INT
который является самым большим целым значением,SIZE_MAX
является самым большим значениемsize_t
. Таким образом, объект размеромSIZE_MAX
все адресуемой памяти. Разумно, что реализация флагов, что как ошибка, однако, я согласитесь, что это ошибка только в том случае, когда фактический объект выделен, будь то в стеке или в глобальной памяти. (Звонок вmalloc
на эту сумму не получится в любом случае)
прежде всего,
size_t
в результатеsizeof
оператора. Таким образом, он гарантированно имеет размер, который может содержать "значение"SIZE_MAX
. Теоретически, это должно позволить вам определить любой объект в размереSIZE_MAX
.тогда, если я правильно помню, вы не ограничены верхним пределом общесистемных ресурсов. Это не ограничения, наложенные стандартом C, а скорее ОС / среда.
Регистрация
ulimit -a
выход. Вы также можете изменить ограничения с помощьюulimit -s <size>
для стека, пока массив, который вы определяете, хранится в стек. В противном случае, для глобальных массивов, вероятно, вам нужно проверить допустимый размер .данные или. BSS согласно вашей ОС. Таким образом, это зависит от среды (или от реализации).