進位制轉換:
現在的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的二進位制就是:000000000000001110101
手算:79 88 121 46
練習1:實現一個函式,能夠獲取無符號int的十進位制資料的二進位制
void print_binary(unsigned int num,char result[],int len);
// result 輸出型引數 儲存資料的二進位制結果
#include <stdio.h>
void print_binary(unsigned int num, char result[], int len) {
int cnt = len - 1;
while(cnt >= 0) {
result[cnt--] = num % 2;
num /= 2;
}
}
int main() {
unsigned int num;
printf("請輸入一個整數:");
scanf("%u",&num);
char result[32];
print_binary(num,result,32);
for (int i = 0; i < 32; ++i) {
printf("%hhd",result[i]);
}
}
八進位制資料:
由0~7八個數字組成,逢8進1,早期使用它記錄二進位制資料,現在基本不再使用,檔案的許可權還依然使用8進位制資料表示,所以還無法退出歷史。0644
二進位制資料轉換八進位制:
從二進位制的低位開始劃分,每三位二進位制對應一位八進位制。
000 0
001 1
010 2
011 3
100 4
101 5
110 6
111 7
注意:
在C程式碼中,以0開頭的是八進位制資料,以%o輸出的也八進位制資料。
0644
十六進位制資料:
由09和af十六個字元組成,隨著計算機的發展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 值 645
b 進位制 8
n 位數 5+4 * 8 +6 * 64
課下算:334 6進位制數 788 9進位制數
關於進位制轉換可能遇到的筆試題:
1、十進位制轉換成二進位制、八進位制、十六進位制,先統一轉換成二進位制,然後再把二進位制轉換成八進位制、十六進位制
2、在程式碼閱讀題中遇到類似 0123 或者 0xabcd,資料的開頭使用0 或者 0x,那麼隱藏了這是個八進位制、十六進位制資料的含義,需要先轉換後運算
3、輸入一個整數和一個N,把該整數轉換成N進位制數 (2<=N<=35)(超過10的位使用字母表示)
#include <stdio.h>
int main() {
int num = 0, N = 0;
scanf("%d %d", &num, &N);
char result[32] = {} ,cnt = 0;
while(num) {
result[cnt++] = num % N;
num /= N;
}
for (int i = cnt - 1; i >= 0; --i) {
if (result[i] < 10) {
printf("%hhd", result[i]);
} else {
printf("%c",'A' + result[i] - 10);
}
}
}
原碼、反碼、補碼:
原碼:
正數二進位制就是它的原碼。
負數符號位為1,數值部分取絕對值的二進位制就是它原碼。
反碼:
正數的原碼就是它的反碼
負數的反碼是他的原碼除符號位外,按位取反。
補碼:
正數的原碼就是補碼
負數的反碼+1是補碼。
十進位制的資料是以補碼形式儲存在計算機中的,因為計算機的CPU中只有加法器,也就是隻能運算加法,其它運算都是使用加法模擬的。
為了能計算出a-b也就是a + -b 所以需要使用特殊格式儲存負數。
-127
1111 1111 原碼
1000 0000 反碼
1000 0001 補碼 0x81
// 以8位計算機為前提
24 - 15
1000 1111 15原
1111 0000 15反
1111 0001 15補碼
0001 1000 24
0000 1001 9補碼
注意:同一個資料,以不同型別顯示時,可能結果不一樣,所以要統一型別去討論
%hhd 0x81 -127
%d 0x81 129
-1234
-5678
補碼轉換成十進位制整數:
補碼的兩種解析方式:
無符號解析:
由於無符號資料全部是正數,所以補碼就是原碼,直接轉換成十進位制即可。
有符號解析:
根據補碼的最高位判斷它是正數的補碼還是負數的補碼。
最高位是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(所有二進位制位都是1)
// 簡述下列程式碼的執行過程
char ch = 7, n = 0;
while(ch) {
ch -= 3;
n++;
}
135 %3 45
-128 1
125 % 3 41
2
130 %3 43
-127 1
126 %3 42 0
173 但是char範圍是-128-127 結果應該-83
printf("%d\n",n);
for (char i = 0; i < 128; ++i) 死迴圈
for (uint8_t i = 10; i >= 0; --i) 死迴圈
位運算子:
位運算子是針對資料的補碼進行運算。
A & B 按位與運算,它是針對資料的補碼進行按位與運算
0 & 0 結果是0
0 & 1 結果是0
1 & 0 結果是0
1 & 1 結果是1
1001 1101 0x9d
0011 1110 0x3e
0001 1100 0x1c
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,按位左移。相當於乘2
10101100 << 3 01100000
01100000
0000 0001 << 1
0000 1000
x >> n 把x的補碼後n位丟掉,前面n位,如果x是正數則補0,如果是負數則補1。
char num = -3;
printf("%hhd\n",num >> 2+1);
1000 0011
1111 1100
1111 1101
1111 1111
實現一個函式,顯示一個整數的補碼(該整數可能是負數也可能是正數)
#include <stdio.h>
void show_bit(int num) {
char bits[32] = {};
for (int i = 0; i < 32; ++i) {
bits[i] = num >> i & 1;
}
for (int i = 31; i >= 0; --i) {
printf("%hhd", bits[i]);
}
}
int main() {
show_bit(88);
}
輸入一個整數,把它的4~7位置為1100,其它位不變
00000000 00000000 00000000 11110000 0xf0
11101101 11110111 11111111 10101000 num
11111111 11111111 11111111 00001111 & ~0xf0
11101101 11110111 11111111 00001000 |
00000000 00000000 00000000 11000000 0xc0
int num;
num & ~0xf0 | 0xc0
num & ~(0xf<<4) | 0xc0 先與再或
標準C語言專案:2048遊戲
1、需要4*4的整數二維陣列
2、需要在陣列的隨機空位置產生2|4
3、顯示介面
4、獲取方向鍵
5、把陣列中的數字向方向鍵的方向移動(前方數字相同,就要合併,並統計分數)
6、如果陣列發生了移動或合併,再在隨機空位置產生一個2|4
7、檢查遊戲是否失敗(沒有空位置或者不能再合併),如果沒有結束重複步驟3往下
#include <stdio.h>
#include <stdbool.h>
int arr[4][4] = {};
// 隨機位置產生2|4
void rand_num(void);
// 顯示介面
void show_view(void);
// 方向處理
void up_move(void);
void down_move(void);
void left_move(void);
void right_move(void);
// 檢查是否失敗
bool is_over(void);
int main(int argc,const char* argv[])
{
for(;;)
{
rand_num();
show_view();
switch(getch())
{
case 183: up_move(); break;
case 184: down_move(); break;
case 185: right_move(); break;
case 186: left_move(); break;
}
if(is_over())
{
printf("遊戲結束\n");
return 0;
}
}
}
~
程式碼實現
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <getch.h>
#include <string.h>
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, 1, -1};
int d[4][4];
bool ok = false;
void print() {
system("clear");
for (int i = 0; i < 9; ++i) {
if (i % 2 == 0) {
puts("-----------------------------");
} else {
for (int j = 0; j < 4; ++j) if (d[i / 2][j] != 0) {
printf("|%6d", d[i / 2][j]);
} else {
printf("| ");
}
printf("|\n");
}
}
}
bool check() {
int cnt = 0;
ok = false;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
cnt += d[i][j] == 0;
for (int dir = 0; dir < 4; ++dir) {
int fx = i + dx[dir], fy = j + dy[dir];
if (fx >= 0 && fy >= 0 && fx < 4 && fy < 4 && d[fx][fy] == d[i][j] && d[i][j]) {
ok = true;
return true;
}
}
}
}
return cnt != 0;
}
void get() {
if (ok) {
return ;
}
int w = rand() % 2;
w = 1 << w + 1;
int x = rand() % 4, y = rand() % 4;
while (d[x][y] != 0) {
x = rand() % 4, y = rand() % 4;
}
d[x][y] = w;
}
int move() {
int dir = getch() - 183, score = 0;
int cpd[4][4], cp[4][4];
memcpy(cp, d, sizeof d);
if (dir == 0) {
for (int j = 0; j < 4; ++j) {
for (int k = 0; k < 4; ++k) {
for (int i = 1; i < 4; ++i) if (d[i - 1][j] == 0 && d[i][j]) {
d[i - 1][j] ^= d[i][j] ^= d[i - 1][j] ^= d[i][j];
}
}
}
memcpy(cpd, d, sizeof d);
for (int j = 0; j < 4; ++j) {
for (int i = 0; i < 4; ++i) {
int fx = dx[dir] + i, fy = dy[dir] + j;
if (fx >= 0 && fy >= 0 && fx < 4 && fy < 4 && cpd[fx][fy] == cpd[i][j] && cpd[fx][fy]) {
d[fx][fy] += d[i][j];
d[i][j] = 0;
score += d[fx][fy];
}
}
}
for (int j = 0; j < 4; ++j) {
for (int k = 0; k < 4; ++k) {
for (int i = 1; i < 4; ++i) if (d[i - 1][j] == 0 && d[i][j]) {
d[i - 1][j] ^= d[i][j] ^= d[i - 1][j] ^= d[i][j];
}
}
}
} else if (dir == 1) {
for (int j = 3; j >= 0; --j) {
for (int k = 0; k < 4; ++k) {
for (int i = 2; i >= 0; --i) if (d[i + 1][j] == 0 && d[i][j]) {
d[i + 1][j] ^= d[i][j] ^= d[i + 1][j] ^= d[i][j];
}
}
}
memcpy(cpd, d, sizeof d);
for (int j = 3; j >= 0; --j) {
for (int i = 3; i >= 0; --i) {
int fx = dx[dir] + i, fy = dy[dir] + j;
if (fx >= 0 && fy >= 0 && fx < 4 && fy < 4 && cpd[fx][fy] == cpd[i][j] && cpd[fx][fy]) {
d[fx][fy] += d[i][j];
d[i][j] = 0;
score += d[fx][fy];
}
}
}
for (int j = 3; j >= 0; --j) {
for (int k = 0; k < 4; ++k) {
for (int i = 2; i >= 0; --i) if (d[i + 1][j] == 0 && d[i][j]) {
d[i + 1][j] ^= d[i][j] ^= d[i + 1][j] ^= d[i][j];
}
}
}
} else if (dir == 2) {
for (int i = 3; i >= 0; --i) {
for (int k = 0; k < 4; ++k) {
for (int j = 2; j >= 0; --j) if (d[i][j + 1] == 0 && d[i][j]) {
d[i][j + 1] ^= d[i][j] ^= d[i][j + 1] ^= d[i][j];
}
}
}
memcpy(cpd, d, sizeof d);
for (int i = 3; i >= 0; --i) {
for (int j = 3; j >= 0; --j) {
int fx = dx[dir] + i, fy = dy[dir] + j;
if (fx >= 0 && fy >= 0 && fx < 4 && fy < 4 && cpd[fx][fy] == cpd[i][j] && cpd[fx][fy]) {
d[fx][fy] += d[i][j];
d[i][j] = 0;
score += d[fx][fy];
}
}
}
for (int i = 3; i >= 0; --i) {
for (int k = 0; k < 4; ++k) {
for (int j = 2; j >= 0; --j) if (d[i][j + 1] == 0 && d[i][j]) {
d[i][j + 1] ^= d[i][j] ^= d[i][j + 1] ^= d[i][j];
}
}
}
} else if (dir == 3) {
for (int i = 0; i < 4; ++i) {
for (int k = 0; k < 4; ++k) {
for (int j = 1; j < 4; ++j) if (d[i][j - 1] == 0 && d[i][j]) {
d[i][j - 1] ^= d[i][j] ^= d[i][j - 1] ^= d[i][j];
}
}
}
memcpy(cpd, d, sizeof d);
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
int fx = dx[dir] + i, fy = dy[dir] + j;
if (fx >= 0 && fy >= 0 && fx < 4 && fy < 4 && cpd[fx][fy] == cpd[i][j] && cpd[fx][fy]) {
d[fx][fy] += d[i][j];
d[i][j] = 0;
score += d[fx][fy];
}
}
}
for (int i = 0; i < 4; ++i) {
for (int k = 0; k < 4; ++k) {
for (int j = 1; j < 4; ++j) if (d[i][j - 1] == 0 && d[i][j]) {
d[i][j - 1] ^= d[i][j] ^= d[i][j - 1] ^= d[i][j];
}
}
}
}
ok = true;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) if (cp[i][j] != d[i][j]) {
ok = false;
}
}
return score;
}
int main() {
srand(time(0));
get(), print();
int score = 0, idx = 0;
while (check()) {
idx += 1, score += move();
get(), print();
}
printf("遊戲已經結束,您的得分是%d, 共用了%d步\n", score, idx);
}