Type conversion warning after bitwise operations in C

后端 未结 4 1238
傲寒
傲寒 2021-02-20 05:29

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

int main()
{
    unsigned char a = 0xFF;
    unsig         


        
相关标签:
4条回答
  • 2021-02-20 05:50

    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.

    0 讨论(0)
  • 2021-02-20 05:56

    I use linux x86_64, GCC 4.70. And get the same error. I compile the code, and use gdb to disassemble the execution file. Here is what I get.

    (gdb) l
    1   int main(){
    2     unsigned char a = 0xff;
    3     unsigned char b = 0xff;
    4     a = a | b;
    5     a = (unsigned char)(b & 0xf);
    6     a |= (unsigned char)(b & 0xf); 
    7     return 0;
    8   }
    (gdb) b 4
    Breakpoint 1 at 0x4004a8: file test.c, line 4.
    (gdb) b 5
    Breakpoint 2 at 0x4004af: file test.c, line 5.
    (gdb) b 6
    Breakpoint 3 at 0x4004b9: file test.c, line 6.
    (gdb) r
    Starting program: /home/spyder/stackoverflow/a.out 
    
    Breakpoint 1, main () at test.c:4
    4     a = a | b;
    (gdb) disassemble 
    Dump of assembler code for function main:
       0x000000000040049c <+0>: push   %rbp
       0x000000000040049d <+1>: mov    %rsp,%rbp
       0x00000000004004a0 <+4>: movb   $0xff,-0x1(%rbp)
       0x00000000004004a4 <+8>: movb   $0xff,-0x2(%rbp)
    => 0x00000000004004a8 <+12>:    movzbl -0x2(%rbp),%eax
       0x00000000004004ac <+16>:    or     %al,-0x1(%rbp)
       0x00000000004004af <+19>:    movzbl -0x2(%rbp),%eax
       0x00000000004004b3 <+23>:    and    $0xf,%eax
       0x00000000004004b6 <+26>:    mov    %al,-0x1(%rbp)
       0x00000000004004b9 <+29>:    movzbl -0x2(%rbp),%eax
       0x00000000004004bd <+33>:    mov    %eax,%edx
       0x00000000004004bf <+35>:    and    $0xf,%edx
       0x00000000004004c2 <+38>:    movzbl -0x1(%rbp),%eax
       0x00000000004004c6 <+42>:    or     %edx,%eax
       0x00000000004004c8 <+44>:    mov    %al,-0x1(%rbp)
       0x00000000004004cb <+47>:    mov    $0x0,%eax
       0x00000000004004d0 <+52>:    pop    %rbp
       0x00000000004004d1 <+53>:    retq   
    End of assembler dump.
    

    the a = a | b is compiled to

    movzbl -0x2(%rbp),%eax
    or     %al,-0x1(%rbp)
    

    the a = (unsigned char)(b & 0xf) is compiled to

    mov    %al,-0x2(%rbp)
    and    $0xf,%eax
    mov    %al,-0x1(%rbp)
    

    the a |= (unsigned char)(b & 0xf); is compiled to

    movzbl -0x2(%rbp),%eax
    mov    %eax,%edx
    and    $0xf,%edx
    movzbl -0x1(%rbp),%eax
    or     %edx,%eax
    mov    %al,-0x1(%rbp)
    

    the explict cast didn't appear in asm code. The problem is when (b & 0xf) operation is done. the output of operation is sizeof(int). So you should use this instead:

    a = (unsigned char)(a | (b & 0xF));
    

    PS: explict cast dont generate any warning. even you will lose something.

    0 讨论(0)
  • 2021-02-20 06:04

    The return type of bitwise operator & is integer. Whenever you cast an int (4 bytes) into char or unsigned char (1 byte) it gives you warning.

    So this is not related to bitwise operator it is related to typecasting from 4 bytes variable to 1 bytes variable.

    0 讨论(0)
  • 2021-02-20 06:05

    I think the problem is that you convert int to unsigned char, AND back to int.

    Line 6 converts int to unsigned char, but just stores it into unsigned char.
    Line 7 converts int to unsigned char, and then, in order to do arithmetic, converts it back to int. The new integer may be different from the original, so you get a warning.

    0 讨论(0)
提交回复
热议问题