64位上的int vs size_t

时间:2010-03-25 21:15:29

标签: c portability 32bit-64bit

将代码从32位移植到64位。

的很多地方
int len = strlen(pstr);

现在这些都会产生警告,因为strlen()返回的是size_t,它是64位,而int仍然是32位。所以我一直用

替换它们
size_t len = strlen(pstr);

但我刚才意识到这不安全,因为size_t是无符号的,可以将其视为代码签名(我实际上碰到了一个导致问题的情况,谢谢你,单元测试!)。

盲目地将strlen返回到(int)感觉很脏。或者也许不应该? 所以问题是:这有一个优雅的解决方案吗?我可能在代码库中有一千行代码;我不能手动检查它们中的每一个,测试覆盖率目前介于0.01和0.001%之间。

6 个答案:

答案 0 :(得分:7)

前段时间我在博客上发布了关于此类问题的简短说明,简短的回答是:

Always use proper C++ integer types

答案很长: 使用C ++编程时,最好使用与特定上下文相关的正确整数类型。一点点的严格总是回报。看到忽略定义为特定于标准容器的整数类型(即size_type)的趋势并不罕见。它可用于标准容器的数量,如std :: string或std :: vector。这种无知可能很容易报复。

下面是一个错误使用类型的简单示例,用于捕获std :: string :: find函数的结果。我很确定很多人会认为unsigned int没有任何问题。但是,实际上这只是一个错误。我在64位架构上运行Linux,当我按原样编译该程序时,它按预期工作。但是,当我用abc替换行1中的字符串时,它仍然有效,但不是预期的: - )

#include <iostream>
#include <string>
using namespace std;
int main()
{
  string s = "a:b:c"; // "abc" [1]
  char delim = ':';
  unsigned int pos = s.find(delim);
  if(string::npos != pos)
  {
    cout << delim << " found in " << s << endl;
  }
}

修复非常简单。只需用std :: string :: size_type替换unsigned int。如果编写此程序的人负责使用正确的类型,则可以避免这个问题。更不用说该计划将立即便携。

我已经多次看到过这种问题,特别是在前C程序员编写的代码中,他们不喜欢穿C ++类型系统强制执行和要求的严格的枪口。上面的例子很简单,但我相信它很好地解决了问题的根源。

我推荐由Andrey Karpov撰写的精彩文章64-bit development,您可以在这里找到更多有关该主题的内容。

答案 1 :(得分:5)

将编译器警告设置为最高级别可以为您提供每个错误符号转换的良好报告。在gcc中,'-Wall -Wextra'应该这样做。

您还可以使用像cppcheck这样的静态代码分析器来查看一切是否正确。

答案 2 :(得分:5)

作为妥协,您可以使用ssize_t(如果可用)。如果没有,请使用long longint_fast64_tintmax_t进行假设,或者使用平台移植标头,以便为平台指定合适的类型。 ssize_t在POSIX中不是标准C或C ++,但是如果您曾经遇到过没有与size_t相同大小的签名类型的平台,那么我会同情。

int的强制转换几乎是安全的(假设您的64位平台上有32位int,这似乎是合理的),因为字符串不可能超过2 ^ 31字节长。对更大的签名类型的强制转换甚至更安全。能够负担2 ^ 63字节内存的客户在业内被称为“一个很好的问题”; - )

当然,您可以查看它:

size_t ulen = strlen(pstr);
if (ulen > SSIZE_MAX) abort(); // preferably trace, log, return error, etc.
ssize_t len = (ssize_t) ulen;

当然有一个开销,但如果你有1000个实例,那么它们不可能都是性能关键。对于那些(如果有的话),你可以开展工作来调查被签署的len是否真正重要。如果没有,请切换到size_t。如果确实如此,重写或冒险从不会遇到一个荒谬的巨大对象。如果lenstrlen返回大于INT_MAX的值而导致{{1}}为负数,原始代码几乎肯定会在32位平台上做错了。

答案 3 :(得分:4)

您可以使用ssize_tsize_t的签名变体)。

答案 4 :(得分:1)

在大多数情况下,您可以安全地处理site_t签名。无符号size_t仅在它(或表达式中的中间结果)大于2 ^ 31(对于32位)或2 ^ 63对于64位时才被视为负。

更新: 抱歉,size_t在while ( (size_t)t >=0 )等结构中不安全。所以正确的答案是使用ssize_t

答案 5 :(得分:1)

如果您的编译器支持c ++ 0x:

auto len = strlen(pstr);