我只是想知道在循环和其他方面,我应该使用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
是最佳实践?
我只是想知道在循环和其他方面,我应该使用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
是最佳实践?
一个好的经验法则是对于需要在循环条件中与自然为std::size_t
的某些内容进行比较的任何东西,都应该使用类型本身。
std::size_t
是任何sizeof
表达式的类型,并且保证能够表示C++中任何对象(包括任何数组)的最大大小。因此,它也被保证足够大来表示任何数组索引,因此是一个循环遍历数组的自然类型。
如果你只是计数到一个数字,那么使用持有该数字的变量的类型或者int
或unsigned int
(如果足够大)可能更自然一些,因为这些类型应该是机器的自然大小。
size_t
是sizeof
运算符的结果类型。
在表示数组大小或索引的变量中使用size_t
。 size_t
传达语义:您立即知道它表示字节大小或索引,而不仅仅是另一个整数。
此外,使用size_t
表示字节大小有助于使代码可移植。
size_t
类型用于指定某个对象的大小,因此在获取字符串长度并处理每个字符时自然而然地使用它:
for (size_t i = 0, max = strlen (str); i < max; i++)
doSomethingWith (str[i]);
当然,你必须注意边界条件,因为它是无符号类型。顶部的边界通常并不那么重要,因为最大值通常很大(尽管有可能会达到那个值)。大多数人只使用 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
运行的原因)。
strlen
不是一个好习惯。您可以像这样做:for(size_t i = 0, len = strlen(str); i < len; i++) ...
- musiphilfor (size_t i = strlen(str); i --> 0;)
- Jo Soif (i == 0) break;
吗?例如: for (size_t i = strlen(str) - 1; ; --i)
。(我更喜欢你的方法,只是想知道这个方法是否同样有效)。 - RastaJedi几乎从不。使用带符号的版本ptrdiff_t
或非标准的ssize_t
。使用函数std::ssize
代替std::size
。
只有在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_t
的有符号等价类型是ptrdiff_t
,而不是int
。但在大多数情况下,使用int
仍然比size_t
更好。在32位和64位系统上,ptrdiff_t
是long
类型。ptrdiff_t
到size_t
的隐式转换方面发出警告,则可以使用构造函数语法使其明确。calc_something(data[size_t(i-1)], data[size_t(i)], data[size_t(i+1)]);
如果只想迭代一个集合,而不需要检查边界,请使用基于范围的for循环:
for(const auto& d : data) {
[...]
}
以下是C++之父Bjarne Stroustrup在“going native”演讲中的一些话:(原文链接)
对于一些人来说,STL中的签名/无符号设计错误足以成为不使用std::vector而使用自己的实现的原因。
for(int i = 0; i < get_size_of_stuff(); i++)
很奇怪。当然,你可能不想做很多原始循环,但是 - 来吧,你也会使用它们。 - einpoklumx + 1 < y
等同于 x < y - 1
,但是对于无符号整数来说并非如此。这很容易在转换时引入错误,因为假定它们是等价的。 - Arnesize_t
是根据sizeof
运算符计算的结果,被用于表示大小。
在您的示例中,您执行某些操作的次数(即10)与大小无关,所以为什么要使用size_t
呢?使用int
或unsigned int
应该可以。
当然,您在循环内部对i
执行的操作也很重要。例如,如果将其传递给需要unsigned int
的函数,则选择unsigned int
。
无论如何,我建议避免隐式类型转换。 让所有类型转换都显式化。
size_t
是一种非常易读的方式来指定一个项目的尺寸维度——例如字符串长度、指针占用的字节数等。此外,它在不同平台上也很便携——你会发现64位和32位都可以很好地与系统函数和 size_t
一起使用,而使用unsigned int
可能不会这样(例如,何时应使用 unsigned long
)。
在索引/计数C风格数组时,请使用std::size_t。
对于STL容器,您将拥有(例如)vector<int>::size_type
,应该用于索引和计数向量元素。
实际上,它们通常都是无符号整数,但并不保证,特别是在使用自定义分配器时。
std::size_t
通常是 unsigned long
,而不是unsigned int
(在64位系统上为8个字节)。 - rafaksize_t
进行索引,因为索引可以是负数。如果不希望出现负数索引,可以对自己定义的此类数组使用size_t
进行索引。请注意,这并不改变数组本身的特性。 - Johannes Schaub - litbvector<T>::size_type
(以及其他所有容器),它实际上是相当无用的,因为它基本上保证为size_t
- 它被typedef为Allocator::size_type
,并且对于与容器相关的其余限制,请参见20.1.5/4 - 特别是,size_type
必须是size_t
,difference_type
必须是ptrdiff_t
。当然,默认的std::allocator<T>
满足这些要求。所以只需使用更短的size_t
,不用关心其他东西 :) - Pavel Minaev很快,大部分计算机都将采用64位架构,并运行在操作数以亿计的容器上的64位操作系统。因此,您必须使用size_t
而不是int
作为循环索引,否则您的索引将在第2^32个元素处回绕,无论是在32位还是64位系统上。
为未来做好准备!
long int
而不是一个 int
。如果在 64 位操作系统上 size_t
是相关的,那么在 32 位操作系统上它同样重要。 - einpoklumsize_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。
使用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;
}
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;
(int)(i - 7)
是一个下溢,之后被转换为 int
,而 int(i) - 7
不是下溢,因为你首先将 i
转换为 int
,然后再减去 7
。此外,我发现你的示例令人困惑。 - hochl
size_t
而没有使用时,可能会导致安全漏洞。 - BlueRaja - Danny Pflughoeftssize_t
。 - EntangledLoopsssize_t
并没有size_t
的全部范围。它只是size_t
所转换的带符号变量。这意味着,使用ssize_t
时无法使用内存的全部范围,并且在依赖于size_t
类型的变量时可能会发生整数溢出。 - Thomasstd::uintmax_t
呢?它很可能足够大了,如果不够大,那么你无论如何都需要有创造性。编辑:这是 C++11,我刚刚查看了一下,答案是来自2009年。 - Bolpat