以下代码在使用g++ 4.1.1和-Wall
参数时不会产生警告。
int octalStrToInt(const std::string& s)
{
return strtol(s.c_str(), 0, 8);
}
我原本期望会收到一个警告,因为 strtol
返回的是 long int
,但我的函数只返回了一个普通的 int
。其他编译器是否会在这里发出警告?在这种情况下,我应该将返回值强制转换为 int 以符合良好的实践吗?
最佳方法是:
long x = strtol(...); assert(x <= INT_MAX); return (int)x;
您需要使用 limits.h
和 assert.h
库。
boost::numeric_cast
,您可以编写一个简单的模拟器:template <typename T, typename S>
T range_check(const S &s) {
assert(s <= std::numeric_limits<T>::max());
assert(s >= std::numeric_limits<T>::min());
return static_cast<T>(s); // explicit conversion, no warnings.
}
return range_check<int>(strtol(some_value,0,8));
min()
是它们的相同边界,但对于浮点类型,您需要检查+/- max()
。留给读者的练习。boost::lexical_cast
(我不知道如何将其读取为八进制),以及stringstream。读取您想要的类型,而不是恰好具有C库函数的类型。assert
感觉有些不对。在我看来,assert
是用于捕获程序员的错误(例如调用者不应该传递一个空指针),但是在这里错误取决于您提供的运行时数据。这意味着,调用者应该首先检查字符串的内容是否适合此函数,这使得它有点无用 - 现在我需要另一个函数来测试转换是否成功。 - UncleBensassert
更大的问题。对于不可靠的输入数据,两者都不是适当的处理方式,假设Kristo的输入确实是不可靠的。 - Steve Jessop你在这里看不到任何警告,因为你的平台上“int”和“long int”数据类型具有相同的大小和范围。根据架构,它们可能会变得不同。
为了保护自己免受奇怪的错误,使用范围检查。我建议您使用std::numeric_limits::min/max(请参见)。
经过范围检查后,您可以安全地使用static_cast或c-style cast。
另一方面,您可以依赖于std::stringstream类中实现的相同功能。使用std::stringstream进行转换将默认情况下是平台安全和类型安全的。
现代大多数编译器都会在警告级别配置的情况下提醒有关转换和可能的截断。在MSVC中,这是警告级别4。
最佳实践是从函数返回长整型,让调用代码决定如何处理转换。除此之外,至少要确保您从strtol返回的值适合于int,如Let_Me_Be所建议的那样。
long -> int
,因为它们的大小相同(至少对于这个千年版本的Windows来说是这样)。不过看到一个调用strtol
而没有至少3行错误检查代码还是很奇怪的,所以我个人会像Let_Me_Be建议的那样添加一个范围检查,或者定义一个范围检查转换函数。 - Steve Jessop