为什么唯一有符号整数类型会在clang中导致溢出警告

时间:2014-10-11 08:06:06

标签: c++ integer compiler-warnings

以下最简单的代码示例会导致一个警告:

#include <iostream>
#include <climits>

int b = INT_MAX + 1; //warning: overflow in expression; result is -2147483648 
                     //with type 'int' [-Winteger-overflow]

unsigned int a = INT_MAX + 1; //OK
int main()
{
    std::cout << b << std::endl << a;
}

DEMO

为什么第二行溢出不会引起任何警告?这是一个错误,还是我不明白?

1 个答案:

答案 0 :(得分:2)

这是一个错误,虽然结果是“正确的”。据我所知,似乎操作实际上在添加操作之前变成了无符号。

警告是由此代码生成的,显然会检查unsigned并跳过溢出检查(明确指出并在评论中说明):

/// Perform the given integer operation, which is known to need at most BitWidth
/// bits, and check for overflow in the original type (if that type was not an
/// unsigned type).
template<typename Operation>
static APSInt CheckedIntArithmetic(EvalInfo &Info, const Expr *E,
                                   const APSInt &LHS, const APSInt &RHS,
                                   unsigned BitWidth, Operation Op) {
  if (LHS.isUnsigned())
    return Op(LHS, RHS);

  APSInt Value(Op(LHS.extend(BitWidth), RHS.extend(BitWidth)), false);
  APSInt Result = Value.trunc(LHS.getBitWidth());
  if (Result.extend(BitWidth) != Value) {
    if (Info.checkingForOverflow())
      Info.Ctx.getDiagnostics().Report(E->getExprLoc(),
        diag::warn_integer_constant_overflow)
          << Result.toString(10) << E->getType();
    else
      HandleOverflow(Info, E, Value, E->getType());
  }
  return Result;
}

我们可以看到,unsigned没有发出警告:

  if (LHS.isUnsigned())
    return Op(LHS, RHS);

删除程序(删除#includes,从相关标头复制#define INT_MAX ...),并使用clang++ -Xclang -ast-dump ...运行clang,会得到以下输出:

TranslationUnitDecl 0x4ca2830 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x4ca2d70 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
|-TypedefDecl 0x4ca2dd0 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
|-TypedefDecl 0x4ca3190 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]'
|-VarDecl 0x4ca31f0 <of.cpp:3:1, col:19> col:5 b 'int' cinit
| `-BinaryOperator 0x4ca3288 <line:1:19, line:3:19> 'int' '+'
|   |-IntegerLiteral 0x4ca3248 <line:1:19> 'int' 2147483647
|   `-IntegerLiteral 0x4ca3268 <line:3:19> 'int' 1
|-VarDecl 0x4ce2280 <line:6:1, col:28> col:14 a 'unsigned int' cinit
| `-ImplicitCastExpr 0x4ce2340 <line:1:19, line:6:28> 'unsigned int' <IntegralCast>
|   `-BinaryOperator 0x4ce2318 <line:1:19, line:6:28> 'int' '+'
|     |-IntegerLiteral 0x4ce22d8 <line:1:19> 'int' 2147483647
|     `-IntegerLiteral 0x4ce22f8 <line:6:28> 'int' 1
`-FunctionDecl 0x4ce23b0 <line:7:1, line:10:1> line:7:5 main 'int (void)'
  `-CompoundStmt 0x4ce24a8 <line:8:1, line:10:1>

我们可以清楚地看到它是整数:

|   `-BinaryOperator 0x4ce2318 <line:1:19, line:6:28> 'int' '+'
|     |-IntegerLiteral 0x4ce22d8 <line:1:19> 'int' 2147483647
|     `-IntegerLiteral 0x4ce22f8 <line:6:28> 'int' 1

因此,编译器必须首先转换为无符号,然后应用+。铸造意味着:

| `-ImplicitCastExpr 0x4ce2340 <line:1:19, line:6:28> 'unsigned int' <IntegralCast>

我将再看一看,看看我是否能找出实际出错的地方。但那将需要等待一段时间......