获取两个size_t对象的区别是否安全?

时间:2016-01-06 21:04:29

标签: c++ size-t

我正在调查我的团队使用size_t vs int(或long等)的标准。我看到的最大的缺点是,两个size_t对象的区别可能会导致问题(我不确定具体的问题 - 可能是某些东西没有补充,而签名/无符号会激怒编译器)。我使用V120 VS2013编译器在C ++中编写了一个快速程序,允许我执行以下操作:

#include <iostream>

main()
{
    size_t a = 10;
    size_t b = 100;
    int result = a - b;
}

该程序导致-90,虽然这是正确的,但如果size_t恰好用于复杂的数学运算,那么会让我对类型不匹配,有符号/无符号问题或者只是简单的未定义行为感到紧张。

我的问题是,使用size_t对象进行数学运算是否安全,特别是采取差异?我正在考虑使用size_t作为索引之类的标准。我在这里看到了一些关于这个主题的有趣帖子,但它们没有解决数学问题(或者我错过了它)。

What type for subtracting 2 size_t's?

typedef for a signed type that can contain a size_t?

3 个答案:

答案 0 :(得分:15)

这不保证可以移植,但也不是UB。代码必须无错误地运行,但生成的int值是实现定义的。因此,只要您在保证所需行为的平台上工作,这很好(只要差异可以由int表示),否则,只需在任何地方使用带符号的类型(参见最后一段)

减去两个std::size_t将产生一个新的std::size_t ,其值将通过换行确定。在您的示例中,假设64位size_ta - b将等于18446744073709551526。这不适合(常用的32位)int,因此将实现定义的值分配给result

老实说,我建议不要使用无符号整数除了有点神奇之外。标准委员会的几位成员同意我的意见:https://channel9.msdn.com/Events/GoingNative/2013/Interactive-Panel-Ask-Us-Anything 9:50,42:40,1:02:50

经验法则(从上面的视频中解读Chandler Carruth):如果您可以自己计算,请使用int,否则请使用std::int64_t

除非其转化率低于int,例如如果std::size_tunsigned short。在这种情况下,结果是int,一切都会正常工作(除非int不宽于short)。然而

  1. 我不知道有任何平台可以做到这一点。
  2. 这仍然是特定于平台的,请参阅第一段。

答案 1 :(得分:5)

如果你不使用size_t,你就会被搞砸了:size_t是一种用于内存大小的类型,因此保证总是足够大那个目的。 (uintptr_t非常相似,但它既不是第一个这样的类型,也不是标准库使用的,也不是在没有包含stdint.h的情况下可用。)如果你使用{int 1}},当你的分配超过2GiB的地址空间时,你可以得到未定义的行为(如果你在int只有16位的平台上,你可以获得32kiB!),即使机器有更多的内存并且你正在执行在64位模式下。

如果您需要size_t的差异可能会变为负值,请使用已签名的变体ssize_t

答案 2 :(得分:4)

size_t类型未签名。任何两个size_t值的减法都是定义的 - 行为

然而,首先,如果从较小的值中减去较大的值,则结果是实现定义的。结果是数学值,减少到最小的正余数模SIZE_T_MAX + 1。例如,如果size_t的最大值为65535,并且减去两个size_t值的结果为-3,则结果将为65536 - 3 = 65533。在不同的编译器或具有不同的size_t,数值将不同。

其次,size_t值可能超出int类型的范围。如果是这种情况,我们会得到强制转换产生的第二个实现定义结果。在这种情况下,任何行为都可以适用;它只需要由实现记录,转换不得失败。例如,结果可以被限制在int范围内,从而产生INT_MAX。在将较宽(或相等宽度)的无符号类型转换为较窄的有符号类型时,在两个补码机器(几乎所有)上看到的常见行为是简单的位截断:从无符号值中取出足够的位以填充有符号值,包括它的符号位。

由于两个补码的工作方式,如果原始的算术正确的抽象结果本身适合int,那么转换将产生该结果。

例如,假设在二进制补码机上减去一对64位size_t值得到抽象算术值-3,它变为正值0xFFFFFFFFFFFFFFFD 。当它被强制转换为32位int时,在许多编译器中看到的两个补码机器的常见行为是将低32位作为结果int的图像: 0xFFFFFFFD。当然,这只是32位的值-3。

结果是,您的代码事实上非常便携,因为几乎所有主流机器都是基于符号扩展和转换规则的两个补充。位截断,包括有符号和无符号之间。

除非在从无符号转换为有符号时将值加宽,否则不会发生符号扩展。因此,他遇到的一个问题是intsize_t宽的罕见情况。如果16位size_t结果为65533,由于从1中减去4,则在转换为32位int时不会产生-3;它会产生65533!