11

7 行目では警告が発生するが、5 行目や 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;
}

32 ビット アーキテクチャ (Windows PC) でコンパイルした場合の GCC 4.6.2 出力:

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]

これが私の質問を理解するのに役立つ場合、これが私がこれをどのように見るかです(おそらく間違っています!):

32 ビットのマシンでは、操作は 32 ビットの数値で行われると思います。unsigned char32ビットに収まるのでint演算結果は32ビットになりますint。しかし、GCC は 5 行目と 6 行目で警告を出さないので、別のことが起こっていると思います。

5 行目: GCC は、(uchar) OR (uchar) が MAX(uchar) よりも大きくなることはないと判断するため、警告はありません。

6 行目: GCC は、(uchar) AND 0xF が MAX(uchar) よりも大きくなることはないと判断するため、警告はありません。明示的なキャストは必要ありません。

7 行目:上記の仮定に基づく: AND は警告を出すべきではなく (6 行目以降)、OR も警告を出すべきではありません (5 行目以降)。

私の論理はどこかで間違っていると思います。コンパイラのロジックを理解するのを手伝ってください。

4

4 に答える 4

1

コンパイラは人によって構築され、すべての算術演算の可能性を判断するための無限の時間はありません。どの場合に警告を発する価値があります。

したがって、コンパイラエンジニアは次のようになると思います(意見に注意してください)。

  • 通常、コードが間違っている可能性があるように見える場合、警告を発行します。
  • コンパイラが簡単に動作するように修正できる明らかなケースをすべて見つけてください。
  • 残りの警告は誤検知のままにしておきます。その人は自分が何をしているのかを知っているか、コンパイラが警告していることに安心するからです。

(unsigned char)結果がキャストされるコード、または最も外側の演算子が上位バイトをすべて定数でマスクするコードを書くことを期待します。

  • a = (unsigned char) ( /* some obscure bit-wise expressoin */ );それなら大丈夫だろう
  • a = 0xff & ( /* some obscure bit-wise expressoin */ );もOK

コンパイラがこれら 2 つのパターンを正しく変換することがわかっている場合は、他のケースはそれほど気にする必要はありません。

a = a | b;GCCが警告を出さないのは無料のボーナスであるため、警告を出すコンパイラを見たことがあります。おそらく、gcc は定数の割り当てを推測するだけで、問題なく動作することがわかっているa | bものに置き換えます。それが起こった場合、他のステートメントで0xff | 0xff定数値を導出できない理由はわかりません。a

于 2012-07-13T19:44:03.780 に答える
0

Linux x86_64、GCC 4.70 を使用しています。そして、同じエラーが発生します。コードをコンパイルし、gdb を使用して実行ファイルを逆アセンブルします。これが私が得るものです。

(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.

a = a | bコンパイルされます

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

a = (unsigned char)(b & 0xf)コンパイルされます

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

a |= (unsigned char)(b & 0xf);コンパイルされます

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

明示的なキャストが asm コードに表示されませんでした。問題は (b & 0xf) 操作を行う場合です。操作の出力は ですsizeof(int)。したがって、代わりにこれを使用する必要があります。

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

PS:明示的なキャストは警告を生成しません。あなたでさえ何かを失うでしょう。

于 2012-06-21T10:05:55.907 に答える
0

ビット演算子 & の戻り値の型は整数です。int (4 バイト) を char または unsigned char (1 バイト) にキャストすると、警告が表示されます。

したがって、これはビット単位の演算子とは関係がなく、4 バイト変数から 1 バイト変数への型キャストに関連しています。

于 2012-06-26T06:54:21.937 に答える
-1

int問題は、 に変換しunsigned char、 に戻すことだと思いますint

6 行目は に変換intしますunsigned charが、単に に格納しunsigned charます。
7 行目で に変換intunsigned char、演算を行うために に変換し直しintます。新しい整数は元の整数と異なる可能性があるため、警告が表示されます。

于 2012-06-21T10:32:29.083 に答える