views:

352

answers:

2

I'm building my project with GCC's -Wconversion warning flag. (gcc (Debian 4.3.2-1.1) 4.3.2) on a 64bit GNU/Linux OS/Hardware. I'm finding it useful in identifying where I've mixed types or lost clarity as to which types should be used.

It's not so helpful in most of the other situations which activate it's warnings and I'm asking how am I meant to deal with these:

enum { A = 45, B, C };   /* fine */

char a = A;              /* huh? seems to not warn about A being int. */
char b = a + 1;          /* warning converting from int to char */
char c = B - 2;          /* huh? ignores this *blatant* int too.*/
char d = (a > b ? b : c) /* warning converting from int to char */

Due to the unexpected results of the above tests (cases a and c) I'm also asking for these differences to be explained also.

Edit: And is it over-engineering to cast all these with (char) to prevent the warning?

Edit2: Some extra cases (following on from above cases):

a += A;         /* warning converting from int to char */
a++;            /* ok */
a += (char)1;   /* warning converting from int to char */

Aside from that, what I'm asking is subjective and I'd like to hear how other people deal with the conversion warnings in cases like these when you consider that some developers advocate removing all warnings.

YAE:

One possible solution is to just use ints instead of chars right? Well actually, not only does it require more memory, it is slower too, as can been demonstrated by the following code. The maths expressions are just there to get the warnings when built with -Wconversion. I assumed the version using char variables would run slower than that using ints due to the conversions, but on my (64bit dual core II) system the int version is slower.

#include <stdio.h>

#ifdef USE_INT
typedef int var;
#else
typedef char var;
#endif

int main()
{
    var start = 10;
    var end = 100;
    var n = 5;
    int b = 100000000;
    while (b > 0) {
        n = (start - 5) + (n - (n % 3 ? 1 : 3));
        if (n >= end) {
            n -= (end + 7);
            n += start + 2;
        }
        b--;
    }
    return 0;
}

Pass -DUSE_INT to gcc to build the int version of the above snippet.

A: 

Maybe the compiler can already see that all the values fit into a char so it doesn't bother warning. I'd expect the enum to be resolved right at the beginning of the compilation.

Kinopiko
What about the case of the ternary operator? How does that make sense?
James Morris
I think that can be resolved by the compiler so that it just puts `d = 44`.
Kinopiko
+2  A: 

When you say /* int */ do you mean it's giving you a warning about that? I'm not seeing any warnings at all in this code with gcc 4.0.1 or 4.2.1 with -Wconversion. The compiler is converting these enums into constants. Since everything is known at compile time, there is no reason to generate a warning. The compiler can optimize out all the uncertainty (the following is Intel with 4.2.1):

    movb    $45, -1(%rbp)    # a = 45
    movzbl  -1(%rbp), %eax
    incl    %eax
    movb    %al, -2(%rbp)    # b = 45 + 1
    movb    $44, -3(%rbp)    # c = 44 (the math is done at compile time)
    movzbl  -1(%rbp), %eax   
    cmpb    -2(%rbp), %al
    jle     L2               
    movzbl  -2(%rbp), %eax
    movb    %al, -17(%rbp)
    jmp     L4
L2: 
    movzbl  -3(%rbp), %eax
    movb    %al, -17(%rbp)
L4:
    movzbl  -17(%rbp), %eax
    movb    %al, -4(%rbp)    # d = (a > b ? b : c)

This is without turning on optimizations. With optimizations, it will calculate b and d for you at compile time and hardcode their final values (if it actually needs them for anything). The point is that gcc has already worked out that there can't be a problem here because all the possible values fit in a char.

EDIT: Let me amend this somewhat. There is a possible error in the assignment of b, and the compiler will never catch it, even if it's certain. For example, if b=a+250;, then this will be certain to overflow b but gcc will not issue a warning. It's because the assignment to a is legal, a is a char, and it's your problem (not the compiler's) to make sure that math doesn't overflow at runtime.

Rob Napier
Added GCC version info (4.3.2)
James Morris
So what you're saying is that in 4.3, they've fixed the piece I referred to as "the compiler will never catch it." So in cases where a constant is used, there's no warning, but if you do math that could overflow the char, they warn. Sounds like it's working well, then. Is there still a problem?
Rob Napier
The problem was not so much with wanting to catch overflows - my code's already tested and within range. Nor was the main question regarding why some of the examples prompted warnings while others do not. The problem is with the "fix all warnings" philosophy which I have a tendency to want to achieve, but in the case of the -Wconversion flag, it seems completely OTT to litter the simplest of expressions with casts. It was this area I wanted to hear peoples views on.
James Morris
OK, this is fair. Yes, I completely agree with both using -Wconversion and a no-warning policy. The answer I believe is to stop treating your enums as chars. They're type is not char; it's enum. Name your enum, and make your variables of that type. Then you will get proper type checking and won't need to cast as often. You may need to cast occasionally when you do something that's actually violating the type (like doing math on an enum; it's not really a number, it's just implemented as one, and math on it may not be legal).
Rob Napier