我需要将strtol的结果转换为int吗?

4

以下代码在使用g++ 4.1.1和-Wall参数时不会产生警告。

int octalStrToInt(const std::string& s)
{    
    return strtol(s.c_str(), 0, 8);
}

我原本期望会收到一个警告,因为 strtol 返回的是 long int,但我的函数只返回了一个普通的 int。其他编译器是否会在这里发出警告?在这种情况下,我应该将返回值强制转换为 int 以符合良好的实践吗?


其他编译器可能会有,比如MSVC在缩小整数类型时会出现警告C4244。不过我不知道它是否适用于long -> int,因为它们的大小相同(至少对于这个千年版本的Windows来说是这样)。不过看到一个调用strtol而没有至少3行错误检查代码还是很奇怪的,所以我个人会像Let_Me_Be建议的那样添加一个范围检查,或者定义一个范围检查转换函数。 - Steve Jessop
出于简化起见,我省略了所有额外的内容。 我只对是否需要/建议进行强制转换感兴趣。 - Michael Kristofik
1
啊哈,使用足够新的GCC和-Wconversion编译例如linux/x64时,我会期望出现警告。 4.1.1是否足够新,我不知道。 -Wconversion不在-Wall或-Wextra中,因为你会得到很多错误警告。 - Steve Jessop
5个回答

4

最佳方法是:

long x = strtol(...); assert(x <= INT_MAX); return (int)x;

您需要使用 limits.hassert.h 库。


8
也要检查负极。 - Steve Jessop

2
如果您无法访问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()。留给读者的练习。
使用assert还是其他错误处理取决于您实际想要处理无效输入的方式。
还有boost::lexical_cast(我不知道如何将其读取为八进制),以及stringstream。读取您想要的类型,而不是恰好具有C库函数的类型。

在这种情况下使用assert感觉有些不对。在我看来,assert是用于捕获程序员的错误(例如调用者不应该传递一个空指针),但是在这里错误取决于您提供的运行时数据。这意味着,调用者应该首先检查字符串的内容是否适合此函数,这使得它有点无用 - 现在我需要另一个函数来测试转换是否成功。 - UncleBens
可以,但是在示例代码中,我倾向于使用“assert”,除非有特定原因要使用其他方式。或者,我会写成“if(bad_thing)panic();”,并留给读者实现“panic()”。在现实生活中,我同意你的看法,这个函数应该有一种指示失败的方法,可以通过引发异常来实现。由于它没有,而且我不愿意介绍异常,所以我使用assert来表示“这里有一个错误情况,请处理它。” - Steve Jessop
好的,但我仍然认为这种做法是误导性的。(例如,你能告诉我 Let_Me_Be 是否有类似示例的约定,或者他是否真诚地推荐 assert 作为处理不良运行时数据的最佳方式吗?) - UncleBens
我做不到,但我不需要能够告诉,因为我不评分他的代码 ;-) Kristo在评论中说,真正的代码中的错误检查已被移除,以便专注于转换/强制转换。如果我们担心人们会盲目复制代码,那么我认为将空指针传递给strtol比assert更大的问题。对于不可靠的输入数据,两者都不是适当的处理方式,假设Kristo的输入确实是不可靠的。 - Steve Jessop

1
您可能需要-Wconversion标志来打开这些警告。但它不会警告long -> int,因为在GCC中它们的大小相同(转换不会改变值)。但如果您将例如long -> short进行转换,则会发出警告。
我想只是做一个强制类型转换可能不建议,因为那只会掩盖错误的可能性。在您检查了这样的强制类型转换不会修改值以便迎合编译器之后,这样做也没问题。

1

你在这里看不到任何警告,因为你的平台上“int”和“long int”数据类型具有相同的大小和范围。根据架构,它们可能会变得不同。

为了保护自己免受奇怪的错误,使用范围检查。我建议您使用std::numeric_limits::min/max(请参见)。

经过范围检查后,您可以安全地使用static_cast或c-style cast。

另一方面,您可以依赖于std::stringstream类中实现的相同功能。使用std::stringstream进行转换将默认情况下是平台安全和类型安全的。


如果没有指定-Wconversion,即使它们不同,也不会看到任何警告。在32位x86上从long转换为short,在4.3.2上确实会收到警告,但在3.4.4上不会,不确定更改是何时出现的。 - Steve Jessop
不确定-Wconversion,似乎会生成太多与潜在危险类型截断无关的信息。在4.3之后,引入了-Wp64,此标志将确保检测到类型截断long int->int。 - user291562
不错,我不知道这个。我并不是说提问者一定要使用-Wconversion,只是当你说,“你在这里看不到任何警告,因为X”,这不仅仅是因为X,也是因为提问者没有打开那个警告... - Steve Jessop

0

现代大多数编译器都会在警告级别配置的情况下提醒有关转换和可能的截断。在MSVC中,这是警告级别4。

最佳实践是从函数返回长整型,让调用代码决定如何处理转换。除此之外,至少要确保您从strtol返回的值适合于int,如Let_Me_Be所建议的那样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接