嵌入式校招_面試經(jīng)驗(yàn)大全【C語(yǔ)言】【3_位運(yùn)算】
目錄
【專欄一】嵌入式校招指南
作者機(jī)械碩士,從零開(kāi)始自學(xué)嵌入式軟件,21屆秋招進(jìn)入國(guó)內(nèi)芯片大廠。
從自身轉(zhuǎn)行經(jīng)歷來(lái)看,網(wǎng)上嵌入式學(xué)習(xí)路線的資料少之又少,大多千篇一律且復(fù)制粘貼。
而嵌入式入行門檻高,技能樹(shù)要求多,學(xué)習(xí)難度非常大,沒(méi)有有效的方法指導(dǎo),很容易迷失方向,錯(cuò)過(guò)校招。
在此專欄分享我的校招從零開(kāi)始轉(zhuǎn)行經(jīng)驗(yàn),聽(tīng)我給你娓娓道來(lái)~
專欄鏈接 http://www.fangfengwang8.cn/creation/manager/columnDetail/MWZkkj
1.專欄大綱&寫(xiě)在前面
2.轉(zhuǎn)行概述
3.前期準(zhǔn)備
4.自學(xué)教材推薦_基礎(chǔ)知識(shí)
5.自學(xué)教材推薦_筆試準(zhǔn)備
6.開(kāi)發(fā)板&項(xiàng)目
7.簡(jiǎn)歷
8.行業(yè)&公司
9.城市&崗位
10.消費(fèi)&工業(yè)電子類公司
未完待續(xù)……
【專欄二】嵌入式校招_面試經(jīng)驗(yàn)大全
嵌入式軟件校招的常見(jiàn)問(wèn)題,應(yīng)付校招面試的速效救心丸,你值得擁有!
嵌入式的知識(shí)太多太雜,不知道面試經(jīng)常問(wèn)哪些? 書(shū)上說(shuō)的知識(shí)點(diǎn)太抽象,沒(méi)有一定的基礎(chǔ)很難理解?
別怕,本專欄用通俗的語(yǔ)言和比喻,為你講清楚!
包含C語(yǔ)言、計(jì)算機(jī)組成原理、操作系統(tǒng)、數(shù)據(jù)結(jié)構(gòu)與算法及計(jì)算機(jī)網(wǎng)絡(luò)等,詳見(jiàn)大綱。
1.【C語(yǔ)言】【1_變量】http://www.fangfengwang8.cn/discuss/491773863525679104
2.【C語(yǔ)言】【2_關(guān)鍵字】http://www.fangfengwang8.cn/discuss/497562309628276736
3.【C語(yǔ)言】【3_位運(yùn)算】http://www.fangfengwang8.cn/discuss/505894349847224320
————————————————————
【問(wèn)】介紹一下enum的用法
【答】enum是一種枚舉數(shù)據(jù)類型,描述的是一組常數(shù)的集合
【解析】
- 背景知識(shí)
我們經(jīng)常會(huì)在代碼中使用各種各樣的數(shù)字來(lái)參與計(jì)算/賦值,那么如何更好地統(tǒng)一使用這些數(shù)字呢?
假設(shè)小明同學(xué)參加秋招,某大廠發(fā)offer時(shí)給出的基本工資是根據(jù)個(gè)人情況而不同的,但是年終獎(jiǎng)都是按照較差、普通、優(yōu)秀、杰出來(lái)給予0、2、4、6個(gè)月來(lái)的。
此時(shí)小明想要計(jì)算自己能夠拿到年包的各種可能性,應(yīng)該怎么辦呢?
如果按照以下的方法計(jì)算,也不是不行,但是總感覺(jué)哪里怪怪的。。
我們會(huì)發(fā)現(xiàn)
1.case后面表示績(jī)效等級(jí)的數(shù)字,無(wú)法直觀地體現(xiàn)它代表的含義
2.年終獎(jiǎng)的數(shù)額(多少個(gè)月),無(wú)法表示所代表的績(jī)效等級(jí)
那么,怎么樣才能更直觀地表示出這些呢?
對(duì)的!就是枚舉~
我們將績(jī)效等級(jí)和其所對(duì)應(yīng)的年終獎(jiǎng)數(shù)額都用枚舉來(lái)表示,這樣就非常直觀明了~
#include <stdio.h> #include <stdint.h> enum { WORSE = 0, NORMAL, GOOD, OUTSTANDING }Bonus_Level; enum { WORSE_BONUS = 0, NORMAL_BONUS = 1, GOOD_BONUS = 3, OUTSTANDING_BONUS = 5 }Bonus; /* 小羽的嵌入式校招指南 */ uint32_t CalSalary(uint32_t SalaryBase, uint8_t PerformLevel) { uint32_t AnnualSalary = 0; switch (PerformLevel) { case WORSE: AnnualSalary = SalaryBase * (12 + WORSE_BONUS); printf("\nYour SalaryBase is:[%dk], the AnnualSalary with WORSE is:[%dk]!\n" , SalaryBase, AnnualSalary); break; case NORMAL: AnnualSalary = SalaryBase * (12 + NORMAL_BONUS); printf("\nYour SalaryBase is:[%dk], the AnnualSalary with NORMAL is:[%dk]!\n" , SalaryBase, AnnualSalary); break; case GOOD: AnnualSalary = SalaryBase * (12 + GOOD_BONUS); printf("\nYour SalaryBase is:[%dk], the AnnualSalary with GOOD is:[%dk]!\n" , SalaryBase, AnnualSalary); break; case OUTSTANDING: AnnualSalary = SalaryBase * (12 + OUTSTANDING_BONUS); printf("\nYour SalaryBase is:[%dk], the AnnualSalary with OUTSTANDING is:[%dk]!\n\n" , SalaryBase, AnnualSalary); break; default: printf("Error Perform Level!\n"); break; } return AnnualSalary; } int main(void) { uint32_t BaseSalary; //k uint32_t AnnualSalary; printf("\nPlease input your BaseSalary:"); scanf("%d",&BaseSalary); //WORSE CalSalary(BaseSalary, WORSE); //NORMAL CalSalary(BaseSalary, NORMAL); //GOOD CalSalary(BaseSalary, GOOD); //OUTSTANDING CalSalary(BaseSalary, OUTSTANDING); return 0; }
- 總結(jié)
根據(jù)C語(yǔ)言編程規(guī)范可知,代碼的可讀性是優(yōu)先級(jí)極高的屬性。
因?yàn)榇a首先是要服務(wù)于人,能夠讓人讀懂,其次才是它的性能。
因此在處理這類純數(shù)字的情況時(shí),使用枚舉類型來(lái)代替純數(shù)字,能夠大大提高代碼可讀性~
【問(wèn)】位或操作
【答】兩個(gè)數(shù)據(jù),按位進(jìn)行“或|”運(yùn)算。
運(yùn)算規(guī)則:參加運(yùn)算的兩個(gè)對(duì)象只要有一個(gè)為1,其值為1。0與0位或等于0。
?????即 :0|0=0;??0|1=1;??1|0=1;???1|1=1;
負(fù)數(shù)按補(bǔ)碼形式參加按位或運(yùn)算。
【解析】
位操作的絕大部分場(chǎng)景下,數(shù)據(jù)都是無(wú)符號(hào)的。即unsigned。此處僅考慮無(wú)符號(hào)場(chǎng)景。
- 背景知識(shí)
在某些場(chǎng)景下(尤其是驅(qū)動(dòng)崗位的寄存器中),我們僅需要表示某個(gè)狀態(tài)的開(kāi)或關(guān),用0或者1表示即可。也就是說(shuō),僅需1bit即可。
然而我們知道,就算是變量類型bool,也是需要占據(jù)1Byte空間(即使它只能用來(lái)表示0和1)。
那么在某些需要大量表示狀態(tài)位的場(chǎng)景下(比如表示8盞燈的開(kāi)關(guān)狀態(tài)),每個(gè)狀態(tài)位都使用1Byte來(lái)表示,那么需要8Byte。
這對(duì)于資源極度緊張的嵌入式設(shè)備來(lái)說(shuō),會(huì)造成嚴(yán)重的資源浪費(fèi)!
所以,聰明的先行者們就想到了使用變量中的每個(gè)bit來(lái)表示一個(gè)狀態(tài)開(kāi)關(guān)。
比如uint8_t Status = 21;那么6即是0001 0101。
如果現(xiàn)在需要表示8盞燈的狀態(tài),那么21就表示第1(bit0)、第3(bit2)和第5(bit4)盞燈是亮著的。
那么此時(shí),僅用一個(gè)Byte,就可以表示8盞燈的狀態(tài),真的是方便呀~
- 移位
左移:將變量向左移位操作,高位溢出丟棄,低位補(bǔ)0。
int main(void) { uint8_t BulbStatus = 21; printf("\nThe original num:"); PrintToBin(8, &BulbStatus); /* 小羽的嵌入式校招指南 */ //左移2位 BulbStatus = BulbStatus << 2; printf("After Move 2 bits to the left:"); PrintToBin(8, &BulbStatus); //左移5位 BulbStatus = 21; BulbStatus = BulbStatus << 5; printf("After Move 5 bits to the left:"); PrintToBin(8, &BulbStatus); return 0; }
21的二進(jìn)制是0001 0101。左移2位后等于84,二進(jìn)制為0101 0100。左移5位后為160,二進(jìn)制為1010 0000.
大家發(fā)現(xiàn)一個(gè)現(xiàn)象沒(méi)有?
2^2=4,21*4=84。
2^5=32,21*32=672(超出char類型的[-127,128]),最終只留下672-(128*4)=160.(具體原理查看前面變量的文章——【問(wèn)】*char型變量最大能表示的值?如果超出最大值怎么處理?http://www.fangfengwang8.cn/discuss/491773863525679104)
我們可以發(fā)現(xiàn),對(duì)一個(gè)數(shù)左移1位就是乘以2,左移n位就是乘以2^n。
在CPU中運(yùn)行位移運(yùn)算比乘除法快得多,所以這也是優(yōu)化程序運(yùn)行速度的一個(gè)技巧哦~
右移:將變量向右移位操作,低位溢出丟棄,高位補(bǔ)0。
21的二進(jìn)制是0001 0101。右移2位后等于5,二進(jìn)制為0000 0101。右移4位后為1,二進(jìn)制為0000 0001.
2^2=4,21/4后取整等于5。2^4=16,21/16后取整等于1。
所以,對(duì)一個(gè)數(shù)右移1位就是除以2后取整,左移n位就是除以2^n后取整~
- 對(duì)數(shù)據(jù)的指定位設(shè)置為1
因?yàn)槲换蜻\(yùn)算的特性,0與任何數(shù)與的結(jié)果都是該數(shù)本身。
因此我們只需將我們想要設(shè)置的位跟1做與運(yùn)算,即可將該位設(shè)置為1.
現(xiàn)在有個(gè)uchar型變量,用來(lái)表示8盞燈的開(kāi)關(guān)狀態(tài)。
如果現(xiàn)在燈全是暗著的,即0B0000 0000,我想要表示第2(bit1)和第3(bit2)盞燈為打開(kāi)狀態(tài),應(yīng)該怎么做呢?
int main(void) { uint8_t BulbStatus = 0; /* 小羽的嵌入式校招指南 */ //設(shè)置第2(bit1)盞燈狀態(tài)為打開(kāi) BulbStatus |= 1 << 1; //設(shè)置第3(bit2)盞燈狀態(tài)為打開(kāi) BulbStatus |= 1 << 2; printf("\nAfter Open The 2nd&3rd Bulb:"); PrintToBin(8, &BulbStatus); return 0; }
我們通過(guò)位或操作,就達(dá)到了給指定bit置1的目的。
當(dāng)然我們可以不需要每次只給一盞燈賦予狀態(tài),我們能不能一次性給兩盞燈的狀態(tài)都打開(kāi)呢?
答案是當(dāng)然可以~
我們可以將我們想要打開(kāi)的燈的位置信息,先用位或計(jì)算出來(lái),然后再位或操作寫(xiě)入BulbStatus。
- 羽你俗說(shuō)
位或操作就是能夠精準(zhǔn)地將我們想要的一個(gè)或多個(gè)位置1,屏蔽掉無(wú)關(guān)位。
舉個(gè)栗子,如果我想要在我的羽毛球線上,DIY一個(gè)特別點(diǎn)的logo,最好的方法是什么呢?
手動(dòng)一點(diǎn)點(diǎn)地去畫(huà)不太現(xiàn)實(shí),因?yàn)槟莻€(gè)對(duì)畫(huà)畫(huà)技術(shù)要求太高,很容易走形。
那么最好的方法是什么呢?用一塊板子蒙住,只留下我們希望畫(huà)的圖案的部分。
見(jiàn)下圖:
用記號(hào)筆,對(duì)著中間的空白地方直接畫(huà),最后就會(huì)在羽毛球線上面留下一個(gè)可愛(ài)的只因(手動(dòng)滑稽)~
只有我們選中的(中間空白處)部分才會(huì)被涂畫(huà),其余部分不受影響,這就是位或操作與現(xiàn)實(shí)生活的聯(lián)系~
————————————————
【問(wèn)】位與操作
【答】兩個(gè)數(shù)據(jù),按位進(jìn)行“與&”運(yùn)算。
運(yùn)算規(guī)則:兩個(gè)位的數(shù)據(jù)同時(shí)為“1”,結(jié)果才為“1”,否則為0。
即:0&0=0;??0&1=0;???1&0=0;????1&1=1;
負(fù)數(shù)按補(bǔ)碼形式參加按位與運(yùn)算。
【解析】
- 常見(jiàn)應(yīng)用
(1)判斷奇偶性
a & 1 = 1;則a為奇數(shù)
b & 1 = 0;則a為偶數(shù)
這個(gè)很好理解,用位與運(yùn)算來(lái)判斷bit0的數(shù)值。
從bit1開(kāi)始往后,都是代表了2^N。只有bit0需要判斷奇偶性。
(2) 對(duì)數(shù)據(jù)的指定位設(shè)置為0
因?yàn)槲慌c運(yùn)算的特性,0與任何數(shù)與的結(jié)果都是0。
因此我們只需將我們想要設(shè)置的位跟0做與運(yùn)算,即可將該位設(shè)置為0。
還是以上面的uchar型變量表示8盞燈的開(kāi)關(guān)狀態(tài)為例。
第2(bit1)、第3(bit2)盞燈為打開(kāi),現(xiàn)在將第3(bit2)盞燈關(guān)閉。
具體如下:
將1左移2位,其值為0B0000 0100。再對(duì)其取反,得到0B1111 1011。
此時(shí)我們就得到了bit2為0,其余bit為1的數(shù)(1和任何數(shù)做與&操作,結(jié)果是該值本身,所以我們只改變了值為0的那一位的數(shù)值)。
關(guān)閉bit2后,就只剩下bit1的燈為打開(kāi)狀態(tài)啦~
(3)取指定位的數(shù)值
可以獲取某個(gè)數(shù)指定1個(gè)或多個(gè)位的數(shù)值。
還是以前面的例子,假設(shè)現(xiàn)在第2(bit1)、第3(bit2)、第6(bit5)盞燈狀態(tài)為打開(kāi),即BulbStatus=38,0B0010 0110。
如果想要獲取到底那幾盞燈是打開(kāi)的,應(yīng)該怎么做呢?
int main(void) { uint8_t BulbStatus = 0; uint8_t GetStatus = 0; int i; /* 小羽的嵌入式校招指南 */ //設(shè)置第2(bit1)、第3(bit2)盞燈狀態(tài)為打開(kāi) BulbStatus |= (1 << 1) | (1 << 2); printf("\nAfter Open The 2nd&3rd Bulb:"); PrintToBin(8, &BulbStatus); //依次獲取每盞燈的開(kāi)關(guān)狀態(tài) for (i = 0; i < 8; i++) { if (0 != (BulbStatus & (1 << i)) ) { printf("The No.%d status is On\n\n",i + 1); } } return 0; }
上面依次獲取0-7位的數(shù)值,從而判斷燈的開(kāi)關(guān)狀態(tài)。
這就是位與的用法~
在驅(qū)動(dòng)崗位中,經(jīng)常會(huì)使用這種方法來(lái)獲取寄存器的值~當(dāng)然,也會(huì)用到位或來(lái)設(shè)置寄存器的值~
- 羽你俗說(shuō)
①指定位置0
同上述位或操作,只擦除指定位置的涂畫(huà)
②獲取指定位的數(shù)值
大家都用過(guò)答題卡吧?如下圖這種:
在沒(méi)有讀卡器的時(shí)候,老師們是怎么快速批改試卷的呢?
答案是:把正確答案全部鑿洞,然后直接放在學(xué)生的答題卡上面,看看洞內(nèi)是否有涂,就可以快速批改試卷啦~
咱們的位與操作也是如此,提前選定指定位,查看這些位的數(shù)值~
【問(wèn)】異或操作
【答】兩個(gè)數(shù)據(jù),按位進(jìn)行“異或^”運(yùn)算。
運(yùn)算規(guī)則:兩個(gè)位的數(shù)據(jù)相同(異)時(shí)為“0”,不同時(shí)為“1”。
即:0^0=0;??0^1=1;??1^0=1;?? 1^1=0;
【解析】
- 與0異或,保留原值;與1異或,翻轉(zhuǎn)數(shù)值
還以前面的燈為例,如果我想做出跑馬燈的效果。
第一步,將第1、3、5、7盞燈翻轉(zhuǎn);
第二步,循環(huán)將每一盞燈在量滅之間轉(zhuǎn)換。
這個(gè)燈光秀有點(diǎn)抽象哈~但是大概的意思已經(jīng)表達(dá)清楚了~
- 如何不適用第三個(gè)變量,交換兩個(gè)變量的值
這個(gè)問(wèn)題經(jīng)常出現(xiàn)在校招面試中,常見(jiàn)的有三種解法,咱們這里只講通過(guò)位運(yùn)算的解法。
首先,由異或的特性,咱們推出一個(gè)結(jié)論:
1.B^B=0
因?yàn)樽约汉妥约寒惢颍厝幻恳晃欢际窍嗤?,結(jié)果自然是0
2.A^0=A
與0異或,保持原值。
那么開(kāi)始我們的計(jì)算。
先做一下分析:A^0=A^(B^B)=(A^B)^B
int main(void) { /* 小羽的嵌入式校招指南 */ int A = 10;//B = 1010 int B = 12; //B = 1100; printf("\nIn the beginning\nOA=%d,OB=%d\n\n",A,B); //最初的A用OA表示,最初的B用OB表示 //A = OA^OB A = A ^ B; //B = (OA^OB)^OB = OA^0 = OA B = A ^ B; //A = (OA^OB)^OA = OB^0 = OB A = A ^ B; //完成互換值 printf("\nAt the end\nA=%d,B=%d\n\n",A,B); }
切記當(dāng)前的A和B代表的是原始的A和B怎樣組合起來(lái)的,就不容易搞混啦~
- 找出唯一重復(fù)出現(xiàn)的數(shù)
題:有N個(gè)數(shù),其中包含1個(gè)僅重復(fù)1次的數(shù)值和從1~N-1個(gè)不重復(fù)的數(shù)。找出該數(shù)值。
例:[1,2,3,4,5,5,6,7,8,9],10個(gè)數(shù)的數(shù)組中,包含重復(fù)1次的5和不重復(fù)的12346789。
答:使用該數(shù)組中的每個(gè)數(shù)與兩個(gè)包含1~N-1的不重復(fù)數(shù)組,所有數(shù)值進(jìn)行異或運(yùn)算,結(jié)果就是重復(fù)的數(shù)值。
因?yàn)锳^A=0,兩個(gè)數(shù)組中重復(fù)的數(shù)值異或后得到0。
而重復(fù)的數(shù)字跟0異或,得到的還是該數(shù)值。
怎么樣,神奇不?如果不用異或運(yùn)算,你要怎么算呢?
————————————————————
更多內(nèi)容,持續(xù)更新中?。?!
【覺(jué)得有用的小伙伴們可以訂閱一下專欄,后續(xù)還有更多文章哦~ ?? 】
作者其他專欄
【嵌入式校招指南_完整學(xué)習(xí)路線】http://www.fangfengwang8.cn/creation/manager/columnDetail/MWZkkj
請(qǐng)幫忙點(diǎn)贊、評(píng)論+收藏,是對(duì)我最大的支持~感謝!??!
#校招##嵌入式##offer##秋招##面經(jīng)#