程序员 面试笔记 C++ 程序设计的基础 第10章
生活随笔
收集整理的這篇文章主要介紹了
程序员 面试笔记 C++ 程序设计的基础 第10章
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
10.1.1 程序的編譯和執(zhí)行
- 以#開頭的代碼都屬于預(yù)處理器處理的步驟
- #include? 將頭文件的內(nèi)容包含進(jìn)入當(dāng)前源文件中
- #define? ?展開宏定義
- #ifdef? ? ? 處理?xiàng)l件編譯指令(#ifdef、ifndef、#if、#else、#elif、#endif)
- #other? ? 處理其他宏指令(#error、#warning、#line、#pragma)
預(yù)處理器? 其他功能如下:
- 處理預(yù)先定義的宏:例如__DATE__? ?__FILE__(其后均為兩條下劃線)
- 處理注釋:用一個(gè)空格代替連續(xù)的注釋
- 處理三元符
注意
- 編譯器對預(yù)先處理過的代碼進(jìn)行詞法分析、語法分析和語義分析,將符合規(guī)則的程序轉(zhuǎn)化為等價(jià)的匯編代碼
- 匯編器將編譯器生成的匯編代碼翻譯成為計(jì)算機(jī)可以識別的機(jī)器指令,并生成目標(biāo)文件。之所以不將源程序之間轉(zhuǎn)化成為機(jī)器指令,而分成了多級轉(zhuǎn)化,是為了在不同的階段應(yīng)用不同的優(yōu)化技術(shù),并且這些優(yōu)化技術(shù)已經(jīng)很強(qiáng)??梢员WC各個(gè)階段分別優(yōu)化之后可以生成更為高效的機(jī)器指令
- 鏈接器 將所有的目標(biāo)程序鏈接在一起,無論是靜態(tài)還是動(dòng)態(tài)鏈接,最終都可以生成一個(gè)可以在機(jī)器上運(yùn)行的可執(zhí)行的程序。運(yùn)行可執(zhí)行程序就會(huì)得到執(zhí)行的結(jié)果
10.1.2 金典面試題解析
簡述#include<>和#include“ ”之間的區(qū)別
- 主要目的都是為了 將指定文件中的內(nèi)容引入到當(dāng)前的文件,但是搜索被引入文件的時(shí)候,二者采用了不同的搜索策略
- #include<>:直接從編譯器指定的路徑處開始搜索
- #include“ ”:從程序所在的目錄進(jìn)行搜索,如果搜索失敗,再從編譯器指定的路徑處開始搜索
- 用戶自定義的文件只能使用#include“ ”,如果系統(tǒng)文件使用#include“ ”,會(huì)在用戶目錄進(jìn)行無意義的搜索嘗試,如果找不到,再按照編譯器指定的路徑處進(jìn)行搜索
簡述#和##在define里面的作用
#include <iostream> using namespace std; #define PRINTCUBE(x) cout<<"cube("<< #x<<") ="<< (x)*(x)*(x) << endl;int main(){int y = 5;PRINTCUBE(5);PRINTCUBE(y);return 0; } // cube(5) =125 // cube(y) =125- define里面使用了#x,因此#x是會(huì)被字符串化的宏參數(shù)
- #運(yùn)算符會(huì)將其前后參數(shù)轉(zhuǎn)化成為字符串
- ##運(yùn)算符會(huì)將前后的參數(shù)進(jìn)行字符串的連接
assert 斷言的概念
- assert是一個(gè)帶參數(shù)的宏定義,不是一個(gè)函數(shù),頭文件在assert.h文件里面
- 使用assert檢測條件表達(dá)式,如果表達(dá)式為假,表示檢測失敗,程序會(huì)向標(biāo)準(zhǔn)錯(cuò)誤流stderr輸出一條錯(cuò)誤的信息,再次調(diào)用函數(shù)abort函數(shù)終止程序的執(zhí)行
- 頻繁使用assert會(huì)降低程序的性能,增加額外的開銷,一般是調(diào)試中使用宏,調(diào)試完成之后,在#include語句之前 使用#define DEBUG禁用assert宏定義
- assert雖然可以檢測多個(gè)條件,但是最好不要如此使用,因?yàn)橐坏┏鲥e(cuò),不知道是哪個(gè)條件導(dǎo)致的錯(cuò)誤,因此沒有任何的意義
- 不要在assert里面修改變量的數(shù)值,因?yàn)閍ssert只會(huì)在debug版本中使用,一旦使用了RELEASE版本,所有的assert都會(huì)失效。
- 斷言的目的是為了捕獲可控但是不應(yīng)該發(fā)生的情況,不適合外部接口的函數(shù)檢測,因?yàn)橥獠枯斎氲膮?shù)是一個(gè)不可控的事件,應(yīng)該使用if進(jìn)行判定
- 斷言用于 內(nèi)部函數(shù),其參數(shù)檢測使用斷言;因?yàn)楹瘮?shù)的使用者是程序開發(fā)者,錯(cuò)誤的參數(shù)是可控并且不該發(fā)生的情況
- assert用于程序的DEBUG版本檢測條件的表達(dá)式,如果結(jié)果為假,則輸出診斷信息并且終止程序的執(zhí)行
10.2.1? 知識點(diǎn)梳理
- 變量名使用小寫字母;常量、宏定義使用大寫字母
C++類型的轉(zhuǎn)換操作符
static_cast?
- static_cast 完全可以替代C風(fēng)格的類型轉(zhuǎn)換實(shí)現(xiàn)基本的類型轉(zhuǎn)換。
- 此外,進(jìn)行對象指針之間類型轉(zhuǎn)化的時(shí)候,可以實(shí)現(xiàn)父類指針和子類指針之間的轉(zhuǎn)換,但是無關(guān)的兩個(gè)類之間,無法實(shí)現(xiàn)相互轉(zhuǎn)化
- 如果父類指針指向一個(gè)一個(gè)父類對象,將其轉(zhuǎn)換成子類指針,雖然可以通過static_cast實(shí)現(xiàn),但是這種轉(zhuǎn)換可能是不安全的
- 入股分類指針一開始就指向一個(gè)子類對象,將其轉(zhuǎn)換成子類指針,則不存在安全性問題
dynamic_cast?
- dynamic_cast只可以用于對象指針之間的類型轉(zhuǎn)換,可以實(shí)現(xiàn)父類和子類指針之間的轉(zhuǎn)換,還可以轉(zhuǎn)換引用,但是dynamic_cast不等同于static_cast
- dynamic_cast在將父類指針轉(zhuǎn)換為子類指針的過程中,會(huì)對其背后 的對象類型進(jìn)行檢查,以保證類型的完全匹配,而static_cast不會(huì)這么做
- 只有當(dāng)父類指針指向一個(gè)子類對象,并且父類中包含虛函數(shù)的時(shí)候,使用dynamic_cast將父類指針轉(zhuǎn)換成子類指針才會(huì)成功,否則返回空的指針,如果是引用則拋出異常
const_cast
- const_cast 在轉(zhuǎn)換的過程中可以增加或者刪除const屬性,一般情況下,無法將常量指針直接賦值給普通指針,使用const_cast就可以移除常量指針的const屬性,實(shí)現(xiàn)const屬性到非const屬性的轉(zhuǎn)換
reinterpert_cast
- reinterpert_cast 可以將一種類型的指針 直接轉(zhuǎn)換成為 另外一種類型的指針,不管二者之間是否存在繼承關(guān)系;
- reinterpert_cast 實(shí)現(xiàn)指針和整數(shù)之間的轉(zhuǎn)換
- reinterpert_cast 還用在不同函數(shù)指針之間的轉(zhuǎn)換
靜態(tài)全局變量的概念
- 全局變量之前加上const關(guān)鍵字
- 定義和聲明放在源文件中,并且不可以使用extern將靜態(tài)全局變量導(dǎo)出
- 靜態(tài)全部變量的作用域僅僅限定于靜態(tài)全局變量所在的文件內(nèi)部
- 普通的全局靜態(tài)變量的作用域是整個(gè)工程,在頭文件中使用extern關(guān)鍵字聲明普通全局變量,并且在源文件中定義
- 其他文件只需要引入全局變量定義的頭文件,就可以在當(dāng)前文件中使用普通全局變量
- 如果在頭文件中聲明靜態(tài)全局變量,靜態(tài)全局變量在聲明的同時(shí)也會(huì)被初始化,如果沒有顯示的初始化,也會(huì)被初始化為默認(rèn)數(shù)值,相當(dāng)于在頭文件中完成了聲明和定義;而普通全局變量不可以直接定義在頭文件中
- 如果多個(gè)文件都使用#include包含了定義某個(gè)靜態(tài)全局變量的頭文件,那么靜態(tài)全局變量在各個(gè)源文件里面都有一份單獨(dú)的拷貝,而且初始數(shù)值相同,拷貝之間相互獨(dú)立;如果修改了某個(gè)靜態(tài)變量的數(shù)值,不會(huì)影響靜態(tài)全局變量的其他拷貝
參考鏈接
- C++ 全局變量 靜態(tài)全局變量 傻傻分不清
10.4.1?知識點(diǎn)梳理
- 宏函數(shù)省略了函數(shù)調(diào)用的過程,節(jié)省了函數(shù)調(diào)用的開銷;但是宏函數(shù)自身的缺陷,引出了內(nèi)聯(lián)函數(shù)
- 編譯階段,編譯器在發(fā)現(xiàn)inline關(guān)鍵字的時(shí)候,會(huì)將函數(shù)體保存在函數(shù)名字所在的符號表內(nèi),在調(diào)用內(nèi)聯(lián)函數(shù)的地方,編譯器直接在符號表中獲取函數(shù)名字和函數(shù)體,并且使用內(nèi)聯(lián)函數(shù)的函數(shù)體替換函數(shù)的調(diào)用,從而節(jié)省了運(yùn)行的時(shí)候函數(shù)調(diào)用的開銷
- inline出現(xiàn)在函數(shù)定義的時(shí)候,聲明使用inline是沒有用的;此外,函數(shù)使用者不需要知道函數(shù)是否是內(nèi)聯(lián)函數(shù),因此聲明不需要使用inline進(jìn)行函數(shù)的修飾
-
如果在函數(shù)聲明的同時(shí)給出函數(shù)的定義,編譯器會(huì)自動(dòng)將函數(shù)識別為內(nèi)聯(lián)函數(shù);但是不推薦,最好實(shí)現(xiàn)函數(shù)的聲明和函數(shù)的分離;將函數(shù)的定義暴露給用戶是不恰當(dāng)?shù)?/p>
內(nèi)聯(lián)函數(shù)和宏定義的區(qū)別
- 二者都將函數(shù)調(diào)用替換成完整的函數(shù)體,節(jié)省了頻繁的函數(shù)調(diào)用過程中產(chǎn)生的時(shí)間和空間的開銷,提高了程序的執(zhí)行的效率
- 區(qū)別:內(nèi)聯(lián)函數(shù)是個(gè)函數(shù),可以像普通函數(shù)一樣進(jìn)行調(diào)試;而宏定義僅僅是字符串的替換
- 宏定義展開是在預(yù)處理階段;內(nèi)聯(lián)函數(shù)展開是在編譯階段;因此很多編譯階段的工作只對內(nèi)聯(lián)函數(shù)有效,例如類型的安全檢查和自動(dòng)類型的轉(zhuǎn)換
- 內(nèi)聯(lián)函數(shù)作為類的成員函數(shù)的時(shí),可以訪問類的所有成員,公有、私有、保護(hù);this指針也可以隱式的正確使用,宏定義無法實(shí)現(xiàn)此功能
- 內(nèi)聯(lián)函數(shù)需要注意代碼膨脹問題:代碼展開之后,所有使用內(nèi)聯(lián)函數(shù)的地方,都會(huì)內(nèi)嵌內(nèi)聯(lián)函數(shù)的代碼,造成程序代碼體積的極度增長,因此使用內(nèi)聯(lián)函數(shù)需要注意函數(shù)的體積
- 編譯器經(jīng)過優(yōu)化,如果程序員定義的內(nèi)聯(lián)函數(shù)代碼體很大,編譯器也會(huì)拒絕將其展開,決絕將其作為內(nèi)聯(lián)函數(shù)使用
宏展開錯(cuò)誤
- 宏定義的缺陷,主要是指宏展開錯(cuò)誤;主要原因是由于運(yùn)算符號的優(yōu)先級等原因?qū)е碌暮甓x展開之后,語義與預(yù)設(shè)產(chǎn)生偏差
- 宏定義當(dāng)中,最好將參數(shù)加上括號,宏定義的整體也要加上括號,從而保證邏輯的正確性
- 內(nèi)聯(lián)函數(shù)可以被重載
- 構(gòu)造函數(shù)可以定義成內(nèi)聯(lián)函數(shù):但是構(gòu)造函數(shù)會(huì)隱式調(diào)用基類的構(gòu)造函數(shù),并且初始化成員變量,所以代碼展開會(huì)很大
sizeof使用
- sizeof運(yùn)算符號 會(huì)獲取其操作數(shù)所占的內(nèi)存空間的字節(jié)數(shù),sizeof是一個(gè)單目運(yùn)算符號,不是一個(gè)函數(shù)
- sizeof運(yùn)算符的操作數(shù)可以是類型的名字,也可以是表達(dá)式;如果是類型的名字,則直接獲得該類型的字節(jié)數(shù),這種操作一般用于給結(jié)構(gòu)體分配空間時(shí),計(jì)算大小;
- 如果是表達(dá)式,先分析表達(dá)式的結(jié)果類型,再確定所占的字節(jié)數(shù),而不是對表達(dá)式實(shí)際進(jìn)行計(jì)算
- 使用sizeof一般都是與內(nèi)存分配或者計(jì)算數(shù)組長度等需求配合使用;使用sizeof(類型的名)作為分配空間的基數(shù);
- ?uint *ptr = static_cast<uint *>(malloc(sizeof (uint) * 20));
- 計(jì)算數(shù)組的元素的個(gè)數(shù)的時(shí)候,使用sizeof運(yùn)算符號加上數(shù)組元素的類型作為基數(shù),例:int count = sizeof(array) / sizeof(double);
- 其中array是數(shù)組的名字,將數(shù)組的名字作為sizeof的操作數(shù),可以獲得整個(gè)數(shù)組所占的空間;如果將array作為實(shí)參傳遞給子函數(shù),子函數(shù)中形參對應(yīng)的參數(shù)已經(jīng)變?yōu)榱酥羔?#xff0c;對指針使用sizeof只會(huì)獲取指針本身所占的字節(jié)數(shù)
- 使用strlen計(jì)算字符串長度,sizeof是一個(gè)運(yùn)算符,用于獲取類型表達(dá)式所占的內(nèi)存的大小
- strlen是一個(gè)函數(shù),用于計(jì)算字符串中字符的個(gè)數(shù),其中字符串結(jié)束符號 \0? 不會(huì)被計(jì)算在內(nèi)
數(shù)據(jù)對齊
- 數(shù)據(jù)對其是指處理結(jié)構(gòu)體中的成員變量的時(shí)候,成員在內(nèi)存中的起始地址編碼必須是成員類型所占的字節(jié)數(shù)的整倍;
- 例如int類型變量占用四個(gè)字節(jié),因此結(jié)構(gòu)體中int類型成員的起始地址必須是4的倍數(shù),同理,double類型成員的起始地址必須是8的整數(shù)倍;由于結(jié)構(gòu)體數(shù)據(jù)成員在內(nèi)存存放的時(shí)候需要數(shù)據(jù)對齊,就會(huì)造成結(jié)構(gòu)體中邏輯上相鄰的內(nèi)存地址在物理上并不相鄰,兩個(gè)成員之間會(huì)出現(xiàn)多余的空間,書上稱之為補(bǔ)齊;
- 結(jié)構(gòu)體成員使用數(shù)據(jù)對其的主要目的是為了加快數(shù)據(jù)的讀取速度,減少指令的周期,使得程序運(yùn)行速度更快,這部分參考計(jì)算機(jī)組成原理
- 結(jié)構(gòu)體的第一個(gè)成員a是char類型,起始地址為0,占用一個(gè)字節(jié);笑一個(gè)地址為1,第二個(gè)成員的類型是short,占用兩個(gè)字節(jié),起始地址必須是2的倍數(shù),因此地址1 需要空出來一個(gè)位置,b的起始地址是2,下一個(gè)地址是4,第三個(gè)數(shù)據(jù)c是int類型,占用4個(gè)字節(jié),當(dāng)前地址正好為4,下一個(gè)地址為8;同理第四個(gè)數(shù)據(jù)成員是double,起始地址是8的倍數(shù),占用8個(gè)地址,因此笑一個(gè)地址是16
- 因此結(jié)構(gòu)體s1需要16個(gè)字節(jié)。由于s1中最大的double,因此計(jì)算結(jié)果必須是8的整數(shù)倍;因此結(jié)構(gòu)體s1的sizeof的運(yùn)算結(jié)果是16
- 第一個(gè)數(shù)據(jù)成員是int,起始地址是0,占據(jù)4個(gè)字節(jié),下一個(gè)地址為4,第二個(gè)數(shù)據(jù)成員類型是double,占用了8個(gè)字節(jié),起始地址必須是8的整數(shù)倍,因此空出4個(gè)字節(jié),double的起始地址是8,占據(jù)8字節(jié),下一個(gè)地址是16;c為short類型,占用兩個(gè)字節(jié),當(dāng)前地址是16,是2的倍數(shù),short占據(jù)2個(gè)字節(jié),下一個(gè)地址是18;第四個(gè)數(shù)據(jù)成員是char類型,起始地址是18,占據(jù)一個(gè)字節(jié),下一個(gè)字節(jié)是19
- *? 結(jié)構(gòu)體的計(jì)算結(jié)果必須是結(jié)構(gòu)體內(nèi)部占用內(nèi)存空間最大成員類型的整數(shù)倍
- 因此,s2結(jié)構(gòu)體的類型必須是s2的整數(shù)倍,因此,需要數(shù)據(jù)對齊,補(bǔ)充5個(gè)字節(jié),因此s2結(jié)構(gòu)體的sizeof運(yùn)算結(jié)果是24
- 結(jié)構(gòu)中成員成員包含 數(shù)組、其他結(jié)構(gòu)體,在數(shù)據(jù)對齊的時(shí)候,需要以結(jié)構(gòu)體最深層的基本數(shù)據(jù)類型為準(zhǔn)
- 所謂的結(jié)構(gòu)體中最深層的基本數(shù)據(jù)類型是指:如果結(jié)構(gòu)體中的成員為復(fù)雜的數(shù)據(jù)類型,不應(yīng)該以復(fù)雜的數(shù)據(jù)類型所占的空間作為數(shù)據(jù)對齊的標(biāo)準(zhǔn),而是要繼續(xù)深入復(fù)雜的數(shù)據(jù)類型的內(nèi)部,查看其所包含的基本數(shù)據(jù)類型所占的空間。如果結(jié)構(gòu)體成員是一個(gè)數(shù)組,應(yīng)該將數(shù)組的類型作為作為數(shù)據(jù)對齊的標(biāo)準(zhǔn)。如果結(jié)構(gòu)體成員 是 其他結(jié)構(gòu)體,應(yīng)該將內(nèi)層結(jié)構(gòu)體中的基本數(shù)據(jù)類型成員作為外層結(jié)構(gòu)體數(shù)據(jù)對齊的標(biāo)準(zhǔn)
內(nèi)存分配
- C語言中,使用malloc函數(shù)動(dòng)態(tài)申請內(nèi)存空間,使用free函數(shù)將空間進(jìn)行釋放
- 使用malloc函數(shù)申請空間的時(shí)候,首先會(huì)掃描可用空間的鏈表,知道發(fā)現(xiàn)第一個(gè)大于申請空間的可用空間,返回可用空間的首地址,并且利用剩余空間的首地址和大小更新可用空間的鏈表
- 動(dòng)態(tài)分配的空間來自堆空間,指針指向一個(gè)堆空間的地址,指針本身作為局部變量存儲在棧空間里面
- 使用malloc申請的空間必須使用free函數(shù)進(jìn)行釋放
- 使用free函數(shù)釋放空間之后,最好將指針立即置空,這樣可以防止后面的程序?qū)χ羔樀腻e(cuò)誤操作
- 反復(fù)使用malloc和free函數(shù)申請和釋放空間之后,可用的空間鏈表會(huì)不斷更新,導(dǎo)致內(nèi)存空間碎片化;必要的時(shí)候,調(diào)用malloc之后會(huì)進(jìn)行空間的整理,將相鄰的較小的空間合并成為一個(gè)較大的可用的空間,從而滿足申請大的空間的需要
- 為了支持面向?qū)ο蟮募夹g(shù),滿足操作類對象的需要;C++引入了new和delete兩個(gè)操作符,用于申請和釋放空間;
- new和delete不僅支持基本類型還支持自定義的類型,其申請和釋放的空間同樣在堆上
- 對于基本數(shù)據(jù)類型,new操作符號先分配空間,然后使用括內(nèi)的數(shù)值初始化堆上的數(shù)據(jù),并且返回空間的首個(gè)地址,delete操作符,直接釋放堆上的內(nèi)存空間
- 對于自定義的數(shù)據(jù)類型,new首先分配空間,再根據(jù)括號內(nèi)的參數(shù)調(diào)用類的構(gòu)造函數(shù)初始化對象,delete操作符號首先調(diào)用類的析構(gòu)函數(shù)再釋放對象在堆上的空間
- new完成了申請空間 和 初始化的工作
- delete完成了 釋放空間 和 調(diào)用對象析構(gòu)函數(shù)的工作
簡述mallo/free 和 new/delete之間的區(qū)別
- malloc和free是C語言自帶的庫函數(shù),通過函數(shù)調(diào)用訪問,需要傳遞參數(shù)并且接收返回?cái)?shù)值;new和delete是C++提供的運(yùn)算符號,有自己的規(guī)則和運(yùn)算方式
- malloc和free只可以用于基本的數(shù)據(jù)類型;new和delete不僅可以應(yīng)用于基本數(shù)據(jù)類型,還可以應(yīng)用于自定義的數(shù)據(jù)類型;
- malloc和free返回的是void * 類型,程序需要顯示的轉(zhuǎn)換成所需要的指針類型;new直接指明了數(shù)據(jù)的類型,不需要類型的轉(zhuǎn)換
- malloc只負(fù)責(zé)申請內(nèi)存空間,并且返回首地址;free只負(fù)責(zé)釋放空間,標(biāo)識這段內(nèi)存空間已經(jīng)被占用;new完成了申請空間 和 初始化的工作;delete完成了 釋放空間 和 調(diào)用對象析構(gòu)函數(shù)的工作
- new和delete已經(jīng)涵蓋了malloc和free的功能,之所以保留malloc和free是為了解決兼容性問題,防止調(diào)用中出現(xiàn)C函數(shù)時(shí)候?qū)е洛e(cuò)誤
delete和delete[]的區(qū)別
int *i = new int[5];delete i;int *j = new int[5];delete[] j;- 數(shù)組元素是基本數(shù)據(jù)類型的時(shí)候,使用delete和使用delete[]釋放整個(gè)數(shù)組空間是等價(jià)的
- 對于基本數(shù)據(jù)空間,系統(tǒng)會(huì)根據(jù)數(shù)組長度和數(shù)據(jù)的類型計(jì)算出數(shù)組所占的空間,然后一次性釋放整個(gè)空間,因此不需要區(qū)分delete和delete[]
- 使用new[]為數(shù)組分配空間的時(shí)候,如果數(shù)組的類型是自定義的類型,必須通過delete[]釋放整個(gè)數(shù)組空間,因?yàn)閐elete[]會(huì)逐個(gè)調(diào)用數(shù)組中每個(gè)對象的析構(gòu)函數(shù),只有這樣才可以將數(shù)組元素申請的資源釋放,從而將整個(gè)數(shù)組對象的空間完全釋放,不會(huì)造成數(shù)據(jù)的泄露。
- 使用delete釋放用戶自定義的數(shù)據(jù)類型的時(shí)候,只會(huì)調(diào)用數(shù)組中首個(gè)元素的析構(gòu)函數(shù),而delete[]在釋放空間的時(shí)候,會(huì)調(diào)用數(shù)組中所有元素的析構(gòu)函數(shù)?
- 申請和釋放采用配對的形式;new和delete、new[]和delete[]配對使用
位運(yùn)算
計(jì)算二進(jìn)制數(shù)中1的個(gè)數(shù)
- 求二進(jìn)制中1的個(gè)數(shù),只需要逐個(gè)判斷,具體辦法就是指定位置上的元素和1進(jìn)行&操作,其余位置上的元素和0進(jìn)行&操作;這樣只會(huì)判斷指定位置上元素是0還是1,其余位置上的元素和0&后是0,排出干擾
- 也可以將數(shù)字每次右移一位,再和1進(jìn)行按位與計(jì)算;也可以將1不斷左移,再與原先位置上的元素進(jìn)行按位與操作,判斷是0還是1
二進(jìn)制表示的整數(shù),其中1的個(gè)數(shù)
int cnt = 0;while (num) {cnt += num&1;num >>= 1;}- 參考鏈接
將二進(jìn)制數(shù)倒數(shù)第M位的前N位取反
- 按位取反,一般是異或的問題。而且是和1進(jìn)行異或操作。0和1異或的結(jié)果是1,1與1異或的結(jié)果是0,相當(dāng)于取反。
步驟
- 將1左移N位(假設(shè)M=2,N=4:00000001 -> 00010000)
- 將上述結(jié)果-1? (00010000 - 1 = 00001111)
- 將上述結(jié)果左移N位 (N=2,00111100)
- 將上述結(jié)果和原數(shù)字進(jìn)行異或
驗(yàn)證工具
-
在線進(jìn)制轉(zhuǎn)換
一個(gè)數(shù)組中有兩種數(shù)出現(xiàn)了奇數(shù)次,其他數(shù)都出現(xiàn)了偶數(shù)次,怎么找到這兩個(gè)數(shù) 例子
- 相同的數(shù)字進(jìn)行異或操作,其結(jié)果是0,異或操作非常適合去重
參考鏈接
- 算法入門篇 一 時(shí)間復(fù)雜度
- vector容器用法詳解
找出人群中 三個(gè)單身狗中的一個(gè)
- 將三個(gè)元素 分成兩組,第一組里面有兩個(gè)元素,第二組里面有一個(gè)元素;分組之后,剩余元素都要出現(xiàn)偶數(shù)次;對第二組元素進(jìn)行異或操作得到三個(gè)元素中的一個(gè)元素
- 將元素轉(zhuǎn)換為二進(jìn)制,從元素的最末尾開始,先按照最末尾的1元素進(jìn)行分組,如果不能實(shí)現(xiàn)“?第一組里面有兩個(gè)元素,第二組里面有一個(gè)元素 ”,那就按照倒數(shù)第二個(gè)1元素進(jìn)行切分,如果實(shí)現(xiàn)不了,繼續(xù)此過程
- 反證法:如果找不到 第一組里面有兩個(gè)元素,第二組里面有一個(gè)元素,三個(gè)元素都會(huì)在一組里面,則表明這三個(gè)元素都相等,這個(gè)假設(shè)錯(cuò)誤
例子
- 數(shù)組元素[2,5,7,6,4,5,7,2,8]
- 二進(jìn)制轉(zhuǎn)換:2=0010;5=0101;7=0111;6=0100;4=0100;8=1000
- 第一步:按照二進(jìn)制末尾最后一位進(jìn)行分組:
- 組1:2=0010;6=0100;4=0100;2=0010;8=1000;
- 組2:5=0101;7=0111;5=0101;7=0111;
- 第二組5和7成對出現(xiàn),沒有實(shí)現(xiàn)?三個(gè)元素 分成兩組,第一組里面有兩個(gè)元素,第二組里面有一個(gè)元素
- 第二步:按照二進(jìn)制末尾倒數(shù)第二位進(jìn)行分組:
- 組1:5=0101;5=0101;4=0100;8=1000;
- 組2:7=0111;6=0100;7=0111;2=0010;2=0010;
- 第一組異或不為0,意味著三個(gè)元素其中兩個(gè)分到第一組,另外一個(gè)元素分到了第二組
- 第二組只有這個(gè)元素出現(xiàn)了一次,其余都出現(xiàn)了偶數(shù)次
- 二進(jìn)制的位數(shù)是有限的,32位操作系統(tǒng)最多進(jìn)行32次分組檢測就可以得到最終的結(jié)果,因此時(shí)間復(fù)雜度是O(n)級
代碼
補(bǔ)充
main函數(shù)
- main函數(shù)之前會(huì)進(jìn)行一些初始化的操作,main函數(shù)之后也會(huì)進(jìn)行一部分 掃尾工作
- main函數(shù)分成無參和有參兩種類型
- int main()
- int main(int argc,char * argv[])
- 返回值都是int類型
- argc 是argument count的縮寫,整數(shù)類型,表示通過命令行輸入?yún)?shù)的個(gè)數(shù)
- argv 是argument value的縮寫,其中,argv[0]是程序的名字,其余元素為通過命令行輸入的參數(shù)
- mian函數(shù)之前會(huì)進(jìn)行全局對象和靜態(tài)對象的初始化(構(gòu)造),也會(huì)初始化基本數(shù)據(jù)類型的全局變量和靜態(tài)變量
- atexit函數(shù)是一個(gè)指向函數(shù)的指針,參數(shù)是函數(shù)的名字,可以使函數(shù)在atexit內(nèi)部完成函數(shù)的注冊,經(jīng)過注冊的函數(shù)會(huì)在main函數(shù)最后一條語句執(zhí)行之前進(jìn)行調(diào)用
- 函數(shù)的調(diào)用順序和注冊順序相反,函數(shù)注冊使用棧,注冊的時(shí)候,將函數(shù)指針入棧,調(diào)用的時(shí)候函數(shù)出棧
- 輸出:fun_3!? fun_2!? ?fun_1!
?
總結(jié)
以上是生活随笔為你收集整理的程序员 面试笔记 C++ 程序设计的基础 第10章的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 农行信用卡分期付款买手机流程?这些事项要
- 下一篇: Python学习17 Turtle库绘图