什么时候使用std::size_t?

我只是想知道我是否应该使用std::size_t for循环和东西而不是int? 例如:< / p >

#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的最佳实践是什么?

139536 次浏览

Size_t是unsigned int。所以当你需要unsigned int时,你可以使用它。

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

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

根据定义,size_tsizeof操作符的结果。size_t是用来引用大小的。

你做某事的次数(在你的例子中是10次)与大小无关,所以为什么要使用size_t呢?intunsigned int应该是可以的。

当然,你在循环中对i做什么也是相关的。例如,如果将它传递给一个接受unsigned int的函数,则选择unsigned int

在任何情况下,我都建议避免隐式类型转换。使所有类型转换显式。

一个很好的经验法则是,你需要在循环条件中与自然是std::size_t本身的东西进行比较。

std::size_t是任何sizeof表达式的类型,as被保证能够在c++中表达任何对象(包括任何数组)的最大大小。通过扩展,它也保证足够大,可以用于任何数组下标,因此它是数组上按下标循环的自然类型。

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

size_tsizeof操作符的结果类型。

size_t用于在数组中建模大小或索引的变量。size_t传达语义:你立即知道它表示字节或索引的大小,而不仅仅是另一个整数。

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

size_t是一种非常可读的方式来指定一个项目的大小维度-字符串的长度,指针占用的字节数,等等。 它也可以跨平台移植——你会发现64位和32位都可以很好地使用系统函数和size_t——这是unsigned int可能做不到的(例如,什么时候应该使用unsigned long

使用std::size_t对c风格数组进行索引/计数。

对于STL容器,你会有(例如)vector<int>::size_type,它应该用于索引和计数向量元素。

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

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是一种无符号类型,它可以为你的体系结构保存最大的整数值,因此它不会因为符号(有符号的int 0x7FFFFFFF加1会得到-1)或短大小(无符号的短int 0xFFFF加1会得到0)而导致整数溢出。

它主要用于数组索引/循环/地址算法等。像memset()这样的函数只接受size_t,因为理论上你可能有一个大小为2^32-1的内存块(在32位平台上)。

对于这样简单的循环,不要麻烦,只使用int。

size_t是一个无符号整型,它可以表示系统中最大的整数。 只有当你需要非常大的数组,矩阵等时才使用它

有些函数返回size_t,如果你试图进行比较,编译器会警告你。

通过使用适当的有符号/无符号数据类型或简单的类型转换来避免快速破解。

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

为未来做好准备!

当使用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。 我花了几天时间才意识到这一点(代码太简单了,我没有做单元测试),尽管只花了几分钟就找到了问题的根源。不确定是执行强制转换还是使用0更好
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

我想听听其他人的意见。

简短的回答:

几乎从来没有。使用signed版本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要好得多。ptrdiff_t在32位和64位系统上是long

这意味着无论何时与std::容器交互,都必须从size_t转换到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:

for(const auto& d : data) {
[...]
}

这里是Bjarne Stroustrup (c++作者)在将本机的一些词

对于一些人来说,STL中的有符号/无符号设计错误是足够的理由,不使用std::vector,而是使用自己的实现。

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只是一个无符号整型数据类型,它定义在各种头文件中,如<stddef.h>, <stdio.h>, <stdlib.h>, <string.h>, <time.h>, <wchar.h>等。

它用于以字节为单位表示对象的大小,因此被sizeof操作符用作返回类型。允许的最大大小取决于编译器;如果编译器是32位的,那么它只是unsigned int的类型定义(别名),但如果编译器是64位的,那么它将是unsigned long long的类型定义。size_t数据类型从不为负(不包括ssize_t) 因此,许多C库函数,如malloc, memcpy and strlen,将其参数声明为size_t,并返回类型为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。例如,

vector<int> a = {1,2,3,4};
for (size_t i=0; i<a.size(); i++) {
std::cout << a[i] << std::endl;
}
size_t n = a.size();
for (size_t i=n-1; i>=0; i--) {
std::cout << a[i] << std::endl;
}

第一个循环是好的。但是对于第二个循环:
当i=0时,i——的结果将是ULLONG_MAX(假设size_t = unsigned long long),这不是你在循环中想要的结果。
此外,如果a为空,则n=0, n-1=ULLONG_MAX,这也不好