Использование size_t в итераторе массива

Недавно я узнал, что size_t был введен для того, чтобы помочь будущему кодексу увеличить количество бит и увеличить доступную память. Специфическое определение использования, по-видимому, заключается в хранении размера чего-либо, как правило, массива.

Теперь я должен задаться вопросом, как далеко следует провести эту будущую проверку. Разумеется, бессмысленно иметь длину массива, определенную с использованием будущего размера и размера size_t, если в следующей задаче итерации по массиву используется unsigned int как индексный массив:

void (double* vector, size_t vectorLength) {
    for (unsigned int i = 0; i < vectorLength; i++) {
        //...
    }
}

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

Означает ли это, что переменная итератора i должна быть просто size_t?

Означает ли это, что любое целое число в любой программе должно быть функционально идентифицировано относительно того, будет ли оно когда-либо использоваться как индекс массива?

Означает ли это, что любой код, использующий логику, которая разрабатывает индекс программно, должен затем создать новое значение результата типа size_t, особенно если логика основана на потенциально значащих целочисленных значениях? то есть.

double foo[100];
//...
int a = 4;
int b = -10;
int c = 50;

int index = a + b + c;
double d = foo[(size_t)index];

Конечно, хотя, поскольку моя логика кода создает фиксированную привязку, преобразование с повышением уровня в size_t не обеспечивает дополнительной защиты.

Ответ 1

Вы должны иметь в виду правила автоматического преобразования языка.

Означает ли это, что переменная итератора я должна быть просто size_t?

Да, это так, потому что если size_t больше, чем unsigned int, и ваш массив на самом деле больше, чем может быть проиндексирован с помощью unsigned int, то ваша переменная (i) никогда не сможет достичь размера массива.

Означает ли это, что любое целое число в любой программе должно быть функционально идентифицировано относительно того, будет ли оно когда-либо использоваться как индекс массива?

Вы пытаетесь сделать это здоровым, а это не так. Почему вы выбираете переменную как double, а не float? Почему вы должны сделать переменную как unsigned, а другая нет? Зачем вам делать переменную short, а другая - int? Конечно, вы всегда знаете, к чему будут использоваться ваши переменные, поэтому вы решаете, какие типы они должны получить. Выбор size_t является одним из многих, и он также решил.

Другими словами, каждая переменная в программе должна быть функционально идентифицирована и задана корректным типом.

Означает ли это, что любой код, использующий логику, которая разрабатывает индекс программно, должен затем создать новое значение результата типа size_t, особенно если логика основана на потенциально значащих целочисленных значениях?

Совсем нет. Во-первых, если переменная никогда не может иметь отрицательных значений, тогда она могла бы быть unsigned int или size_t в первую очередь. Во-вторых, если переменная может иметь отрицательные значения при вычислении, то вы должны обязательно убедиться, что в конце она неотрицательна, потому что вы не должны индексировать массив с отрицательным числом.

Тем не менее, если вы уверены, что ваш индекс неотрицателен, то приведение его к size_t не имеет никакого значения. C11 в 6.5.2.1 говорит (внимание мое):

Постфиксное выражение, за которым следует выражение в квадратных скобках [], является индексированным обозначение элемента объекта массива. Определение индексного оператора [] состоит в том, что E1[E2] идентично (*((E1)+(E2))). Из-за правил преобразования, которые применяются к двоичному + оператору, если E1 является объектом массива (эквивалентно, указатель на исходный элемент объекта массива) и E2 является целым числом, E1[E2] обозначает E2 > th элемента E1 (считая с нуля).

Это означает, что любой тип index, для которого some_pointer + index имеет смысл, разрешен для использования в качестве индекса. Другими словами, если вы знаете, что у вашего int достаточно места для размещения индекса, который вы вычисляете, нет необходимости бросать его на другой тип.

Ответ 2

Как обсуждалось Neil Kirk, итераторы являются будущим доказательством соответствия size_t.

Дополнительной точкой в ​​вашем вопросе является вычисление позиции, и это обычно включает абсолютное положение (например, a в вашем примере) и, возможно, одно или несколько относительных величин (например, b или c), потенциально подписанный.

Подписанный аналог size_t равен ptrdiff_t, а аналогичный для типа итератора I равен typename I::difference_type.

Как вы описали в своем вопросе, лучше всего использовать соответствующие типы в вашем коде, чтобы конверсии не нужны. Для эффективности памяти, если у вас есть, например, массив из миллиона позиций в другие массивы, и вы знаете, что эти позиции находятся в диапазоне 0-255, тогда вы можете использовать unsigned char; но в какой-то момент необходимо преобразование.

В таких случаях лучше всего называть этот тип, например.

using pos = unsigned char;

и сделать все преобразования явными. Тогда код будет легче поддерживать, если в будущем диапазон 0-255 увеличится.

Ответ 3

Конечно, бессмысленно определять длину массива с использованием будущего размера и размера size_t, если в следующей задаче итерации по массиву используется unsigned int как индексный массив

Да, это так. Так что не делайте этого.

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

Это будет продвигаться только в этой конкретной операции <. Верхний предел вашей переменной int не будет изменен, поэтому операция ++ всегда будет работать с int, а не с size_t.

Означает ли это, что переменная итератора я должна быть просто size_t?

Означает ли это, что любое целое число в любой программе должно быть функционально идентифицировано относительно того, будет ли оно когда-либо использоваться как индекс массива?

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

В случае с 100 элементами просто используйте uint_fast8_t. Затем программа оптимизирована как для размера, так и для скорости, а также для переноски на 100%.

При объявлении переменной хороший программист активирует свой мозг и учитывает следующее:

  • Каков диапазон значений, которые я буду хранить внутри этой переменной?
  • Нужно ли мне хранить в нем отрицательные числа?
  • В случае массива, сколько значений мне понадобится в худшем случае? (Если неизвестно, мне нужно использовать динамическую память?)
  • Есть ли проблемы с совместимостью с этой переменной, если я решил перенести эту программу?

В отличие от плохого программиста, который не активирует свой мозг, но просто набирает int повсюду.

Ответ 4

Да, если вы используете int для индексации массива, вы теряете точку использования size_t в других местах. Вот почему вы можете использовать итераторы с STL. Они являются будущим доказательством. Для C-массивов вы можете использовать либо size_t, указатели, либо алгоритмы, и lambdas, либо диапазоны для циклов (С++ 11). Если вам нужно сохранить размер или индекс в переменных, они должны быть size_t или другими подходящими типами, как и все, с чем они взаимодействуют, если вы не знаете, что размер будет небольшим. (Например, если вы сохраняете расстояние между двумя элементами, которые всегда будут в небольшом диапазоне, вы можете использовать int).

double *my_array;
for (double *it = my_array, *end_it = my_array + my_array_size, it != end_it; ++it)
{
    // use it
}

std::for_each(std::begin(my_array), std::end(my_array), [](double& x)
{
    // use x
});

for (auto& x : my_array)
{
    // use x
}

Ответ 5

Означает ли это, что любое целое число в любой программе должно быть функционально идентифицировано относительно того, будет ли оно когда-либо использоваться как индекс массива?

Я заберу этот пункт и скажу четко Да. Кроме того, в большинстве случаев переменная, используемая как индекс массива, используется только как (или что-то связанное с ней).

И это правило применимо не только здесь, но и в других обстоятельствах. Существует много случаев, когда в настоящее время существует специальный тип: ptrdiff_t, off_t (который даже может меняться в зависимости от используемой нами конфигурации!), pid_t и многие другие.