何时使用std :: size_t?


201

我只是想知道我应该使用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

Answers:


185

一个好的经验法则是,您需要在循环条件下将其与自然std::size_t本身进行比较的任何内容。

std::size_t是任何sizeof表达式的类型,并保证能够表达C ++中任何对象(包括任何数组)的最大大小。通过扩展,它也保证对任何数组索引都足够大,因此它是数组上逐个索引循环的自然类型。

如果您只计算一个数字,那么使用保存该数字的变量类型或一个intunsigned int(如果足够大)的类型可能会更自然,因为这些对于机器而言应该是自然大小。


40
值得一提的是,使用size_t时机可能会导致安全漏洞
BlueRaja-Danny Pflughoeft

5
不仅int是“自然的”,而且混合有符号和无符号类型也可能导致安全错误。无符号索引是一个痛苦的处理,也是使用自定义向量类的一个很好的理由。
乔苏

2
@JoSo也有ssize_t用于签名的值。
EntangledLoops

70

size_tsizeof运算符的结果类型。

使用size_t变量数组中的模型的大小或指数。size_t传达了语义:您立即知道它代表一个以字节为单位的大小或一个索引,而不仅仅是另一个整数。

另外,使用size_t字节表示大小有助于使代码具有可移植性。


32

size_t类型用于指定内容的大小,因此很自然地使用它,例如,获取字符串的长度然后处理每个字符:

for (size_t i = 0, max = strlen (str); i < max; i++)
    doSomethingWith (str[i]);

必须注意,当然边界条件,因为它是一个无符号类型。因为最大通常比较大(虽然它在高端产品的界限通常不是重要的可以到那里)。大多数人只是将an 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)。


14
顺便说一句,调用strlen循环的每次迭代都是一种不好的做法。:)您可以执行以下操作:for (size_t i = 0, len = strlen(str); i < len; i++) ...
musiphil 2014年

1
即使它是带符号的类型,您也必须提防边界条件,甚至可能还要多加注意,因为带符号的整数溢出是未定义的行为。
阿德里安·麦卡锡

2
可以通过以下(臭名昭著)的方式正确倒计时:for (size_t i = strlen (str); i --> 0;)
Jo So

1
@JoSo,虽然我不确定我是否喜欢引入-->“ goes to”运算符(实际上请参见stackoverflow.com/questions/1642028/…),但这实际上是一个巧妙的技巧。已将您的建议纳入答案。
paxdiablo '16

您可以if (i == 0) break;在for循环的末尾做一个简单的事情吗(例如,for (size_t i = strlen(str) - 1; ; --i)。(虽然我更喜欢您,但是我想知道这是否也可以工作)。)
RastaJedi

13

根据定义,size_tsizeof运算符的结果。size_t是为了参考尺寸而创建的。

您做某事的次数(在您的示例中为10)与尺寸无关,那么为什么要使用size_tintunsigned int应该可以。

当然i,循环内的处理也很重要。unsigned int例如,如果将其传递给需要的函数,请选择unsigned int

无论如何,我建议避免隐式类型转换。使所有类型转换都明确。


10

size_t这是一种非常容易理解的方式,用于指定项目的大小尺寸-字符串的长度,指针占用的字节数等。它还可以跨平台移植-您会发现64位和32位的系统功能都很好,而且size_t-一些unsigned int可能做不到的事情(例如,何时应使用unsigned long


9

简短答案:

几乎从不

长答案:

每当您需要在32位系统上使用大于2gb的char向量时。在其他所有用例中,使用带符号的类型比使用无符号的类型安全得多。

例:

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_tptrdiff_t,没有int。但是int在大多数情况下,使用仍然比size_t好得多。ptrdiff_tlong在32个64位系统。

这意味着,当您与不太漂亮的std :: containers交互时,始终必须在size_t之间来回转换。但是在一个即将举行的原生会议上,C ++的作者提到使用无符号size_t设计std :: vector是一个错误。

如果您的编译器对从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 ++的作者)的话去本地

对于某些人来说,STL中有符号/无符号的设计错误足以说明原因,不要使用std :: vector,而是使用自己的实现。


1
我知道它们的来源,但我仍然认为写起来很奇怪for(int i = 0; i < get_size_of_stuff(); i++)。现在,可以肯定,您可能不想做很多原始循环,但是-来吧,您也可以使用它们。
einpoklum's

我使用原始循环的唯一原因是因为c ++算法库的设计非常糟糕。有些语言(例如Scala)具有更好,更进化的库,可以对集合进行操作。这样就几乎消除了原始循环的用例。也有使用新的更好的STL改进c ++的方法,但是我怀疑这种情况是否会在未来十年内发生。
Arne

1
我得到那个无符号的我= 0; 断言(i-1,MAX_INT); 但是我不明白为什么您说“如果我已经发生下溢,那就变成了事实”,因为总是定义无符号整数上的算术行为,即。结果是对最大可表示整数的大小取模的结果。因此,如果i == 0,则i--变为MAX_INT,然后i ++再次变为0。
mabraham

@mabraham我仔细地看了看,你是对的,我的代码并不是最好的显示问题的方法。通常,这x + 1 < y等价于x < y - 1,但它们不是不带正整数的整数。当事物被认为是等效的时,这很容易引入错误。
Arne

8

使用std :: size_t索引/计数C样式数组。

对于STL容器,您将拥有(例如)vector<int>::size_type,应将其用于索引和计数向量元素。

实际上,它们通常都是无符号的整数,但是不能保证,特别是在使用自定义分配器时。


2
在Linux上使用gcc时,std::size_t通常是unsigned long(在64位系统上为8字节),而不是unisgned int(4字节)。
rafak

5
size_t但是,C样式的数组不会被索引,因为索引可以是负数。但是size_t,如果一个人不想变成负数,就可以使用自己的数组实例。
Johannes Schaub-litb

在u64上进行比较是否与在u32上进行比较一样快?对于使用u8和u16作为循环标记,我已经对性能造成了严重的损失,但是我不知道英特尔是否已经在64s上达成共识。
Crashworks

2
由于C样式的数组索引等效于+对指针使用运算符,因此似乎ptrdiff_t是用于索引的一种。
帕维尔·米纳夫

8
至于vector<T>::size_type(以及所有其他容器的同上),它实际上是无用的,因为可以有效地保证它是size_t-类型定义为Allocator::size_type,对于容器的限制请参见20.1.5 / 4-特别是size_type必须是size_t并且difference_type必须是ptrdiff_t。当然,默认值std::allocator<T>可以满足这些要求。因此,只使用较短size_t的部分,而不必理会其余部分:)
Pavel Minaev 09年

7

很快,大多数计算机将成为具有64位OS的64位体系结构:运行在数十亿个元素的容器上运行的程序。然后,您必须使用size_t而不是int作为循环索引,否则在32位和64位系统上,索引都在2 ^ 32:th元素处环绕

为未来做准备!


您的论点仅涉及一个需要一个long int而不是一个的含义int。如果size_t与64位操作系统相关,则与32位操作系统相关。
einpoklum

4

使用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;
}

无论x的值是多少,您都会在if表达式中得到false。我花了几天时间才意识到这一点(代码是如此简单,以至于我没有进行单元测试),尽管只花了几分钟就可以找出问题的根源。不确定执行强制转换或使用零更好。

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
请注意,这(int)(i - 7)是一个强制转换为int之后int(i) - 7的下溢,而不是由于您先转换iint,然后减去,所以不是下溢7。另外,我发现您的示例令人困惑。
hochl

我的观点是,进行减法运算时,通常int更安全。
Kemin Zhou '18

4

各种库返回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");
}

使用无符号整数有可能造成这些类型的细微问题。因此,恕我直言,我更喜欢仅在与需要它的容器/类型交互时才使用size_t。


Everone似乎不费吹灰之力地在循环中使用size_t,而我却很难学到这一点
Pranjal Gupta

-2

size_t是一种无符号类型,可以容纳您的体系结构的最大整数值,因此可以防止整数因符号(带符号的int 0x7FFFFFFF递增1会给您-1)或短的大小(无符号的短int 0xFFFF会使您给整数)溢出0)。

它主要用于数组索引/循环/地址运算等。类似的功能仅memset()接受size_t,因为从理论上讲,您可能会拥有一块大小的内存2^32-1(在32位平台上)。

对于这样的简单循环,不要打扰,只使用int即可。


-3

size_t是无符号整数类型,可以表示系统上的最大整数。仅在需要非常大的数组,矩阵等时才使用它。

某些函数返回size_t,并且如果您尝试进行比较,则编译器会警告您。

为避免这种情况,请使用适当的带符号/无符号数据类型,或者只是简单地进行类型转换以快速破解。


4
仅在要避免错误和安全漏洞时才使用它。
Craig McQueen

2
它实际上可能无法代表系统上的最大整数。
阿德里安·麦卡锡

-4

size_t是unsigned int。因此,无论何时您想要unsigned int都可以使用它。

我想指定数组的大小,计数器等时使用它

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

10
实际上,它不一定与unsigned int相同。它无符号的,但可能比int更大(或者我猜是较小的,尽管我不知道这是真的)。
Todd Gamblin,2009年

例如,在64位计算机上,它size_t可能是一个无符号的64位整数,而在32位计算机上,它只是一个32位的无符号整数。
HerpDerpington
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.