【问题标题】:-Wsign-compare warning in g++-Wsign-compare g++ 中的警告
【发布时间】:2013-05-25 22:26:47
【问题描述】:

我有一个使用 64 位整数比较的代码。它看起来类似于以下内容:

#include <cstdio>

long long getResult()
{
    return 123456LL;
}

int main()
{
    long long result = getResult();

    if (result > 0x000FFFFFFFFFFFFFLL
        || result < 0xFFF0000000000000LL)
    {
        printf("Something is wrong.\n");

        if (result > 0x000FFFFFFFFFFFFFLL
            || result < -4503599627370496LL)
        {
            printf("Additional check failed too.\n");
        }
        else
        {
            printf("Additional check went fine.\n");
        }
    }
    else
    {
        printf("Everything is fine.\n");
    }

    return 0;
}

当此代码在 g++ 中编译时(在 Ubuntu 12.04 x64 上尝试了不同版本:4.6.3、4.6.4、4.7.3、4.8.0),带有标志 -Wall -pedantic -std=c++0x 测试。 cpp -o test 我收到 -Wsign-compare 第一个 if 语句第二行的警告(从 g++-4.8 输出):

test.cpp:13:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
 || result < 0xFFF0000000000000LL)
             ^

当测试程序运行时,我得到两行文本:

Something is wrong.
Additional check went fine.

使用 MS Visual Studio 11 Express Update 2 在 Windows 上编译相同的代码时,使用 x86 或 x64 架构的默认项目选项,我既没有收到警告也没有收到此输出,而是输出:

Everything is fine.

是不是代码有问题?如果是的话,你能指出来吗?还是使用的编译器有问题?

在第一个 if 语句中为第二个常量添加额外的类型转换会删除 g++ 中的警告。

【问题讨论】:

  • 这是一个很好的第一个问题,包括一个完整的示例和所有相关信息。
  • 0xFFF0000000000000,作为正值,不适合 long long。但是,它确实适合 unsigned long long,所以这是 gcc 使用的类型。
  • 后缀LL 可用于强制编译器选择更长的类型(1LL 是 long long),但不能选择更小的类型,您需要为此进行强制转换(@ 987654328@(严格来说这是实现定义的))。

标签: c++ c++11 g++


【解决方案1】:

根据标准中的 [lex.icon],十六进制文字 0xFFF0000000000000LL 的类型为 unsigned long long,因为该值不适合 long long(请参阅 Unsigned hexadecimal constant in C?C interpretation of hexadecimal long integer literal "L" 了解更多信息这方面的信息。)

这意味着 G++ 的警告是正确的,您将 long long resultunsigned long long 文字进行比较。

显然作为无符号值,123456LL小于0xFFF0000000000000LL,所以G++的结果也是正确的。

MSVC 似乎有一个错误[编辑:或出于兼容性原因表现不同,请参阅 cmets],因为此断言失败:

static_assert(0xFFF0000000000000LL > 0, "big number is big");

MSVC 给出了文字 0xFFF0000000000000LL 类型 long long,正如 MSVC 接受的这个无效代码所示:

auto i = 0xFFF0000000000000LL;
long long& l = i;

一个 C++03 的例子应该可以正确编译:

template<typename T>
void f(T t)
{
    unsigned long long& l = t;
}

int main()
{
    f(0xFFF0000000000000LL);
}

GCC、Clang、Intel 和 Solaris CC 都正确地理解了这个示例,而 VC++ 则错误。

【讨论】:

  • +1 使用static_assert 和自动类型推断来验证错误的好方法:)
  • VC++ 是否支持 long long 作为 C++11 功能或作为 C++03 扩展? long long 在 C++11 之前不是标准 C++ 的一部分,是吗?
  • 在 clang 的 -fms-extensions 标志下 0xFFF0000000000000LL 被签名。我相信这是因为,正如@hvd 所指出的,VC++ 在 C++11 之前有 long long,并且这种行为是兼容性所必需的。
  • @hvd,问题的标签为c++11c++ 只是后来添加的),VC++ 默认为 C++11,不是吗?几年前,所有其他编译器也支持long long(和unsigned long long!),C++11 规则直接来自 C99。虽然我不知道 Clang 使用该标志改变的行为,所以这显然是 VC++ 的已知/预期行为,而不仅仅是一个错误
  • @JonathanWakely 我不是在问这个问题,而是在问实施。我怀疑 VC++ 使用了符合 C++03 的扩展,而不是 C++11 功能的错误实现。如果您说 VC++ 默认为 C++11(我真的不知道),而不是 C++03 加扩展,那回答了我的问题,谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-25
  • 2014-12-15
  • 2019-05-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多