简体   繁体   中英

"Narrowing conversion from 'int' to 'char' inside { }" for legal values when cross compiling

I have a C++ project that I compile both using g++ on my machine (compiling to "host") and to an ARM processor using a cross compiler (in my case arm-cortex_a8-linux-gnueabi-g++ ). I am in the process of converting to C++0x/11 standart and there is an error I get when compiling initialization list, which I was able to reproduce in the following snippet:

int main(void) {
    char c[1] = {-108};
}

This program is seemingly correct as -108 is a legal value for a char . Compiling this with g++ yields no error with the following command line:

g++ example.cc -std=c++0x

However, when I compile with the cross-compiler, like so:

arm-cortex_a8-linux-gnueabi-g++ example.cc -std=c++0x

I get the following error:

example.cc: In function 'int main()':
example.cc:2:22: error: narrowing conversion of '-0x0000000000000006c' from 'int' to 'char' inside { } [-fpermissive]

Since the value is legal, this seems like a bug. Can you explain why I get this error and what to do to solve it?

Edit : note that using positive values (eg, 108 ) is legal and does not result in an error on both compilers.

When you declare a variable as char , it's implementation-dependent whether it's signed or unsigned. If you need to be able to store negative values, you should declare it signed explicitly, rather than relying on the implementation-defined default.

signed char c[1] = { -108 };

Since the value is legal

How do you know that? char s signedness is implementation defined. And if it's unsigned, your code is ill-formed by narrowing - §8.5.4/7:

A narrowing conversion is an implicit conversion
[…]
(7.4) — from an integer type […] to an integer type that cannot represent all the values of the original type , except where the source is a constant expression whose value after integral promotions will fit into the target type.

§8.5.1/2:

If the initializer-clause is an expression and a narrowing conversion (8.5.4) is required to convert the expression, the program is ill-formed.

However, if you need a signed char , use signed char .

signed char c[1] = {-108};

…is guaranteed to work.

I was in a similar situation compiling binutils in an arm cross-compiler with crosstool-ng, although my error was:

i386.cc:3165:31: error: narrowing conversion of '144' from 'int' to 'char'

Since 144 is greater than signed char range of -128 to 127, I had to change the affected lines and return of the function to unsigned char .

I also needed to use reinterpret_cast<char *> in the return of the function as the result was being used to form a string sequence. This then compiled correctly.

All 8-bit values are distinctly representable by char object (on typical modern hardware) so it should be safe(*) to assign such a value.

The compiler won't emit a warning if the literal is of char type. An escape sequence allows you to provide a hexadecimal number as a character literal:

int main(void) {
    char c[1] = {'\x94'};
}

This will compile without triggering -Wnarrowing on systems with either signed or unsigned char ( example ).

(*) Safe here means no information will be lost, although the value may not be what you expect: -108 if char is signed and 148 is char is unsigned.

This should be even better:

signed char c[] = { (signed char)(-108) };

Because in brackets the value could be treated as int by default.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM