8

我有一个文件 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 位整数类型。

    但是,当T任何 const 有符号整数类型小于 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
4

8 回答 8

1

我对细节有点怀疑,但在我看来,问题实际上在于符号扩展(因为 size_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;
}

它打印:

0x80000000 == 2147483648
0xffffffff80000000 == -2147483648

请注意,我故意选择了一个表示负数的值作为整数,而不是长整数。第一次打印实际上应该是 0x0000000080000000。如果我选择一个简单的 -1,它仍然会被符号扩展为 -1,但这个给出了完全不同的东西。

当然,如果你明确地将它转换为无符号,你也会得到其他东西,但我的猜测是编译器更担心你更可能错过的隐式转换(在这种情况下上转换为 64 位)( “更多位可能会出现什么问题?”)

另一个提示是上转换问题困扰着我们(如果不是,我很乐意听到另一种解释):

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 得到它),因为编译器会想要在给定恒定大小的情况下做一些分配魔法。想象一下当它看到你正在尝试做的事情时的震惊。

于 2013-08-11T13:54:04.367 回答
0

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

于 2013-04-09T16:24:34.777 回答
0

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

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

于 2013-08-11T05:54:46.450 回答
0

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

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

符号转换的问题仍然存在,您只是明确表示。我的猜测是,编译器仍然不够明确,无法相信你。它仍然认为您宣布签署n是有原因的! const int

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

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

如果n是非常量,则在函数开头和转换之间可能存在代码,以确保 n 为正数,例如n = (long unsigned int)(n);. 在这种情况下,编译器似乎给了你怀疑的好处,因此不会发出警告。当它声明为 const 时,编译器肯定知道它正在处理 anint并发出警告。

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

于 2013-07-03T08:37:14.167 回答
0

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

问题是 arraynew将 type 的值作为其大小参数std::size_t,标准保证它是无符号整数类型,编译器对转换为 的抱怨证明了这一点long unsigned int。那就是发生有符号-无符号转换的地方,解决问题的(IMO)正确方法是简单地给出n函数f类型的参数std::size_t。这至少在 GCC 4.6 for x86_64 GNU/Linux 中抑制了警告。

于 2013-06-24T17:52:52.573 回答
0

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

int myArray[10];

数字 10 被转换为有符号整数,编译器不应该出于明显的原因抱怨这一点。所以,我猜,有符号整数的类型检查会出现异常。当您使用 const int 时,gcc 似乎认为这是一种不适用整数异常的不同类型,因此会发出警告。

于 2013-06-15T08:45:51.763 回答
0

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

尝试打开您打开的转换标志,看看它是否仍然这样做,该标志是导致警告的原因,因为它是进行转换的东西

于 2013-05-03T03:42:52.577 回答
0

std::size_t通常用于数组索引和循环计数。使用其他类型(例如 unsigned int)进行数组索引的程序可能会在索引超出UINT_MAX或依赖于 32 位模运算时在 64 位系统上失败。单击此处阅读有关 std::size_t 和 64 位系统的更多信息

于 2020-01-09T12:51:20.727 回答