Когда следует использовать 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 ответов


хорошее эмпирическое правило для всего, что вам нужно сравнить в состоянии цикла с чем-то, что естественно .

std::size_t тип любое sizeof expression и as гарантированно смогут выразить максимальный размер любого объекта (включая любой массив) в C++. По расширению он также гарантированно будет достаточно большим для любого индекса массива, поэтому он является естественным типом для цикла по индексу над массивом.

если вы просто подсчитываете до число тогда может быть более естественным использовать либо тип переменной, которая содержит это число, либо 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 конечно, нужно следить за граничными условиями, так как это тип без знака. Граница на верхнем конце обычно не так важна, так как максимум обычно большой (хотя это is возможно добраться туда). Большинство людей просто используют Ан 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, например, pick unsigned int.

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


size_t - очень читаемый способ указать размер элемента-длину строки, количество байтов, которые принимает указатель, и т. д. Он также переносим на разных платформах - вы обнаружите, что 64bit и 32bit оба ведут себя хорошо с системными функциями и size_t - что-нибудь вроде unsigned int может не делать (например, когда вы должны использовать unsigned long


используйте std:: size_t для индексирования/подсчета массивов C-стиля.

для контейнеров STL у вас будет (например) vector<int>::size_type, который следует использовать для индексирования и подсчета векторных элементов.

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


скоро большинство компьютеров будут 64-битными архитектурами с 64-битной ОС: es запущенными программами, работающими на контейнерах миллиардов элементов. Тогда ты!--3-->должны использовать size_t вместо int как индекс цикла, иначе ваш индекс будет обернуть вокруг на элементе 2^32: th, как на 32 -, так и на 64-разрядных системах.

готовиться к будущему!


короткий ответ:

почти никогда не

ответ:

всякий раз, когда вам нужно иметь вектор char больше, чем 2gb на 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 is ptrdiff_t, а не int. Но используя int все еще намного лучше в большинстве случаев, чем size_t. ptrdiff_t is 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) {
    [...]
}

вот несколько слов из Bjarne Stroustrup (c++ author) at буду уроженца

для некоторых людей эта ошибка подписанного / неподписанного дизайна в 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 int 0x7FFFFFFF incremented by 1 даст вам -1) или короткий размер (unsigned short int 0xFFFF incremented by 1 даст вам 0).

Он главным образом использован в индексировании массива/петлях/арифметике адреса и так далее. Функции как memset() и признать size_t только, потому что теоретически у вас может быть блок памяти размера 2^32-1 (на 32-битной платформе).

для таких простых циклов не беспокойтесь и используйте только int.


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

некоторые функции возвращают size_t, и ваш компилятор предупредит вас, если вы попытаетесь сделать сравнения.

избегайте этого, используя соответствующий подписанный / неподписанный тип данных или просто typecast для быстрого взлома.


size_t является unsigned int. поэтому, когда вы хотите unsigned int, вы можете использовать его.

Я использую его, когда хочу указать размер массива, счетчик ect...

void * operator new (size_t size); is a good use of it.