进制转换:
现在的CPU只能识别高低两种电流,只能对二进制数据进行计算。
二进制数据虽然可以直接CPU计算识别,但不方便书写、记录,把二进制数据转换成八进制是为了方便记录在文档中。
随着CPU的不断发展位数不断增加,由早期的8位逐渐发展成现在的64位,因此八进制就不能满足需求了,所以发展出了十六进制,但由于历史原因八进制还不能退出历史舞台。为了理解编程时的一些奇怪现象,我们需要掌握二进制数据、八进制数据、十六进制数据。
二进制数据:
由0~1两个数字组成,逢2进1,由于计算机的运算器只能识别高低两种电流,所以它在运算时只能识别二进制数据。
十进制转换成二进制:
假如把x转换成二进制,x/2记录下余数,然后对商继续除以2,重复这个过程,直到商为0结束,然后把记录的余数倒序汇总,就得到了x的二进制。
例如:117转换成二进制 117 / 2 余数1 58 / 2 余数0 29 / 2 余数1 14 / 2 余数0 7 / 2 余数1 3 / 2 余数1 1 / 2 余数1 0 117的二进制就是:01110101 手算:79 63 121 49
练习:实现一个函数,能够打印无符号的十进制数据的二进制。
#include <stdio.h> void print_binary(unsigned long num,char result[],size_t len) { int cnt = len-1; do{ result[cnt--] = num % 2; num /= 2; }while(cnt >= 0); } int main(int argc,const char* argv[]) { unsigned int num; printf("请输入一个整数:"); scanf("%u",&num); char result[32]; print_binary(num,result,32); for(int i=0; i<32; i++) { printf("%d",result[i]); } }
八进制数据:
由0~7八个数字组成,逢8进1,早期使用它记录二进制数据,现在基本不再使用,文件的权限还依然使用8进制数据表示,所以还无法退出历史。
二进制数据转换八进制:
三位二进制对应一位八进制。
000 0 001 1 010 2 011 3 100 4 101 5 110 6 111 7
注意:
在C代码中,以0开头的是八进制数据,以%o输出的也八进制数据。
十六进制数据:
由0~9和a~f十六个字符组成,随着计算机的发展CPU的位数越来越多,输出的二进制也越来越长,随后科学家又发明出十六进制用于记用二进制数据。
二进制转换成十六进制:
四位二进制对应一位十六进制,超过9的用字母ABCDEF表示(不区分大小写)。
1000 8 1001 9 1010 a 1011 b 1100 c 1101 d 1110 e 1111 f
注意:
在C代码中,以0x开头的是十六进制数据,以%x,%p输出的是十六进制数据。
%#x、%#o 以对应的进制显示数据
任意进制转换成十进制:
每一个非0位带该公式求和 v*b^(n-1) 就得到了十进制。 v 值 b 进制 n 位数
10101100 128+32+8+4 172
0123 64+16+3
关于进制转换可能遇到的笔试题:
1、十进制转换成二进制、八进制、十六进制(以%o或%x为输出占位符)
统一先转换成二进制,然后再把二进制转换成八进制或十六进制。
2、在代码阅读题中遇到0123或0xabcdf
0开头的是8进制,0x开头的是十六进制,可以先转换成二进制再转换10进制,然后再运算。
3、输入一个整数转换成N进制(2<= N <= 35)
#include <stdio.h> int main(int argc,const char* argv[]) { int m = 0 , n = 0; scanf("%d%d",&m,&n); char bits[32] = {} , cnt = 0; while(m) { bits[cnt++] = m % n; m /= n; } for(int i=cnt-1; i>=0; i--) { if(bits[i] < 10) { printf("%d",bits[i]); } else { printf("%c",'A'+bits[i]-10); } } }
原码、反码、补码:
原码:
正数二进制就是它的原码。
负数符号位为1,数值部分取绝对值的二进制就是它原码。- 10
10000000 00000000 00000000 00001010
反码:
正数的原码就是它的反码
负数的反码是他的原码除符号位外,按位取反。
11111111 11111111 11111111 11110101
补码:
正数的原码就是补码
负数的反码+1是补码。
11111111 11111111 11111111 11110110
十进制的数据是以补码形式存储在计算机中的,因为计算机的CPU中只有加法器,也就是只能运算加法,其它运算都是使用加法模拟的。
为了能计算出a-b也就是a + -b 所以需要使用特殊格式存储负数。
1 + (-1)
0000 0001
1111 1111 -1补码
1000 0000 补码
0111 1111 反码
0000 0000 原码
// 以8位计算机为例 24 - 15 24 + -15 10001111 -15的原码 11110000 -15的反码 11110001 -15的补码 00011000 24的补码 00001001 结果是补码,最高位为0,所以是9 // 以32位计算机为例 10000000 00000000 00000000 00001111 -15的原码 11111111 11111111 11111111 11110000 -15的反码 11111111 11111111 11111111 11110001 -15的补码 00000000 00000000 00000000 00011000 24的补码 00000000 00000000 00000000 00001001 结果是9 0xfffffff1
练习:计算出-4567,-1234的补码,把补码转换成16进制赋值给int类型变量,打印输出验证。
-4567 10000000 00000000 00010001 11010111 原码 11111111 11111111 11101110 00101000 反码 11111111 11111111 11101110 00101001 补码 0xffffee29 -1234 10000000 00000000 00000100 11010010 原码 11111111 11111111 11111011 00101101 反码 11111111 11111111 11111011 00101110 补码 0xfffffb2e -127【-128 = -127 + -1】 11111111 原码 10000000 反码 1000 0001 补码 【printf(“%hhd”,0x81)】 【%hd 00000000 10000001 ->129】
补码转换成十进制整数:
补码的两种解析方式:
无符号解析:
由于无符号数据全部是正数,所以补码就是原码,直接转换成十进制即可。
有符号解析:1100011
根据补码的最高位判断它是整数的补码还是负数的补码。
最高位是1:它必然是负数的补码。
1、补码-1得到反码
2、反码符号位不变,按位求反得到原码
3、原码转换成十进制数据。
最高位是0:它必然是正数的补码,直接转换成十进制即可。
int num = 11111111 11111111 11111011 00101110; 1、由于最高位是1,它必须是负数的补码 2、补码-1 得到反码 11111111 11111111 11111011 00101101 3、反码 按位求反 得到 原码 10000000 00000000 00000100 11010010 1024+128+64+16+2 -1234
注意:
当遇到补码转换成十进制数据的笔试题时,必须有是否是有符号信息。
// 假如以下变量的补码是10110011,代码会输出什么,补码的符号信息已经包含在代码中了 char num; printf("%hhd",num); 10110011 10110010 11001101 64+8+4+1 -77 // 把以下补码转换成十进制数据,条件不够充分 11001100
注意:
一个整型变量的取值范围是环型状的,当它是最大值时再加1就会变成最小值,当它是最小值时减1就是变成最大值。
以char类型数据为例: -128 10000000 补码 01111111 -1后的补码,转换成十进制的结果是127 127 01111111 补码 10000000 加1后的补码,转换成十进制的结果是-128 char特殊的补码: 10000000 最小值(最高位是1,其余全是0) 01111111 最大值(最高位是0,其余全是1) 11111111 -1(所有二进制位都是)
// 以下循环执行多少次: char cnt = 7, n = 0; while(cnt) { cnt -= 3; n++; } printf("%hhd\n",n); 7 45 -128 1 125 42 -1 42 -127 1 126 42 0 173 for(char i = 0;i<128;i++) for(uint8_t i = 10;i >= 0;i--)
练习:实现一个函数,显示一个整数的补码(num可能是正数也可能是负数)。
#include <stdio.h> void show_bit(int num) { int back = num; char bit[32] = {} , i = 0; do{ bit[i++] = num % 2; num /= 2; }while(num); if(back < 0) { char carry = 1; for(int i=0; i<32; i++) { bit[i] = !bit[i]; bit[i] += carry; carry = bit[i] /2; bit[i] %= 2; } } for(int i=31; i>=0; i--) { printf("%hhd",bit[i]); } printf("\n"); } int main(int argc,const char* argv[]) { show_bit(0x80000000); }
位运算符:
位运算符是针对数据的补码进行运算。
A & B 按位与运算,它是针对数据的补码进行按位与运算 0 & 0 结果是0 0 & 1 结果是0 1 & 0 结果是0 1 & 1 结果是1 1001 & 0111 0001 A | B 按位或运算 0 | 0 结果是0 0 | 1 结果是1 1 | 0 结果是1 1 | 1 结果是1 A ^ B 按位异或运算 0 ^ 0 结果是0 0 ^ 1 结果是1 1 ^ 0 结果是1 1 ^ 1 结果是0 ~A 按位求反,是单目运算符 ~0 结果是1 ~1 结果是0 x << n 把x的补码前n位丢掉,末尾补上n个0,按位左移。 10101100 << 3 01100000 0000 1000 x >> n 把x的补码后n位丢掉,前面n位,如果x是正数则补0,如果是负数则补1。 char num = -3; printf("%hhd\n",num >> 2+1); num<<3; printf("")
练习:实现一个函数,显示一个整数的补码(num可能是整数也可能是负数)。
#include <stdio.h> void show_bit(int num) { for(int i=0; i<32; i++) { printf("%hhd",((num<<i)&0x80000000)?1:0); } } int main(int argc,const char* argv[]) { show_bit(0x78787878); } #include <stdio.h> void show_bit(int num) { char bit[32], n = 0; for(int i=0; i<32; i++) { bit[n++] = (num>>i) & 1; } for(int i=31; i>=0; i--) { printf("%hhd",bit[i]); } } int main(int argc,const char* argv[]) { show_bit(0x77777777); }
练习2:输入一个整数,把它的4~7位设置1010,其它位置不变。【先与再或】
n & ~(f<<4) | (0xA << 4)
练习3:输入两个整数,把A的4~7位设置为B的3~6位,其它位置不变。
a & ~(0xf0) | (b << 1 & 0xf0)
2048游戏:
1、需要4x4的二维数组。
2、在数组中随机产生4|2,两个数字。
3、显示界面
4、获取方向
5、把数组中的数字向某个方向移动(相同的数字要合并),统计得分。
6、如果数组发生了移动,要在数组再随机产生一个4|2。
7、检查游戏是否结束(没有空位置,上下左右相邻的数字没有相同的),如果不需要结束继续步骤3。
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <getch.h> #include <time.h> unsigned short arr[4][4] = {}; unsigned long score = 0; void init_game(void); void show_arr(void); void up_move(void); void down_move(void); void right_move(void); void left_move(void); void rand_num(void); bool check_over(void); int main(int argc,const char* argv[]) { init_game(); for(;;) { show_arr(); switch(getch()) { case 183: up_move(); break; case 184: down_move(); break; case 185: right_move(); break; case 186: left_move(); break; case 'n': init_game(); break; case 'q': return 0; } rand_num(); if(check_over()) { show_arr(); printf("xxxxxxxxxxxxxx\n"); break; } } }