C++ 为什么64位GCC在分配数组时警告将常量int转换为长无符号int?

C++ 为什么64位GCC在分配数组时警告将常量int转换为长无符号int?,c++,arrays,gcc,64-bit,C++,Arrays,Gcc,64 Bit,我有一个文件test.cpp,如下所示: void f(const int n) { unsigned char *a=new unsigned char[n]; delete[] a; } int main() { f(4); return 0; } 使用-Wsign conversion标志在64位GCC中编译它会产生警告: test.cpp:2:39: warning: conversion to ‘long unsigned int’ from ‘const int’

我有一个文件test.cpp,如下所示:

void f(const int n) {
  unsigned char *a=new unsigned char[n];
  delete[] a;
}

int main() {
  f(4);
  return 0;
}
使用
-Wsign conversion
标志在64位GCC中编译它会产生警告:

test.cpp:2:39: warning: conversion to ‘long unsigned int’ from ‘const int’ may change the sign of the result [-Wsign-conversion]
(第2行是调用
new
的行)。在我看来,GCC在分配数组时发出这样的警告似乎很奇怪,但以下事情更奇怪:

  • unsigned char*a=新的unsigned char[(long unsigned int)n]替换有问题的行不会消除警告,使用
    static\u cast()
    也不会消除警告
  • 如果使用签名
    void f(tn)
    定义了
    f
    ,其中
    T

  • 任何大小的非常量、有符号或无符号整数类型,或
  • 有符号的64位整数类型
  • 但是,当
    T
    是任何小于64位的常量有符号整数类型时,它会产生警告

    请记住,我在一台64位(Linux)机器上,为什么符号转换警告在这种情况下关心
    n
    的常量和大小,为什么类型转换不能解决这个问题

    注1:我想在另一个编译器下测试这一点,但Comeau网站已经关闭,我没有访问任何其他编译器的权限,因此我无法判断这是符合标准的行为,还是GCC错误

    注释2:Test.CPP是一个从一个“真实”C++文件中的问题的最小例子,在其中我最好的方法是用“

    ”包围攻击线。
    const int是一个有符号值,您将有符号值强制转换为无符号值。
    因此,编译器正在生成一个警告,在某些情况下,这种转换可能会导致错误的计算。

    好吧,它为您转换,但它警告您,它正在进行的转换将改变它正在改变的值的符号,否则您可能会自动丢失一些值,错误可能会导致它只是说您是 将int强制转换为无符号int这通常可能会更改符号的值,如果我将int传递给数组边界运算符,它不会像您这样发出警告


    试着转动你打开的转换标志,看看它是否仍然这样做。该标志是导致警告的原因,因为它是进行转换的东西。我想,这与处理整数文本的方式有关:当你写

    int myArray[10];
    

    数字10被转换成有符号整数,编译器不应该因为明显的原因而抱怨这个数字。所以,我想,有符号整数的类型检查中会出现异常。当使用const int时,GCC似乎认为这是一个不适用整数异常的不同类型,因此警告:

    TL;DR:如果它代表某个东西的大小,那么将其设置为
    std::size\u t


    问题是数组
    new
    将类型为
    std::size\u t
    的值作为其大小参数,该值由标准保证为无符号整数类型,编译器抱怨将其转换为
    长无符号int
    就证明了这一点。这就是有符号-无符号转换发生的地方,(IMO)解决该问题的正确方法就是简单地给出函数
    f
    type
    std::size\u t
    的参数
    n
    。这至少在用于x86_64 GNU/Linux的GCC 4.6中会抑制警告。

    编译器会发出警告,因为在转换为无符号值时符号可能会更改

    一,。用无符号字符替换有问题的行*a=新的无符号字符[(长无符号整数)n];不会消除警告,也不会使用static_cast()

    符号转换的问题仍然存在,您只是将其显式化了。我的猜测是,它仍然不够明确,编译器不会相信你。它仍然相信您声明
    n
    a signed
    const int
    是有原因的

    二,。如果用签名void f(tn)定义f,则不会产生警告,其中T是

    一,。任何大小的非常量、有符号或无符号整数类型

    如果
    n
    为非常量,则函数开头和转换之间可能存在代码,这确保了n为正,如
    n=(长无符号int)(n)。在这种情况下,编译器似乎给了您怀疑的好处,因此没有发出警告。当它被声明为const时,编译器肯定知道它正在处理一个
    int
    ,并发出警告


    我承认,我的解释听起来不像g++通常会做的事情。

    问题在于函数的签名-当您将常量literal 4传递给一个声明为常量int的函数时,编译器会进行隐式转换


    您可以尝试将参数类型替换为const unsigned int,以消除警告消息。

    我对细节有点不太了解,但在我看来,问题实际上在于符号扩展(因为在您的系统中,size\u t很可能是一个无符号长字符)。考虑下面的代码:

    #include <stdio.h>
    void f(const int n) {
          unsigned long int b = static_cast<long unsigned int>(n);
          printf ("0x%llx == %lld\n", b, b);
    }
    
    int main() {
          unsigned long int c = 1;
          c <<= 31;
          printf ("0x%llx == %lld \n", c, c);
          f(c);
          return 0;
    }
    
    请注意,我特意选择了一个表示负数的值作为int,而不是long。第一次打印实际上应该是0x0000000080000000。如果我选择一个简单的-1,它仍然会得到符号扩展到-1的长度,但这一个给出了完全不同的东西

    当然,如果您显式地将其强制转换为未签名的,您也会得到一些其他的结果,但我的猜测是编译器更担心隐式转换(在本例中是向上转换为64位),您更可能会错过这些转换(“更多位可能会出什么问题?”)

    另一个提示是,困扰我们的是上转换(如果不是,我很高兴听到另一个解释):

    所以我们排除了函数参数传递应该受到责备的选项

    现在,如果我可以的话
    #include <stdio.h>
    void f(const int n) {
          unsigned long int b = static_cast<long unsigned int>(n);
          printf ("0x%llx == %lld\n", b, b);
    }
    
    int main() {
          unsigned long int c = 1;
          c <<= 31;
          printf ("0x%llx == %lld \n", c, c);
          f(c);
          return 0;
    }
    
    0x80000000 == 2147483648
    0xffffffff80000000 == -2147483648
    
    int main() {
        int n = 1;
        const long int a = static_cast<long int> (n);
        const int b = static_cast<int> (n);
        char* ap = new char [a];
        char* bp = new char [b];
    }
    
    test.cpp: In function ?int main()?:
    test.cpp:8:27: warning: conversion to ?long unsigned int? from ?const int? may change the sign of the result [-Wsign-conversion]
         char* bp = new char [b];