Использование 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
и многие другие.