3

How would you remove the warning produced by gcc -Wconversion on the following code, without an explicit (=ugly) cast:

int main()
{
  int val;
  unsigned char c1 = (val % 65536) / 256;
  unsigned char c2 = (val % 65536) % 256;
}

Is there a way to tell the compiler that obviously the cast is implicitely done during (% 65536) / 256 or % 256

For reference

$ gcc -Wconversion w.c
w.c: In function ‘main’:
w.c:4:36: warning: conversion to ‘unsigned char’ from ‘int’ may alter its value [-Wconversion]
w.c:5:36: warning: conversion to ‘unsigned char’ from ‘int’ may alter its value [-Wconversion]
malat
  • 12,152
  • 13
  • 89
  • 158
  • 2
    By the way, `unsigned char c2 = val % 256;` is enough in the second case. – FreeNickname Feb 26 '14 at 12:55
  • Whole point of this warning is to warn you, you've done a risky implicit cast, so to get rid of the warning, use an explicit cast. Still potentially risky of course, but the compiler will shrug and assume you know best. :) – Tony Hopkinson Feb 26 '14 at 12:57
  • They're all important, but: `correctness > clarity > style` – Brett Hale Feb 26 '14 at 15:17

4 Answers4

6

Using a static cast (or casting your result TO an unsigned char)

int main()
{
    int val;
    unsigned char c1 = (unsigned char)((val % 65536) / 256);
    unsigned char c2 = (unsigned char)(val % 256);
}

This will cut the warning, as it essentially tells the compiler, "Wait, I meant to do that".

If you are intending to not truly use signed VALUES, than you should start by:

unsigned int val;

instead of:

int val;

Even doing this, you will need the cast from unsigned int to unsigned char, because the compiler will ultimately still think that you didn't mean to down-convert the way that you did.

trumpetlicks
  • 7,033
  • 2
  • 19
  • 33
  • 2
    "without an ugly cast:" – janisz Feb 26 '14 at 12:55
  • @janisz "without an ugly cast" is going to be uglier than with an "ugly" cast. It's what is needed to unobfuscate this change of type to the informed reader, and the compiler. – rubenvb Feb 26 '14 at 13:08
  • 3
    @janisz - I am open to suggestions as to how you believe this would be done without a cast at SOME point in time. The fact of the matter is that C is a fairly strongly typed language, and therefor if you are going to move from a higher bit-width numeric to a smaller one, a cast is going to be needed. – trumpetlicks Feb 26 '14 at 13:12
  • @rubenvb I agree that it could be less readable byt question is how to do it without casting – janisz Feb 26 '14 at 14:24
  • @janisz - I also see your point here, but I think the "without cast" was added after I answered :-) In which case, Kiwi's answer is probably most correct, and using the "-Wno-sign-conversion" compiler switch. – trumpetlicks Feb 26 '14 at 14:25
  • Oh, I see. Changing requirements comes to SO :) – janisz Feb 26 '14 at 14:41
  • @janisz - I suppose LOL :-) As I say, Keep having FUN! – trumpetlicks Feb 26 '14 at 14:42
4

Sounds like you want these results :

c1 = (val >> 8) & 0xff;
c2 = val & 0xff;

Edit :

From gcc wiki : Wconversion

Warn for implicit conversions that may alter a value. This includes conversions between real and integer, like abs (x) when x is double; conversions between signed and unsigned, like unsigned ui = -1; and conversions to smaller types, like sqrtf (M_PI). Do not warn for explicit casts like abs ((int) x) and ui = (unsigned) -1, or if the value is not changed by the conversion like in abs (2.0). Warnings about conversions between signed and unsigned integers can be disabled by using -Wno-sign-conversion. For C++, also warn for conversions between NULL and non-pointer types; confusing overload resolution for user-defined conversions; and conversions that will never use a type conversion operator: conversions to void, the same type, a base class or a reference to them. Warnings about conversions between signed and unsigned integers are disabled by default in C++ unless -Wsign-conversion is explicitly enabled.

The warning won't happen if val is unsigned but you have to be sure about what you want to get for negative values.

Kiwi
  • 2,698
  • 16
  • 15
  • This will mathematically give the same result, Im not sure however that it will by itself get rid of the warnings. Because val is still an int, the compiler will treat the constants 8, and 0xff as int value, not unsigned chars. It will always default to the largest bit width type in these cases, not the smallest. – trumpetlicks Feb 26 '14 at 13:08
  • indeed I'll make sure to use unsigned int – malat Feb 26 '14 at 13:50
  • 2
    You might want to read your own post and reference, it actually DOES state that a warning will be thrown "and conversions to smaller types". I still fail to recognize how this answers the OPs question, your code will still throw a warning. Simply changing it to unsigned still doesn't do away with the conversion from a 32 bit value down to an 8 bit value. – trumpetlicks Feb 26 '14 at 14:05
  • @trumpetlicks I tried and didn't get the warning, that's the whole point of masking with `0xff` – Kiwi Feb 26 '14 at 14:10
  • @Kiwi - I see your point. Guess it is a compiler difference. I have worked with compilers, that indeed would throw a warning due to the size conversion. Most compilers I have worked with would look at the 0xff and extend that to be 0x000000FF because val is a 32 bit type. This is good to know about GCC – trumpetlicks Feb 26 '14 at 14:23
  • 1
    @trumpetlicks - this also suppresses a warning with `clang`, even with the very strict `-Weverything` flag. It's perfectly correct, but I still find it strange. I'd rather the certainty of a cast. This isn't calligraphy. – Brett Hale Feb 26 '14 at 14:36
0

that's because int is signed and char is not, and this can lead confusion if int is a negative value because of two's complement. just add U near your literal and add unsigned to int and you will also fix your code, as actually val goes from -32768 to 32767 (signed) and not from 0 to 65536 (unsigned)

Lesto
  • 2,260
  • 2
  • 19
  • 26
  • 1
    In C, plain `char` is either signed or unsigned, at the implementation's whim. And signed integers aren't guaranteed to be represented in two's complement either. – vonbrand Feb 26 '14 at 13:51
0

The warning is telling you that what you are doing isn't defined in all cases. If val is negative, the ´%´ might return a negative result and asigning that to an unsigned char is undefined. See also this question.

Either make sure val is never negative and live with the warning (checking each time this hasn't changed!), or declare it unsigned int.

Community
  • 1
  • 1
vonbrand
  • 11,412
  • 8
  • 32
  • 52