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


Я под тем же впечатлением, что и ответ, что 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   51  

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 подписано, реализации сталкиваются со следующими вариантами:

  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 он больше не компилируется.

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

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

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

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

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