我只是想知道我应该使用std::size_t
for 循环和其他东西而不是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
for 循环和其他东西而不是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
?
一个好的经验法则是,对于需要在循环条件中与自然为 a 的事物进行比较的任何事物std::size_t
。
std::size_t
是任何sizeof
表达式的类型,as 保证能够表示 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
)。
根据定义,size_t
是sizeof
运算符的结果。size_t
创建是为了参考尺寸。
您做某事的次数(在您的示例中为 10 次)与大小无关,那么为什么要使用size_t
? int
, 或unsigned int
, 应该没问题。
当然,你i
在循环内做什么也很重要。如果你将它传递给一个接受 的函数unsigned int
,例如, pick unsigned int
。
无论如何,我建议避免隐式类型转换。使所有类型转换显式。
几乎从不
每当您需要在 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 好得多。ptrdiff_t
在long
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,而是使用自己的实现。
size_t
是一种非常易读的方式来指定项目的大小维度 - 字符串的长度、指针占用的字节数等。它还可以跨平台移植 - 你会发现 64 位和 32 位都可以很好地使用系统函数和size_t
-unsigned int
可能不会做的事情(例如你应该什么时候使用unsigned long
使用 std::size_t 对 C 样式数组进行索引/计数。
对于 STL 容器,您将拥有 (例如) vector<int>::size_type
,它应该用于索引和计数向量元素。
在实践中,它们通常都是无符号整数,但不能保证,尤其是在使用自定义分配器时。
很快,大多数计算机将采用 64 位体系结构和 64 位操作系统:运行在包含数十亿个元素的容器上运行的程序。然后你必须使用size_t
而不是int
作为循环索引,否则你的索引将在 2^32:th 元素处环绕,在 32 位和 64 位系统上。
为未来做准备!
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 。
使用 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 表达式中得到错误。我花了几天的时间才意识到这一点(代码太简单了,我没有做单元测试),尽管只需要几分钟就可以找出问题的根源。不确定进行强制转换或使用零会更好。
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
是一种无符号类型,可以为您的体系结构保存最大整数值,因此可以防止由于符号(有符号0x7FFFFFFF
的 int 递增 1 将给您 -1)或短大小(无符号短整数 0xFFFF 递增 1 会给您带来整数溢出) 0)。
主要用于数组索引/循环/地址运算等。类似的函数只memset()
接受size_t
,因为理论上你可能有一块大小的内存2^32-1
(在 32 位平台上)。
对于这样简单的循环,不要打扰,只需使用 int。
我一直在努力理解什么以及何时使用它。但是 size_t 只是一个无符号整数数据类型,它在各种头文件中定义,例如 <stddef.h>, <stdio.h>, <stdlib.h>, <string.h>, <time.h>, <wchar.h>
等。
它用于以字节为单位表示对象的大小,因此它被 sizeof 运算符用作返回类型。最大允许大小取决于编译器;如果编译器是 32 位,那么它只是 unsigned int 的 typedef(别名),但如果编译器是 64 位,那么它将是 unsigned long long 的 typedef。size_t 数据类型永远不会是负数(不包括 ssize_t),因此许多 C 库函数喜欢malloc, memcpy and strlen
将它们的参数和返回类型声明为size_t
.
/ Declaration of various standard library functions.
// Here argument of 'n' refers to maximum blocks that can be
// allocated which is guaranteed to be non-negative.
void *malloc(size_t n);
// While copying 'n' bytes from 's2' to 's1'
// n must be non-negative integer.
void *memcpy(void *s1, void const *s2, size_t n);
// the size of any string or `std::vector<char> st;` will always be at least 0.
size_t strlen(char const *s);
size_t
或者任何无符号类型都可能被视为循环变量,因为循环变量通常大于或等于 0。
size_t 是无符号整数类型,可以表示系统上的最大整数。仅当您需要非常大的数组、矩阵等时才使用它。
一些函数返回一个 size_t,如果您尝试进行比较,您的编译器会警告您。
通过使用适当的有符号/无符号数据类型或简单地进行类型转换以进行快速破解来避免这种情况。
size_t 是无符号整数。所以每当你想要 unsigned int 时,你都可以使用它。
当我想指定数组的大小时使用它,计数器等...
void * operator new (size_t size); is a good use of it.