64位上的int vs size_t

将代码从32位移植到64位。 有很多地方

int len = strlen(pstr); 

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

 size_t len = strlen(pstr); 

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

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

作为妥协,您可以使用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 。 如果确实如此,重写或冒险从不会遇到一个荒谬的巨大的对象。 原始代码几乎肯定会在32位平台上做错了,如果由于strlen返回大于INT_MAX的值而导致len为负数。

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

始终使用适当的C ++整数类型

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

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

 #include  #include  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位开发 ,你可以在这里找到更多关于这个主题的文章。

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

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

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

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

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

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

 auto len = strlen(pstr);