Нужно ли мне всегда использовать size_t
при индексации массива, даже если массив недостаточно велик, чтобы превышать размер int?
Это не вопрос о том, когда я должен использовать size_t
. Я просто хочу знать, если, например, программа, имеющая 2 ГБ доступной памяти (все эти поля могут быть проиндексированы int32), но с этой памятью (виртуальная память), назначенная «полям» 14 ГБ - 16 ГБ компьютера ОЗУ.
Будет ли это всегда ошибкой при индексации памяти, если я использую int32
вместо size_t
(или unsigned long int
) в этом случае?
Возможно, вопрос больше о виртуальной памяти, чем об указателях.
Обычно программа не должна заботиться о том, сколько памяти установлено на машине, на которой она работает. Но если логика программы диктует, что будет нижний предел количества элементов, вы можете использовать меньший тип индекса.
size_t
гарантированно сможет проиндексировать каждый байт всего, что вы ему подбрасываете. Удобно иногда.
Вы можете использовать любой целочисленный тип, достаточно большой для хранения индексов вашего массива. char
подходит для небольших массивов. Это не имеет никакого отношения к общему объему адресуемой памяти, виртуальной памяти или чему-то еще. size_t
может хранить любой индекс любого массива, поэтому это полезно, если вы заранее не знаете размер.
@ user4581301: Стандарт C не дает такой гарантии.
@EricPostpischil: да. В частности, он может содержать размер максимально возможного массива, поскольку массив — это тип объекта. Поэтому, если вы можете проиндексировать его, размер (в байтах) подходит для size_t. А так как элементы массива состоят как минимум из одного байта, то и каждый индекс может. Путаница может быть связана с тем, что size_t не гарантирует, что он будет содержать сумму размеров нескольких объектов.
@MSalters: это не так. Стандарт C определяет (C 2018 7.19 2), что size_t
— это беззнаковый целочисленный тип результата оператора sizeof
. Но это говорит нам только о типе, а не о значении. Стандарт не гарантирует, что размеры не будут переполнять тип, равно как и то, что арифметические операции не будут переполнять различные типы int
.
size_t
— это целое число без знака, способное удерживать размер самого большого объекта, который вы можете выделить. Это полезно для индексации, потому что это означает, что он может индексировать самый большой массив, который вы можете выделить.
Это не означает, что это требуется или даже обязательно рекомендуется для индексации. Вы можете использовать любой целочисленный тип, достаточно большой для индексации массива. int_fast32_t
может быть быстрее, uint_least16_t
может быть меньше по структуре и так далее. Знайте свои данные, и вы сможете сделать правильный выбор.
Следует учитывать, что на некоторых платформах для использования подписанного индекса может потребоваться дополнительная инструкция расширения знака. Например, вот x86:
// movzx eax, BYTE PTR [rcx+rdx]
// ret
char get_index(char *ptr, unsigned idx)
{
return ptr[idx];
}
// ; sign extending idx from 32 bits to 64 bits with movsx here.
// movsx rdx, edx
// movzx eax, BYTE PTR [rcx+rdx]
// ret
char get_index(char *ptr, int idx)
{
return ptr[idx];
}
Виртуальная память выходит за рамки C или C++. С их точки зрения, вы просто индексируете память, и ваша платформа должна заставить это работать. На практике ваше приложение использует только виртуальные адреса; ваш ЦП/ОС переводит виртуальный адрес в физический за кулисами. Это не то, о чем вам нужно беспокоиться.
Чтобы избежать сбоев программы, программист всегда должен использовать тип индекса, по крайней мере такой же большой, как тип, возвращаемый методом size()
. Это гарантирует, что индекс никогда не переполнит любой возможный размер массива. Реализация массива обычно гарантирует, что его размер во время выполнения никогда не переполнит тип, возвращаемый методом size()
. Это означает, что тип индекса должен быть:
size_t
в случае char[N]
, uint8_t[N]
, int[N]
и т. д.size_t
в случае std::vector
и std::list
int
в случае QList
и QVector
size()
возвращает aint)size()
возвращает aint)size()
массива возвращает aint)int
в случае java.util.Collection
и его подклассовПодводя итог: безопасный тип индекса — это тип, возвращаемый методом size()
.
Примечание. Если метод size()
возвращает беззнаковый size_t
, то знаковые int
и ssize_t
не являются безопасными типами индексов. В случае gcc и clang флаги компилятора -Wsign-compare
(включены -Wall
) и -Wconversion
можно использовать для предотвращения большинства этих случаев.
Индексация массива не связана с оперативной памятью хост-машины. Причина, по которой
size_t
рекомендуется, заключается в том, что она делает вашу программу переносимой.