Когда следует использовать std:: size t?
мне просто интересно, должен ли я использовать std::size_t
для петель и прочее вместо int
?
Например:
#include <cstdint>
int main()
{
for (std::size_t i = 0; i < 10; ++i) {
// std::size_t OK here? Or should I use, say, unsigned int instead?
}
}
В общем, что является лучшей практикой в отношении того, когда использовать std::size_t
?
13 ответов:
хорошее эмпирическое правило для всего, что вам нужно сравнить в состоянии цикла против чего-то, что естественно a
std::size_t
сам по себе.
std::size_t
тип любоеsizeof
expression и as гарантированно смогут выразить максимальный размер любого объекта (включая любой массив) в C++. По расширению он также гарантированно будет достаточно большим для любого индекса массива, поэтому это естественный тип для цикла по индексу над массивом.если вы просто считаете до a число тогда может быть более естественным использовать либо тип переменной, которая содержит это число, либо
int
илиunsigned int
(если достаточно большой), поскольку они должны быть естественным размером для машины.
size_t
- Это тип результатаsizeof
оператора.использовать
size_t
для переменных, которые моделируют размер или индекс в массиве.size_t
передает семантику: вы сразу знаете, что он представляет собой размер в байтах или индекс, а не просто другое целое число.также, используя
size_t
для представления размера в байтах помогает сделать код переносимым.
The
size_t
тип предназначен для указания в размере чего-то, поэтому естественно использовать его, например, получая длину строки, а затем обрабатывая каждый символ:for (size_t i = 0, max = strlen (str); i < max; i++) doSomethingWith (str[i]);
вы do должны следить за граничными условиями, конечно, так как это беззнаковый тип. Граница в верхнем конце обычно не так важна, так как максимум обычно большой (хотя это и можно туда попасть). Большинство людей просто используют Ан
int
для такого рода вещей, потому что они редко имеют структуры или массивы, которые становятся достаточно большими, чтобы превысить емкость этогоint
.но следите за такими вещами, как:
for (size_t i = strlen (str) - 1; i >= 0; i--)
что вызовет бесконечный цикл из-за поведения обертывания беззнаковых значений (хотя я видел, что компиляторы предупреждают об этом). Это также может быть облегчено (немного сложнее понять, но, по крайней мере, иммунитет к проблемам упаковки):
for (size_t i = strlen (str); i-- > 0; )
By смещение декремента в побочный эффект после проверки условия продолжения, это делает проверку продолжения на значение до decrement, но все еще использует уменьшенное значение внутри цикла (именно поэтому цикл выполняется из
len .. 1
, а неlen-1 .. 0
).
по определению
size_t
результатsizeof
оператора.size_t
был создан для обозначения размеров.количество раз вы делаете что-то (10, в вашем примере) не о размерах, так зачем использовать
size_t
?int
илиunsigned int
, должно быть нормально.конечно, это также важно, что вы делаете с
i
внутри цикла. Если вы передадите его в функцию, которая принимаетunsigned int
, например, pickunsigned int
.в любом случае, я рекомендую избегать неявное преобразование типов. сделать все явные преобразования типов.
size_t
Это очень читаемый способ указать размер размер элемента-длина строки, количество байтов, которые принимает указатель и т. д. Он также переносится на разных платформах - вы обнаружите, что 64bit и 32bit оба ведут себя хорошо с системными функциями иsize_t
- что-нибудь вродеunsigned int
может не делать (например, когда вы должны использоватьunsigned long
используйте std:: size_t для индексирования/подсчета массивов C-стиля.
для контейнеров STL, вы будете иметь (например)
vector<int>::size_type
, который должен использоваться для индексации и подсчета векторных элементов.на практике они обычно являются беззнаковыми int, но это не гарантируется, особенно при использовании пользовательских распределителей.
скоро большинство компьютеров будут 64-битными архитектурами с 64-битной ОС: es под управлением программ, работающих на контейнерах миллиардов элементов. Тогда ты должны использовать
size_t
вместоint
как индекс цикла, в противном случае ваш индекс будет обернуть вокруг на 2^32:й элемент, как на 32-и 64-разрядных системах.готовьтесь к будущему!
короткий ответ:
почти никогда не
ответ:
всякий раз, когда вам нужно иметь вектор char больше, чем 2 Гб на 32-битной системе. В любом другом случае использование подписанного типа намного безопаснее, чем использование беззнакового типа.
пример:
std::vector<A> data; [...] // calculate the index that should be used; size_t i = calc_index(param1, param2); // doing calculations close to the underflow of an integer is already dangerous // do some bounds checking if( i - 1 < 0 ) { // always false, because 0-1 on unsigned creates an underflow return LEFT_BORDER; } else if( i >= data.size() - 1 ) { // if i already had an underflow, this becomes true return RIGHT_BORDER; } // now you have a bug that is very hard to track, because you never // get an exception or anything anymore, to detect that you actually // return the false border case. return calc_something(data[i-1], data[i], data[i+1]);
подписанный эквивалент
size_t
- этоptrdiff_t
, а неint
. Но с помощьюint
все еще намного лучше в большинстве случаев, чем size_t.ptrdiff_t
иlong
на 32 и 64 бит системный.это означает, что вы всегда должны конвертировать в и из size_t всякий раз, когда вы взаимодействуете с std::контейнеры, которые не очень красиво. Но на идущей родной конференции авторы c++ упомянули, что проектирование std::vector с беззнаковым size_t было ошибкой.
если ваш компилятор дает вам предупреждения о неявных преобразованиях из ptrdiff_t в size_t, вы можете сделать его явным с помощью синтаксиса конструктора:
calc_something(data[size_t(i-1)], data[size_t(i)], data[size_t(i+1)]);
если просто хотите перебирать коллекция, без проверки границ, использует диапазон, основанный на:
for(const auto& d : data) { [...] }
вот несколько слов от Бьярне Страуструпа (автора C++) в буду уроженца
для некоторых людей эта подписанная / неподписанная ошибка проектирования в STL является достаточной причиной, чтобы не использовать std::vector, а вместо этого собственную реализацию.
при использовании size_t будьте осторожны со следующим выражением
size_t i = containner.find("mytoken"); size_t x = 99; if (i-x>-1 && i+x < containner.size()) { cout << containner[i-x] << " " << containner[i+x] << endl; }
вы получите false в выражении if независимо от того, какое значение у вас есть для x. Мне потребовалось несколько дней, чтобы понять это (код настолько прост, что я не делал модульный тест), хотя это займет всего несколько минут, чтобы понять источник проблемы. Не уверен, что лучше сделать бросок или использовать ноль.
if ((int)(i-x) > -1 or (i-x) >= 0)
оба способа должны работать. Вот мой тестовый прогон
size_t i = 5; cerr << "i-7=" << i-7 << " (int)(i-7)=" << (int)(i-7) << endl;
В выход: i-7=18446744073709551614 (int) (i-7)=-2
Я хотел бы получить комментарии других.
size_t возвращается различными библиотеками, чтобы указать, что размер этого контейнера не равен нулю. Вы используете его, когда вы получаете один раз обратно :0
однако, в вашем примере выше цикл на size_t является потенциальной ошибкой. Рассмотрим следующее:
for (size_t i = thing.size(); i >= 0; --i) { // this will never terminate because size_t is a typedef for // unsigned int which can not be negative by definition // therefore i will always be >= 0 printf("the never ending story. la la la la"); }
использование целых чисел без знака имеет потенциал для создания этих типов тонких проблем. Поэтому imho я предпочитаю использовать size_t только тогда, когда я взаимодействую с контейнерами/типами, которые этого требуют.
size_t
- Это беззнаковый тип, который может содержать максимальное целочисленное значение для вашей архитектуры, поэтому он защищен от переполнения целых чисел из-за знака (signed int0x7FFFFFFF
увеличенный на 1 даст вам -1) или короткий размер (unsigned short int 0xFFFF, увеличенный на 1, даст вам 0).Он главным образом использован в индексировании массива/петлях/арифметике адреса и так далее. Функции, такие как
memset()
и признатьsize_t
только потому, что теоретически у вас может быть блок памяти размера2^32-1
(на 32-битной платформе).для таких простых петель не беспокойтесь и используйте только int.
size_t-это беззнаковый интегральный тип, который может представлять самое большое целое число в вашей системе. Используйте его только в том случае,если вам нужны очень большие массивы, матрицы и т. д.
некоторые функции возвращают size_t и ваш компилятор предупредит вас, если вы попытаетесь сделать сравнения.
избегайте этого, используя соответствующий подписанный / неподписанный тип данных или просто типизацию для быстрого взлома.