您如何解释第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位int
,因此运算结果为32位int
。但由于海湾合作委员会没有在第5和第6行发出警告,我猜还有其他事情发生了:
第5行: GCC认为(uchar)OR(uchar)永远不会大于MAX(uchar),所以没有警告。
第6行: GCC认为(uchar)和0xF永远不会大于MAX(uchar),所以没有警告。甚至不需要显式演员。
第7行:基于上述假设:AND不应发出警告(自第6行以来),或者也不应发出警告(从第5行开始)。
我猜我的逻辑在某处有错。帮助我理解编译器的逻辑。
答案 0 :(得分: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
的常量值。
答案 1 :(得分: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)
explict强制转换没有出现在asm代码中。问题是当(b&amp; 0xf)操作完成时。操作的输出是sizeof(int)
。
所以你应该使用它:
a = (unsigned char)(a | (b & 0xF));
PS:explict cast不会产生任何警告。即使你会失去一些东西。
答案 2 :(得分:0)
我认为问题在于您将int
转换为unsigned char
,并转回int
。
第6行将int
转换为unsigned char
,但只将其存储到unsigned char
。
第7行将int
转换为unsigned char
,然后,为了进行算术运算,将其转换回int
。新整数可能与原始整数不同,因此您会收到警告。
答案 3 :(得分:0)
按位运算符的返回类型&amp;是整数。无论何时将int(4个字节)转换为char或unsigned char(1个字节),它都会向您发出警告。
因此,这与按位运算符无关,它与从4字节变量到1字节变量的类型转换有关。