让我们假设这个世界中的所有数字都是正整数,它们可以用uintX_t C ++类型表示。
让我们考虑将std :: string转换为数字的下一个很棒的代码:
#include <string>
#include <cstdint>
#include <iostream>
template <typename T>
T MyAwsomeConversionFunction(const std::string& value)
{
T result = 0;
for(auto it = value.begin(); it != value.end() && std::isdigit(*it); ++it)
{
result = result * 10 + *it - '0';
}
return result;
}
int main(int argc, const char * argv[])
{
std::cout<<MyAwsomeConversionFunction<uint16_t>("1234")<<std::endl;
std::cout<<MyAwsomeConversionFunction<uint16_t>("123456")<<std::endl;
return 0;
}
正如您所看到的,此函数中存在多个错误,但我对特定的错误感兴趣:如何检测何时类型不足以包含值(第二次转换调用为例)并避免UB时制作result = result * 10 + *it - '0';
。我想知道在执行之前该操作是否会超过T
的最大值。这可能吗?
编辑:请查看Is signed integer overflow still undefined behavior in C++?以获取有关使用C ++进行算术运算的UB的更多信息。我想避免在结果溢出时执行行result = result * 10 + *it - '0';
。在答案中,该行仍在执行......
EDIT2:我在这里找到答案:How to detect integer overflow?
EDIT3:接受的答案适用于签名类型。对于未签名的类型干杯和hth。 - 阿尔法回答是正确的。
答案 0 :(得分:1)
我会对此感到震惊,虽然我可能因为错误而被分开。这不处理字符串中的负值(您的原始代码也没有)。并且它仅限于ASCII数字,正如Alf在评论中提到的那样。
template <typename T>
T MyAwsomeConversionFunction(const std::string& value)
{
T maxBeforeMult = std::numeric_limits<T>::max / 10;
T result = 0;
for(auto it = value.begin(); it != value.end() && std::isdigit(*it); ++it)
{
// Check if multiplying would overflow
if (result > maxBeforeMult)
{
// throw overflow
}
result = result * 10;
T digit = *it - 0;
// Check if adding would overflow
if (std::numeric_limits<T>::max - result < digit)
{
// throw overflow
}
result += digit;
}
return result;
}
答案 1 :(得分:1)
你只需要向后工作,询问给定的数字是否会溢出:
// When result exceeds this thresh, appending a digit will always overflow.
static const T thresh = std::numeric_limits<T>::max() / 10;
// When result equals this thresh, appending a digit larger than
// thresh_last_digit will overflow.
static const T thresh_last_digit = std::numeric_limits<T>::max() - 10 * thresh;
for(auto it = value.begin(); it != value.end() && std::isdigit(*it); ++it)
{
if(result > threshold)
throw std::overflow_error(value);
T digit = *it - '0';
if(result == threshold && digit > thresh_last_digit)
throw std::overflow_error(value);
result = result * 10 + digit;
}
答案 2 :(得分:0)
对于无符号类型T
,您始终可以执行
T const original = result;
result = result * 10 + *it - '0';
if( result / 10 != original ) { throw 666; }
除了用某些东西替换throw 666
。
对于使用溢出检测转换字符串→整数的明显原始问题,请参阅strtol
和family。