Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type conversion warning after bitwise operations in C

How do you explain that line 7 gets a warning, but not line 5 or line 6?

int main()
{
    unsigned char a = 0xFF;
    unsigned char b = 0xFF;
    a = a | b;                        // 5: (no warning)
    a = (unsigned char)(b & 0xF);     // 6: (no warning)
    a = a | (unsigned char)(b & 0xF); // 7: (warning)
    return 0;
}

GCC 4.6.2 output when compiled on 32-bit architecture (Windows PC):

gcc -c main.c --std=c89 -Wall -Wextra -Wconversion -pedantic
main.c: In function 'main':
main.c:7:11: warning: conversion to 'unsigned char' from 'int' may alter its value [-Wconversion]

If this helps you understand my question, here is how I see this (probably incorrect!):

I suppose that on a 32-bit machine operations are done on 32-bit numbers. Since unsigned char fits into 32-bit int, the operation result is 32-bit int. But since GCC doesn't give warnings on lines 5 and 6, I guess there is something else going on:

line 5: GCC figures that (uchar) OR (uchar) is never bigger than MAX(uchar), so no warning.

line 6: GCC figures that (uchar) AND 0xF is never bigger than MAX(uchar), so no warning. Explicit cast is not even necessary.

line 7: Based on assumptions above: AND should not give warning (since line 6), OR should not give warning either (since line 5).

I guess my logic is faulty somewhere there. Help me understand the logic of the compiler.

like image 566
Alex Avatar asked Jun 21 '12 09:06

Alex


1 Answers

Compilers are built by people and they don't have infinite time to figure out all arithmetic possibilities to decide, which cases are worth issuing a warning.

So I believe (attention opinion) that compiler engineers would go the following way:

  • generally issue a warning if code looks as if it could be wrong.
  • find all obvious cases where the compiler can be corrected to work easily.
  • leave the rest of the warnings as false positives, because the person either knows what he's doing or will be relieved that the compiler is warning.

I would expect people to write code where either the result is casted to (unsigned char) or where the outermost operator masks all higher bytes with a constant.

  • a = (unsigned char) ( /* some obscure bit-wise expressoin */ ); would be OK then
  • a = 0xff & ( /* some obscure bit-wise expressoin */ ); also OK

if you know that your compiler translates those two patterns correctly the other cases shouldn't bother you too much.

I've seen compilers that would issue a warning because of a = a | b; so GCC not giving a warning is a free bonus. it might be, that gcc just infers the constant assignment in a | b and therefore replaces it with 0xff | 0xff which is known to work without problems. If that happens though I don't know why it cannot derive the constant value of a in the other statements.

like image 171
Alexander Oh Avatar answered Oct 20 '22 08:10

Alexander Oh