Когда я должен использовать std :: size_t? - PullRequest
175 голосов
/ 23 декабря 2009

Мне просто интересно, стоит ли использовать 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 ]

164 голосов
/ 23 декабря 2009

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

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

Если вы просто рассчитываете до числа, то может быть более естественным использовать либо тип переменной, содержащей это число, либо int или unsigned int (если он достаточно большой), так как они должны быть естественными Размер для машины.

68 голосов
/ 23 декабря 2009

size_t - это тип результата оператора sizeof.

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

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

30 голосов
/ 23 декабря 2009

Тип 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; )

Путем смещения декремента в побочный эффект условия проверки продолжения после проверки выполняется проверка продолжения на значение до декремента , но все еще используется уменьшенное значение внутри цикла (которое почему цикл запускается с len .. 1, а не len-1 .. 0).

12 голосов
/ 23 декабря 2009

По определению size_t является результатом оператора sizeof. size_t был создан для обозначения размеров.

Количество раз, когда вы что-то делаете (в вашем примере 10), не о размерах, так зачем использовать size_t? int или unsigned int, должно быть в порядке.

Конечно, также важно, что вы делаете с i внутри цикла. Если вы передаете его функции, например, unsigned int, выберите unsigned int.

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

10 голосов
/ 23 декабря 2009

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

8 голосов
/ 23 декабря 2009

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

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

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

6 голосов
/ 12 ноября 2015

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

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

длинный ответ:

Всякий раз, когда вам нужно, чтобы вектор символа был больше 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 :: Containers, что не очень красиво. Но на нативной конференции авторы 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, а вместо этого собственную реализацию.

6 голосов
/ 27 апреля 2011

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

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

3 голосов
/ 07 октября 2014

При использовании 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

Я хотел бы получить комментарии других.

2 голосов
/ 20 июля 2016

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 только тогда, когда я взаимодействую с контейнерами / типами, которым это требуется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...