Подтвердить что ты не робот

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

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

Однако этот код не скомпилирован в gcc/Mingw:

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

typedef uint8_t array_t [SIZE_MAX];

error: размер массива '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, потому что установка максимально допустимого размера на основе целочисленного типа со знаком не имеет никакого смысла.

4b9b3361

Ответ 1

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

C Стандартные мандаты 1 что тип size_t не указан и тип ptrdiff_t подписан.

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

Таким образом, значение 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, определенным в заголовке. Если результат не представлен в объекте этого типа, поведение undefined.

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

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

Ответ 2

Диапазон 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. Примите тот факт, что вычитание указателя может переполняться и вызывать поведение undefined, если указатели расположены дальше, чем элементы SIZE_MAX / 2. Спецификация языка не запрещает этот подход.

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

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