计算机内存中的每个bit只有两种状态:0或1。计算机内存中,数值一律用补码来表示和存储。无符号数的补码跟原码相同,负数的补码:由负数的原码各位取反(除符号位)后,再加1。
如,值为1类型为char的变量ch1在内存中的二进制码(补码)为:0000 0001。值为-1类型为char的变量ch2在内存中的二进制码(补码)为:1111 1111(-1的原码1000 0001 –>除符号位外各位取反1111 1110 ->再加1得补码1111 1111)。
Debian GNU/Linux平台下验证。
#include <stdio.h> int main(void) { char ch0, ch1, ch2, ch3; ch0 = 64;//0100 0000 ch0 += ch0;//0100 0000 + 0100 0000 ch1 = -1;//1111 1111 ch1 += 1;//1111 1111 + 0000 0001 ch2 = 128;//1000 0000 ch3 = ch2 + ch2;//1000 0000 + 1000 0000 printf("ch0 = %d\tch1 = %d\tch2 = %d\tch3 = %d\n", ch0, ch1, ch2, ch3); return 0; }
编译包含以上内容的文件并执行可执行程序:
lly7@debian:~/C$ gcc DataOverflow.c -o DataOverflow lly7@debian:~/C$ ./DataOverflow ch0= -128 ch1 = 0 ch2 = -128 ch3= 0 |
#include <stdio.h> int main(void) { unsigned char uch1, uch2; uch1 = 128;//1000 0000 uch2 = uch1 + uch1;//1000 0000 + 1000 0000 printf("uch2 = %d\n", uch2); return 0; }
编译包含以上内容的文件并执行可执行程序:
lly7@debian:~/C$ gcc DataOverflow.c -o DataOverflow lly7@debian:~/C$ ./DataOverflow uch2= 0 |
#include <stdio.h> int main(void) { unsigned char uch1, uch2; char ch1, ch2; uch1 = 0x40;//uch1 = 64 uch2 = 0x80;//uch2 = 128 ch1 = uch1 << 1; uch1 = uch1 << 1; ch2 = uch2 >> 1; uch2 = uch2 >> 1; printf("ch1 = %d\tuch1 = %d\tch2 = %d\tuch2 = %d\n", ch1, uch1, ch2, uch2); return 0; }
在DebianGNU/Linux下用gcc编译包含以上内容的文件并执行可执行程序:
lly7@debian:~/C$gcc BitOperationNotation.c -o BitOperationNotation lly7@debian:~/C$./BitOperationNotation ch1 =-128 uch1 = 128 ch2 = 64 uch2= 64 lly7@debian:~/C$ |
由于uch1和uch2的值是无符号数,所以它们在内存中的补码和原码相同,分别为0x40(64)和0x80(128)。ch1在内存中的补码为uch1的补码左移1位(最高位溢出,最低位补0)得0x80,uch1的补码左移一位的值赋给uch1后,uch1的补码也为0x80;ch2在内存中的补码为uch2的补码右移一位(最低位溢出,最高位补0,有的编译器是最低位溢出,最高位补与符号位相同的值)得0x40,uch2补码右移一位的值赋给uch2后,uch2的补码也为0x40。
值的类型并不是值的内在本质,而是取决于它被使用的方式(访问的指令)。无数据转换的情况下,访问二进制序列的指令由编译器关联(编译器根据定义二进制段的类型决定产生何种访问指令)。字符类型其实是具8个位的整型类型。
用printf语句输出(u)ch1,(u)ch2的整型值(%d)时,不涉及数据转换。虽然ch1和uch1在内存中的补码都为0x80,ch2和uch2在内存中的补码都为0x40,但编译器会产生与之数据类型相对应的指令去访问,从而得到不同的输出结果。
编译器根据ch1和ch2的数据类型(char型),产生“字符型(char)访问指令”去访问它们的二进制码,即这两段二进制序列表示有符号char类型值,最高位为符号位,其余位为数据位。所以,ch1和ch2的十进制值分别为-128和64。编译器根据uch1和uch2的数据类型(unsigned char),产生“无符号字符(unsignedchar)型访问指令”去访问它们的二进制码,即这两段二进制序列表示无符号字符类型数据,无符号位。所以,uch1和uch2的十进制值分别为128和64。
移位运算符不需要两个操作数的类型一致,但两边操作数都要做Integer Promotion,整个表达式的类型和左操作数提升后的类型相同。当操作数是有符号数时,右移运算的规则比较复杂:如果是正数,那么高位移入0。如果是负数,那么高位移入1还是0不一定,这是Implementation-defined的。对于x86平台的gcc编译器,最高位移入1,也就是仍保持负数的符号位,这种处理方式对负数仍然保持了“右移1位相当于除以2”的性质。
C Note Over.