在 size_t 中使用负整数安全吗?
Is it safe to use negative integers with size_t?
我刚刚看到一些这样的 C++ 代码。它使用一个条件来决定是向前走还是向后走 std::vector
。编译器没有抱怨,但我认为 size_t
是未签名的。这很危险吗?
vector<int> v { 1,2,3,4,5 };
bool rev = true;
size_t start, end, di;
if (rev) {
start = v.size()-1;
end = -1;
di = -1;
}
else {
start = 0;
end = v.size();
di = 1;
}
for (auto i=start; i!=end; i+=di) {
cout << v[i] << endl;
}
Is it safe to use negative integers with size_t?
不,这很危险。溢出。
size_t a = -1;
std::cout << a << "\n";
输出:
4294967295 // depends on the system, largest value possible here
我不能说该代码有多安全,但我认为这是一种非常糟糕的风格。更好的方法是使用支持正向或反向迭代的迭代器。
例如:
std::vector<int> v = { 1, 2, 3, 4, 5 };
bool rev = true;
if (rev)
{
for (auto itr = v.rbegin(); itr != v.rend(); ++itr)
{
std::cout << *itr << "\n";
}
}
else
{
for (auto itr = v.begin(); itr != v.end(); ++itr)
{
std::cout << *itr << "\n";
}
}
以这种方式使用无符号整数(并且 size_t
是无符号的)是明确定义的,带有环绕:该行为由标准保证,而不是有符号整数,标准不保证.
然而,这是不必要的聪明。
作为一般规则,为了避免由于对无符号的隐式包装提升而导致的问题,对位级的东西使用无符号整数,对数字使用有符号整数。在您需要对应于 size_t
的有符号整数的地方,有 ptrdiff_t
适合您。定义一个带符号结果的 n_items
函数,例如
using Size = ptrdiff_t;
template< class Container >
auto n_items( Container const& c )
-> Size
{ return end( c ) - begin( c ); }
您就可以开始了,编译器不会再发出愚蠢的警告了。
而不是太聪明的给定代码
vector<int> v { 1,2,3,4,5 };
bool rev = true;
size_t start, end, di;
if (rev) {
start = v.size()-1;
end = -1;
di = -1;
}
else {
start = 0;
end = v.size();
di = 1;
}
for (auto i=start; i!=end; i+=di) {
cout << v[i] << endl;
做例如
const vector<int> v { 1,2,3,4,5 };
const bool reverse = true; // whatever
for( int i = 0; i < n_items( v ); ++i )
{
const int j = (reverse? n_items( v ) - i - 1 : i);
cout << v[j] << endl;
}
每当我需要处理签名类型时,我总是使用:
typedef std::make_signed<std::size_t>::type ssize_t; // Since C++11
...作为 std::size_t.
的签名替代品
我知道这个问题已经有几年了,但我希望这能帮助其他人。感谢 moodycamel::ConcurrentQueue.
我刚刚看到一些这样的 C++ 代码。它使用一个条件来决定是向前走还是向后走 std::vector
。编译器没有抱怨,但我认为 size_t
是未签名的。这很危险吗?
vector<int> v { 1,2,3,4,5 };
bool rev = true;
size_t start, end, di;
if (rev) {
start = v.size()-1;
end = -1;
di = -1;
}
else {
start = 0;
end = v.size();
di = 1;
}
for (auto i=start; i!=end; i+=di) {
cout << v[i] << endl;
}
Is it safe to use negative integers with size_t?
不,这很危险。溢出。
size_t a = -1;
std::cout << a << "\n";
输出:
4294967295 // depends on the system, largest value possible here
我不能说该代码有多安全,但我认为这是一种非常糟糕的风格。更好的方法是使用支持正向或反向迭代的迭代器。
例如:
std::vector<int> v = { 1, 2, 3, 4, 5 };
bool rev = true;
if (rev)
{
for (auto itr = v.rbegin(); itr != v.rend(); ++itr)
{
std::cout << *itr << "\n";
}
}
else
{
for (auto itr = v.begin(); itr != v.end(); ++itr)
{
std::cout << *itr << "\n";
}
}
以这种方式使用无符号整数(并且 size_t
是无符号的)是明确定义的,带有环绕:该行为由标准保证,而不是有符号整数,标准不保证.
然而,这是不必要的聪明。
作为一般规则,为了避免由于对无符号的隐式包装提升而导致的问题,对位级的东西使用无符号整数,对数字使用有符号整数。在您需要对应于 size_t
的有符号整数的地方,有 ptrdiff_t
适合您。定义一个带符号结果的 n_items
函数,例如
using Size = ptrdiff_t;
template< class Container >
auto n_items( Container const& c )
-> Size
{ return end( c ) - begin( c ); }
您就可以开始了,编译器不会再发出愚蠢的警告了。
而不是太聪明的给定代码
vector<int> v { 1,2,3,4,5 };
bool rev = true;
size_t start, end, di;
if (rev) {
start = v.size()-1;
end = -1;
di = -1;
}
else {
start = 0;
end = v.size();
di = 1;
}
for (auto i=start; i!=end; i+=di) {
cout << v[i] << endl;
做例如
const vector<int> v { 1,2,3,4,5 };
const bool reverse = true; // whatever
for( int i = 0; i < n_items( v ); ++i )
{
const int j = (reverse? n_items( v ) - i - 1 : i);
cout << v[j] << endl;
}
每当我需要处理签名类型时,我总是使用:
typedef std::make_signed<std::size_t>::type ssize_t; // Since C++11
...作为 std::size_t.
的签名替代品我知道这个问题已经有几年了,但我希望这能帮助其他人。感谢 moodycamel::ConcurrentQueue.