Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does 64-bit GCC warn about converting a const int to long unsigned int when allocating an array?

I have a file test.cpp that looks like this:

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

int main() {
  f(4);
  return 0;
}

Compiling it in 64-bit GCC with the -Wsign-conversion flag produces the warning:

test.cpp:2:39: warning: conversion to ‘long unsigned int’ from ‘const int’ may change the sign of the result [-Wsign-conversion]

(line 2 is the line in which new is called). It seems strange to me that GCC should give this warning about allocating an array, but the following things are even stranger:

  1. Replacing the offending line with unsigned char *a=new unsigned char[(long unsigned int)n]; does not get rid of the warning, nor does using static_cast<long unsigned int>().
  2. No warning is produced if f is defined with the signature void f(T n), where T is

    1. any non-const, signed or unsigned integer type of any size, or
    2. a signed 64-bit integer type.

    It does however produce warnings when T is any const signed integer type smaller than 64-bits.

Bearing in mind that I'm on a 64-bit (Linux) machine, why does the sign-conversion warning care about the constness and size of n in this case, and why doesn't type casting fix the problem?

Note 1: I wanted to test this under another compiler, but the Comeau site is down, and I don't have access to any other compilers, so I can't tell if this is standard-compliant behaviour, or a GCC bug.

Note 2: test.cpp is a minimal example of a problem from a "real" C++ file that I have in which the best way for me to get rid of the warning was to surround the offending line with:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
// ...
#pragma GCC diagnostic pop
like image 217
Ose Avatar asked Apr 09 '13 15:04

Ose


People also ask

Is unsigned long long 64 bit?

Some properties of the unsigned long long int data type are: An unsigned data type stores only positive values. It takes a size of 64 bits. A maximum integer value that can be stored in an unsigned long long int data type is 18, 446, 744, 073, 709, 551, 615, around 264 – 1(but is compiler dependent).

What is the difference between unsigned long and unsigned long long?

unsigned long is required to be at least 32 bits. unsigned long long is required to be at least 64 bits. (Actually the requirements are stated in terms of the ranges of values they can represent.) As you've seen, this is consistent with them both being the same size, as long as that size is at least 64 bits.

What is unsigned const?

Unsigned integer constant is an integer constant which has the permissible range from 0 to 65536. Thus significance of declaring a constant as unsigned almost doubles the size of the largest possible value.

How many bits is unsigned long C++?

Unsigned long variables are extended size variables for number storage, and store 32 bits (4 bytes).


1 Answers

I'm a little flaky on the details, but it seems to me that the issue is actually in sign extension (since size_t is most likely an unsigned long on your system). Consider the following code:

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

it prints:

0x80000000 == 2147483648
0xffffffff80000000 == -2147483648

Note that I intentionally picked a value that means a negative as an int, but not as a long. The first print should actually be 0x0000000080000000. If I picked a simple -1, it would still get sign-extended to long as a -1, but this one gives something completely different.

Of course if you explicitly cast it to an unsigned you also get something else, but my guess is that the compiler is more worried about the implicit conversions (the up-convert to 64bit in this case) that you're more likely to miss ("what could possibly go wrong with more bits?")

Another hint it's the upconvert that's bugging us (if not, i'll be happy to hear another explanation):

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

This complains only on 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];

so we've ruled out the option that function arguments passing is to blame

Now, if I may further speculate, perhaps that warning is specific to memory allocations (note that you don't get it from simply casting n to long), because the compiler would have wanted to do some allocation magic given a constant size. Imagine the shock it got when it saw what you're trying to do.

like image 182
Leeor Avatar answered Sep 21 '22 02:09

Leeor