为什么64位GCC警告在分配数组时将const int转换为long unsigned int?

时间:2013-04-09 15:31:10

标签: 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’ may change the sign of the result [-Wsign-conversion]

(第2行是调用new的行)。对我来说,GCC应该给出关于分配数组的警告似乎很奇怪,但以下事情甚至更奇怪:

  1. 使用unsigned char *a=new unsigned char[(long unsigned int)n];替换违规行并不会消除警告,也不会使用static_cast<long unsigned int>()
  2. 如果使用签名f定义void f(T n)T

    ,则不会产生任何警告
    1. 任何大小的任何非常量,有符号或无符号整数类型,或
    2. 带符号的64位整数类型。
    3. T是任何小于64位的常量整数类型时,它会产生警告。

      请记住,我在64位(Linux)计算机上,为什么在这种情况下,符号转换警告会关注n的常量和大小,以及为什么不输入类型解决问题?

      注1:我想在另一个编译器下测试它,但是Comeau站点已关闭,我无法访问任何其他编译器,所以我无法判断这是否符合标准,或GCC错误。

      注意2:test.cpp是我所拥有的“真实”C ++文件中的一个问题的最小示例,其中我摆脱警告的最佳方式是围绕违规行:

      #pragma GCC diagnostic push
      #pragma GCC diagnostic ignored "-Wsign-conversion"
      // ...
      #pragma GCC diagnostic pop
      

8 个答案:

答案 0 :(得分:1)

我对细节有点不稳定,但在我看来,问题实际上是签名扩展(因为size_t很可能是系统上的unsigned long)。请考虑以下代码:

#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,但不是长。第一个打印实际上应该是0x0000000080000000。如果我选择了一个简单的-1,它仍然会被符号扩展为-1,但是这个给出了完全不同的东西。

当然如果你明确地将它转换为unsigned,你也会得到别的东西,但我的猜测是编译器更担心隐式转换(在这种情况下向上转换为64位)你更有可能错过(“更多位可能出错?”)

另一个提示是,这是一个困扰我们的upconvert(如果没有,我会很高兴听到另一个解释):

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];
}

这只是抱怨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];

所以我们排除了函数参数传递的责任

现在,如果我可以进一步推测,也许这个警告特定于内存分配(注意你不能简单地将n转换为long),因为编译器本来想要给定一个常量的分配魔法尺寸。想象一下当它看到你想要做的事情时所得到的震撼。

答案 1 :(得分:0)

const int是有符号值,您将有符号值转换为无符号值。 因此,编译器会生成警告,在某些情况下,此转换可能会导致错误的计算。

答案 2 :(得分:0)

它转换为你,但它警告你它正在进行的转换将改变它正在改变的值的符号否则你可能会自动失去一些价值和错误可能会重新说它只是说你是  将int转换为unsigned int这可能会改变符号的值,如果我将int传递给数组边界运算符,它就不会像你喜欢的那样发出警告

尝试转换你所拥有的转换标志并查看它是否仍然执行此标志是什么导致警告,因为它是进行转换的事情

答案 3 :(得分:0)

我想,这将与处理整数文字的方式有关:当你写

int myArray[10];

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

答案 4 :(得分:0)

TL; DR:如果它代表某事物的大小,则将其设为std::size_t

问题是数组new将其大小参数作为类型std::size_t的值,标准保证它是无符号整数类型,编译器转换为转换为long unsigned int。这就是签名无符号转换发生的地方,而(IMO)解决问题的正确方法就是给出函数n类型f的参数std::size_t。这至少使用GCC 4.6 for x86_64 GNU / Linux来禁止警告。

答案 5 :(得分:0)

编译器发出警告,因为转换为无符号值时符号可能会发生变化。

  

1。用unsigned char替换违规行* a = new unsigned char [(long unsigned int)n];没有摆脱警告,也没有使用static_cast()。

标志转换的问题仍然存在,你只是明确了。我的猜测是,编译器相信你还不够明确。它仍然认为您出于某种原因宣布n 已签署 const int

  

2。如果使用签名void f(T n)定义f,则不会产生警告,其中T为

     
    

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

  

如果n是非const,那么可能是函数开头和转换之间的代码,它确保n是正数,如n = (long unsigned int)(n);。在这种情况下,编译器似乎给你带来了疑问,因此不会发出警告。当 声明为const时,编译器确定它正在处理int并发出警告。

我承认,我的解释听起来并不像g ++通常会做的那样。

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

:s通常用于数组索引和循环计数。使用其他类型(例如unsigned int)进行数组索引的程序可能会失败,例如索引超过std::size_t或依赖于32位模块化算术时的64位系统。 click here to read more about std::size_t and 64-bit systems