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 char适合 32-bit int,因此运算结果为 32-bit int。但由于 GCC 没有在第 5 行和第 6 行给出警告,我想还有其他事情发生:

第 5 行: GCC 认为 (uchar) OR (uchar) 永远不会大于 MAX(uchar),所以没有警告。

第 6 行: GCC 认为 (uchar) AND 0xF 永远不会大于 MAX(uchar),所以没有警告。甚至不需要显式强制转换。

第 7 行:基于上述假设:AND 不应该发出警告(从第 6 行开始),或者也不应该发出警告(从第 5 行开始)。

我想我的逻辑在那里的某个地方有问题。帮助我理解编译器的逻辑。

4

4 回答 4

1

编译器是由人构建的,他们没有无限的时间来找出所有算术可能性来决定,哪些情况值得发出警告。

所以我相信(注意意见)编译器工程师会采取以下方式:

  • 如果代码看起来可能是错误的,通常会发出警告。
  • 找到所有可以更正编译器以使其轻松工作的明显情况。
  • 将其余的警告保留为误报,因为该人要么知道他在做什么,要么会因为编译器发出警告而松了一口气。

我希望人们编写代码,其中要么将结果转换为,要么(unsigned char)最外层的运算符用常量屏蔽所有更高的字节。

  • a = (unsigned char) ( /* some obscure bit-wise expressoin */ );那么就可以了
  • a = 0xff & ( /* some obscure bit-wise expressoin */ );还可以

如果您知道您的编译器正确地翻译了这两种模式,那么其他情况就不应该打扰您太多。

我见过编译器会发出警告,因为a = a | b;GCC 不发出警告是免费的奖励。可能是,gcc 只是推断出常量分配,因此用已知可以毫无问题地工作的方式a | b替换它。0xff | 0xff如果发生这种情况,尽管我不知道为什么它不能a在其他语句中导出常数值。

于 2012-07-13T19:44:03.780 回答
0

按位运算符 & 的返回类型是整数。每当您将 int(4 个字节)转换为 char 或 unsigned char(1 个字节)时,它都会给您警告。

所以这与按位运算符无关,它与从 4 字节变量到 1 字节变量的类型转换有关。

于 2012-06-26T06:54:21.937 回答
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 回答
-1

我认为问题在于您转换intunsigned char, 并返回int.

第 6 行转换intunsigned char,但只是将其存储到unsigned char.
第 7 行转换intunsigned char,然后为了进行算术运算,将其转换回int. 新整数可能与原始整数不同,因此您会收到警告。

于 2012-06-21T10:32:29.083 回答