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

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, например, pick unsigned 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 int 0x7FFFFFFF увеличенный на 1 даст вам -1) или короткий размер (unsigned short int 0xFFFF, увеличенный на 1, даст вам 0).

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

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

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

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

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

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

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

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