5

我最近在我的代码中发现了一个错误,我花了几个小时来调试。

问题出在定义为的函数中:

unsigned int foo(unsigned int i){
   long int v[]={i-1,i,i+1} ;
       .
       .
       .
 return x ; // evaluated by the function but not essential how for this problem.
}

v 的定义在我的开发机器(ubuntu 12.04 32 位,g++ 编译器)上没有引起任何问题,其中 unsigned int 被隐式转换为 long int,因此正确处理了负值。

在另一台机器上(ubuntu 12.04 64 位,g++ 编译器)但是这个操作并不安全。当 i=0 时,v[0] 没有设置为 -1,而是设置为一些奇怪的大值(因为它经常发生在试图使 unsigned int 为负数时)。

我可以解决将 i 的值转换为 long int 的问题

long int v[]={(long int) i - 1, (long int) i, (long int) i + 1};

一切正常(在两台机器上)。

我无法弄清楚为什么第一个在机器上工作正常而在另一个机器上不起作用。

你能帮我理解这一点,以便我将来避免这个或其他问题吗?

4

2 回答 2

6

对于unsigned值,加法/减法被明确定义为模算术,因此0U-1可以计算为std::numeric_limits<unsigned>::max().

从无符号转换为有符号时,如果目标类型足够大以容纳无符号值的所有值,那么它只需将数据直接复制到目标类型中。如果目标类型不足以容纳所有无符号值,我相信它是实现定义的(将尝试找到标准参考)。

因此,什么时候long是 64 位(大概是 64 位机器上的情况),无符号适合并直接复制。

long32 位机器上是 32 位时,它很可能只是将位模式解释为有符号值,在这种情况下为 -1。

编辑:避免这些问题的最简单方法是避免混合有符号和无符号类型。从一个概念不允许负数的值中减去一个是什么意思?我要争辩说,函数参数在你的例子中应该是一个有符号的值。

也就是说,g++(至少 4.5 版)提供了一个方便的方法-Wsign-conversion,可以在您的特定代码中检测到这个问题。

于 2012-09-18T17:42:32.270 回答
0

您还可以使用专门的演员来捕捉所有溢出演员:

template<typename O, typename I>
O architecture_cast(I x) {
/* make sure I is an unsigned type. It  */
static_assert(std::is_unsigned<I>::value, "Input value to architecture_cast has to be unsigned");

assert(x <= static_cast<typename std::make_unsigned<O>::type>( std::numeric_limits<O>::max() ));

return static_cast<O>(x);
}

使用它会在调试中捕获比结果类型可以容纳的更大数字的所有强制转换。这包括您的 unsigned int 为 0 并减去 -1 导致最大的 unsigned int 的情况。

于 2016-04-08T16:31:35.433 回答