数据是以二进制补码存放在计算机中的,其中signed char类型的范围是[-128,127],unsigned char类型的范围是[0,255]。这是因为signed char类型在32位机器中占8个字节,最高位是符号位,所以正数的范围是:00000000——01111111(0到127),负数的范围是:10000000——11111111,因为不可以出现负0,所以10000000被规定值是-128。所以整个signed char的取值范是[-127,128]。那么unsigned char 就更好理解:00000000——11111111(0到255)。
下面我们来看几个典型试题和面试题。
1.
#include <stdio.h>
#include <windows.h>
int main(){
char a = -1;
unsigned char b = -1;
printf("%d %d\n", a, b);
system("pause");
return 0;
}
这道题的结果为什么会是这样呢?明明都赋值为-1,而b打印出来确是255,下面来简要分析一下代码的执行流程:
赋值: 数字在计算机中都是以二进制补码存储的,所以整形-1在32位机器下计算机中的存储形式是:11111111111111111111111111111111。在把int型的变量赋值给char形会发生截断现象,取int的低8位,因为char类型占1个字节(8比特位),所以这时在变量a和b中存储的都是:11111111(存储时与变量类型无关,只看当前数字的二进制补码)。
打印:因为%d打印的是有符号整型,所以在打印字符型变量时,发生整型提升,整型提升关键看变量类型(指的是a或者b,与它们之中存储的数据无关)。如果是有符号数(signed),先看符号位为几(负数为1正数为0),就在前面的高位补24个1或者24个0。如果是无符号(unsigned),前面的高位补24个0。a是有符号数,整型提升时,符号位是1,所以整型提升后的补码为:11111111111111111111111111111111,计算机识别它为-1,所以就打印出了-1。而b是无符号char型,整型提升时直接补上24个0,补码为:00000000000000000000000011111111,因为正数的补码和原码相同,所以计算机直接识别它为255。
2.
#include <stdio.h>
#include <windows.h>
int main(){
char a = -128;
printf("%u", a);
system("pause");
return 0;
}
看完第一题,这个代码就好理解多了,因为在signed char中规定,-128的二进制表示为:10000000(即是原码又是补码)。在赋值时所以在打印时发生整型提升,因为是有符号数,先看符号位,符号位是1,直接补上24个1:11111111111111111111111110000000。又因为是按照无符号打印(%u,直接把内存中的补码数据按照正数打印),所以出现了4294967168,它的二进制补码就是:11111111111111111111111110000000。
3.
#include <stdio.h>
#include <windows.h>
int main(){
int a = -20;
unsigned int b = 10;
printf("%d", a + b);
system("pause");
return 0;
}
这个代码很有意思,如果没有接触过c语言,可能会直接给出正确答案,那不就是-10嘛。但是等你学完c语言的隐式类型转换后可能会对这道题犯愁了, 因为a和b的类型不同,不能直接进行算数计算。所以要把int型转换成unsigned int型(规定),1111 1111 1111 1111 1111 1111 1110 1100和00000000000000000000000000001010直接进行加法运算(符号位也参与运算)得到:1111 1111 1111 1111 1111 1111 1111 0110。因为按照有符号数打印,所以补码转原码后是:-10。
4.
#include <stdio.h>
#include <windows.h>
int main(){
unsigned int i;
for (i = 9; i >= 0; i--){
printf("%u\n", i);
Sleep(500);//睡眠函数,让程序睡眠0.5秒后执行
}
system("pause");
return;
}
这是一个死循环,这个代码应该非常好理解,因为i是unsigned int 类型,i的值永远大于等于0。
那么到0后后边那个很大的数字怎么理解呢?
因为0的二进制补码是:00000000000000000000000000000000,在进行减1操作后,变成:11111111111111111111111111111111,在按照无符号打印就是 4294967295了。
5.
#include <stdio.h>
#include <windows.h>
unsigned char i = 0;
int main(){
for (i = 0; i <= 255; i++){
printf("hello word\n");
}
system("pause");
return;
}
毫无疑问,这个程序也是死循环,站在宏观的角度上,unsigned char类型变量的取值范围是[0,255],所以i的值一直满足条件,所以死循环。
在进一步分析,当i的值是255时,在计算机中存的是255的补码:11111111。当这个值加1后,变成:100000000 ,因为char类型只有1个字节(8位),所以只取低八位,此时i又从0开始,死循环。
6.
#include <stdio.h>
#include <windows.h>
#include <string.h>
int main(){
char a[1000];
int i;
for (i = 0; i < 1000; i++){
a[i] = -1 - i;
}
printf("%d\n", strlen(a));
system("pause");
return;
}
这道题就有点难了,首先要知道strlen是求字符串的长度的,遇到'\0'就会停止。
在for循环中对a数组进行赋值,依此赋值为:-1,-2,......-127,-128。当i=128时-1-i是-129,但是int char类型是存不下-129的,那么这个-129要如何去理解呢?
-129的补码是:1111 1111 1111 1111 1111 1111 0111 1111。当把-129放入char类型数组时,会发生截断,只取后8位:011111111。此时计算机识别这个int char类型的值是127。
那么当i是129时,-1-i的值就是-130,-130的补码是:1111 1111 1111 1111 1111 1111 0111 1110。依然出现截断,此时数组中存的是:011111110。此时这个值就是126。
当i的值是255时,-1-i的值就是-256,-256的补码是: 1111 1111 1111 1111 1111 1111 0000 0000。存入数组时依然发生截断,取后八位:00000000。此时这个值就是0。
当使用strlen时,碰到了'\0'就结束,而'\0'的ASLL码就是0,所以碰到0也就停止了。当第一次a[i]==0时,i的值是255。所以a[255]==0。那么字符串长度此时就是从a[0]到a[254]一共255个值,那么strlen(a)就是255了。程序执行结果如下: