C++ 面試題目(整理自??途W(wǎng))

1、寫出完整版的strcpy函數(shù)
char * strcpy( char *strDest, const char *strSrc ) { assert( (strDest != NULL) && (strSrc != NULL) ); char *address = strDest; while( (*strDest++ = * strSrc++) != ‘\0’ ); return address; }
要點(diǎn):
使用assert斷言函數(shù),判斷參數(shù)是否為NULL;
遇'\0'則停止賦值;
返回新的字符串的首地址。
2、指出代碼錯(cuò)誤
void Test( void ) { char *str = (char *) malloc( 100 ); strcpy( str, "hello" ); free( str ); ... //省略的其它語句 }
錯(cuò)誤有二:
使用malloc分配內(nèi)存后,應(yīng)判斷是否分配成功;
free之后,應(yīng)置str為NULL,防止變成野指針。
PS:malloc函數(shù)
malloc函數(shù)是一種分配長(zhǎng)度為num_bytes字節(jié)的內(nèi)存塊的函數(shù),可以向系統(tǒng)申請(qǐng)分配指定size個(gè)字節(jié)的內(nèi)存空間。malloc的全稱是memory allocation,中文叫動(dòng)態(tài)內(nèi)存分配,當(dāng)無法知道內(nèi)存具體位置的時(shí)候,想要綁定真正的內(nèi)存空間,就需要用到動(dòng)態(tài)的分配內(nèi)存。
第一、malloc 函數(shù)返回的是 void * 類型。
對(duì)于C++,如果你寫成:p = malloc (sizeof(int)); 則程序無法通過編譯,報(bào)錯(cuò):“不能將 void* 賦值給 int * 類型變量”。
所以必須通過 (int *) 來將強(qiáng)制轉(zhuǎn)換。而對(duì)于C,沒有這個(gè)要求,但為了使C程序更方便的移植到C++中來,建議養(yǎng)成強(qiáng)制轉(zhuǎn)換的習(xí)慣。
第二、函數(shù)的實(shí)參為 sizeof(int) ,用于指明一個(gè)整型數(shù)據(jù)需要的大小。
在Linux中可以有這樣:malloc(0),這是因?yàn)長(zhǎng)inux中malloc有一個(gè)下限值16Bytes,注意malloc(-1)是禁止的;但是在某些系統(tǒng)中是不允許malloc(0)的。
malloc 只管分配內(nèi)存,并不能對(duì)所得的內(nèi)存進(jìn)行初始化,所以得到的一片新內(nèi)存中,其值將是隨機(jī)的。
3、分別給出BOOL,int,float,指針變量 與“零值”比較的 if 語句
BOOL型變量:if(!var)
int型變量: if(var==0)
float型變量:
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)
指針變量:if(var==NULL)
4、以下為Windows NT下的32位C++程序,請(qǐng)計(jì)算sizeof的值
void Func ( char str[100] ) { sizeof( str ) = ? } void *p = malloc( 100 ); sizeof ( p ) = ? sizeof( str ) = 4 sizeof ( p ) = 4
Func ( char str[100] )函數(shù)中數(shù)組名作為函數(shù)形參時(shí),在函數(shù)體內(nèi),數(shù)組名失去了本身的內(nèi)涵,僅僅只是一個(gè)指針;在失去其內(nèi)涵的同時(shí),它還失去了其常量特性,可以作自增、自減等操作,可以被修改。
但是數(shù)組名在不作形參時(shí),仍然代表整個(gè)數(shù)組,這時(shí)的sizeof應(yīng)返回?cái)?shù)組長(zhǎng)度。
sizeof返回的單位是字節(jié)。對(duì)于結(jié)構(gòu)體,sizeof返回可能會(huì)有字節(jié)填充。結(jié)構(gòu)體的總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍。
5、寫一個(gè)“標(biāo)準(zhǔn)”宏MIN,這個(gè)宏輸入兩個(gè)參數(shù)并返回較小的一個(gè)。另外,當(dāng)你寫下面的代碼時(shí)會(huì)發(fā)生什么事?
least = MIN(*p++, b);
答案:
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
四個(gè)注意點(diǎn):
宏定義中,左側(cè)為宏名和參數(shù),右側(cè)為宏的實(shí)現(xiàn);
在宏的實(shí)現(xiàn)中,所有參數(shù)應(yīng)用括號(hào)括起來;
整個(gè)宏的實(shí)現(xiàn)的外面也要用括號(hào)括起來;
最后沒有分號(hào)。
寫下如上代碼會(huì)導(dǎo)致p自增兩次。
6、ifndef、extern的用法
條件指示符#ifndef 的最主要目的是防止頭文件的重復(fù)包含和編譯。
extern修飾變量的聲明。
如果文件a.c需要引用b.c中變量int v,就可以在a.c中聲明extern int v,然后就可以引用變量v。
這里需要注意的是,被引用的變量v的鏈接屬性必須是外鏈接(external)的,也就是說a.c要引用到v,不只是取決于在a.c中聲明extern int v,還取決于變量v本身是能夠被引用到的。
7、請(qǐng)說出static和const關(guān)鍵字盡可能多的作用
static:
1. 修飾普通變量,修改變量的存儲(chǔ)區(qū)域和生命周期,使變量存儲(chǔ)在靜態(tài)區(qū),在main函數(shù)運(yùn)行前就分配了空間,如果有初始值就用初始值初始化它,如果沒有初始值系統(tǒng)用默認(rèn)值初始化它。在每次調(diào)用時(shí),其值為上一次調(diào)用后改變的值,調(diào)用結(jié)束后不釋放空間。此變量只在聲明變量的文件內(nèi)可見。
2. 修飾普通函數(shù),表明函數(shù)的作用范圍,僅在定義該函數(shù)的文件內(nèi)才能使用。在多人開發(fā)項(xiàng)目時(shí),為了防止與他人命令函數(shù)重名,可以將函數(shù)定義為static。
3. 修飾成員變量,修飾成員變量使所有的對(duì)象只保存一個(gè)該變量,而且不需要生成對(duì)象就可以訪問該成員。
4. 修飾成員函數(shù),修飾成員函數(shù)使得不需要生成對(duì)象就可以訪問該函數(shù),但是在static函數(shù)內(nèi)不能訪問非靜態(tài)成員。
const:
1. 修飾變量,說明該變量不可以被改變;
2. 修飾指針,分為指向常量的指針和指針常量;
3. 常量引用,經(jīng)常用于形參類型,即避免了拷貝,又避免了函數(shù)對(duì)值的修改;
4. 修飾成員函數(shù),說明該成員函數(shù)內(nèi)不能修改成員變量。
8、請(qǐng)說一下C/C++ 中指針和引用的區(qū)別?
1.指針有自己的一塊空間,而引用只是一個(gè)別名;
2.使用sizeof看一個(gè)指針的大小是4,而引用則是被引用對(duì)象的大?。?/p>
3.指針可以被初始化為NULL,而引用必須被初始化且必須是一個(gè)已有對(duì)象 的引用;
4.作為參數(shù)傳遞時(shí),指針需要被解引用才可以對(duì)對(duì)象進(jìn)行操作,而直接對(duì)引用的修改都會(huì)改變引用所指向的對(duì)象;
5.可以有const指針,但是沒有const引用;
6.指針在使用中可以指向其它對(duì)象,但是引用只能是一個(gè)對(duì)象的引用,不能被改變;
7.指針可以有多級(jí)指針(**p),而引用只有一級(jí);
8.指針和引用使用++運(yùn)算符的意義不一樣;
9.如果返回動(dòng)態(tài)內(nèi)存分配的對(duì)象或者內(nèi)存,必須使用指針,引用可能引起內(nèi)存泄露。
9、給定三角形ABC和一點(diǎn)P(x,y,z),判斷點(diǎn)P是否在ABC內(nèi),給出思路并手寫代碼
根據(jù)面積法,如果P在三角形ABC內(nèi),那么三角形ABP的面積+三角形BCP的面積+三角形ACP的面積應(yīng)該等于三角形ABC的面積。
10、野指針是什么?
野指針就是指向一個(gè)已刪除的對(duì)象或者未申請(qǐng)?jiān)L問受限內(nèi)存區(qū)域的指針。
11、為什么析構(gòu)函數(shù)必須是虛函數(shù)?為什么C++默認(rèn)的析構(gòu)函數(shù)不是虛函數(shù)?
將可能會(huì)被繼承的父類的析構(gòu)函數(shù)設(shè)置為虛函數(shù),可以保證當(dāng)我們new一個(gè)子類,然后使用基類指針指向該子類對(duì)象,釋放基類指針時(shí)可以釋放掉子類的空間,防止內(nèi)存泄漏。
C++默認(rèn)的析構(gòu)函數(shù)不是虛函數(shù)是因?yàn)樘摵瘮?shù)需要額外的虛函數(shù)表和虛表指針,占用額外的內(nèi)存。而對(duì)于不會(huì)被繼承的類來說,其析構(gòu)函數(shù)如果是虛函數(shù),就會(huì)浪費(fèi)內(nèi)存。因此C++默認(rèn)的析構(gòu)函數(shù)不是虛函數(shù),而是只有當(dāng)需要當(dāng)作父類時(shí),設(shè)置為虛函數(shù)。
PS:C++類的六個(gè)默認(rèn)成員函數(shù):
- 構(gòu)造函數(shù):一個(gè)特殊的成員函數(shù),名字與類名相同,創(chuàng)建類類型對(duì)象的時(shí)候,由編譯器自動(dòng)調(diào)用,在對(duì)象的生命周期內(nèi)只且調(diào)用一次,以保證每個(gè)數(shù)據(jù)成員都有一個(gè)合適的初始值。
- 拷貝構(gòu)造函數(shù):只有單個(gè)形參,而且該形參是對(duì)本類類型對(duì)象的引用(常用const修飾),這樣的構(gòu)造函數(shù)稱為拷貝構(gòu)造函數(shù)??截悩?gòu)造函數(shù)是特殊的構(gòu)造函數(shù),創(chuàng)建對(duì)象時(shí)使用已存在的同類對(duì)象來進(jìn)行初始化,由編譯器自動(dòng)調(diào)用。
- 析構(gòu)函數(shù):與構(gòu)造函數(shù)功能相反,在對(duì)象被銷毀時(shí),由編譯器自動(dòng)調(diào)用,完成類的一些資源清理和收尾工作。
- 賦值運(yùn)算符重載:對(duì)于類類型的對(duì)象我們需要對(duì)‘=’重載,以完成類類型對(duì)象之間的賦值。
- 取址操作符重載:函數(shù)返回值為該類型的指針,無參數(shù)。
- const修飾的取址運(yùn)算符重載。
12、C++中析構(gòu)函數(shù)的作用?
析構(gòu)函數(shù)與構(gòu)造函數(shù)對(duì)應(yīng),當(dāng)對(duì)象結(jié)束其生命周期,如對(duì)象所在的函數(shù)已調(diào)用完畢時(shí),系統(tǒng)會(huì)自動(dòng)執(zhí)行析構(gòu)函數(shù)。
析構(gòu)函數(shù)名也應(yīng)與類名相同,只是在函數(shù)名前面加一個(gè)位取反符~,例如~stud( ),以區(qū)別于構(gòu)造函數(shù)。它不能帶任何參數(shù),也沒有返回值(包括void類型)。只能有一個(gè)析構(gòu)函數(shù),不能重載。
如果用戶沒有編寫析構(gòu)函數(shù),編譯系統(tǒng)會(huì)自動(dòng)生成一個(gè)缺省的析構(gòu)函數(shù)(即使自定義了析構(gòu)函數(shù),編譯器也總是會(huì)為我們合成一個(gè)析構(gòu)函數(shù),并且如果自定義了析構(gòu)函數(shù),編譯器在執(zhí)行時(shí)會(huì)先調(diào)用自定義的析構(gòu)函數(shù)再調(diào)用合成的析構(gòu)函數(shù)),它也不進(jìn)行任何操作。所以許多簡(jiǎn)單的類中沒有用顯式的析構(gòu)函數(shù)。
如果一個(gè)類中有指針,且在使用的過程中動(dòng)態(tài)的申請(qǐng)了內(nèi)存,那么最好顯示構(gòu)造析構(gòu)函數(shù)在銷毀類之前,釋放掉申請(qǐng)的內(nèi)存空間,避免內(nèi)存泄漏。
類析構(gòu)順序:1)派生類本身的析構(gòu)函數(shù);2)對(duì)象成員析構(gòu)函數(shù);3)基類析構(gòu)函數(shù)。
13、map和set有什么區(qū)別,分別又是怎么實(shí)現(xiàn)的?
map和set都是C++的關(guān)聯(lián)容器,其底層實(shí)現(xiàn)都是紅黑樹(RB-Tree)。由于 map 和set所開放的各種操作接口,RB-tree 也都提供了,所以幾乎所有的 map 和set的操作行為,都只是轉(zhuǎn)調(diào) RB-tree 的操作行為。
map和set區(qū)別在于:
(1)map中的元素是key-value(關(guān)鍵字—值)對(duì):關(guān)鍵字起到索引的作用,值則表示與索引相關(guān)聯(lián)的數(shù)據(jù);Set與之相對(duì)就是關(guān)鍵字的簡(jiǎn)單集合,set中每個(gè)元素只包含一個(gè)關(guān)鍵字。
(2)set的迭代器是const的,不允許修改元素的值;map允許修改value,但不允許修改key。其原因是因?yàn)閙ap和set是根據(jù)關(guān)鍵字排序來保證其有序性的,如果允許修改key的話,那么首先需要?jiǎng)h除該鍵,然后調(diào)節(jié)平衡,再插入修改后的鍵值,調(diào)節(jié)平衡,如此一來,嚴(yán)重破壞了map和set的結(jié)構(gòu),導(dǎo)致iterator失效,不知道應(yīng)該指向改變前的位置,還是指向改變后的位置。所以STL中將set的迭代器設(shè)置成const,不允許修改迭代器的值;而map的迭代器則不允許修改key值,允許修改value值。
(3)map支持下標(biāo)操作,set不支持下標(biāo)操作。map可以用key做下標(biāo),map的下標(biāo)運(yùn)算符[ ]將關(guān)鍵碼作為下標(biāo)去執(zhí)行查找,如果關(guān)鍵碼不存在,則插入一個(gè)具有該關(guān)鍵碼和mapped_type類型默認(rèn)值的元素至map中,因此下標(biāo)運(yùn)算符[ ]在map應(yīng)用中需要慎用,const_map不能用,只希望確定某一個(gè)關(guān)鍵值是否存在而不希望插入元素時(shí)也不應(yīng)該使用,mapped_type類型沒有默認(rèn)值也不應(yīng)該使用。如果find能解決需要,盡可能用find。
14、C++中類成員的訪問權(quán)限有哪些?
C++通過 public、protected、private 三個(gè)關(guān)鍵字來控制成員變量和成員函數(shù)的訪問權(quán)限,它們分別表示公有的、受保護(hù)的、私有的,被稱為成員訪問限定符。在類的內(nèi)部(定義類的代碼內(nèi)部),無論成員被聲明為 public、protected 還是 private,都是可以互相訪問的,沒有訪問權(quán)限的限制。在類的外部(定義類的代碼之外),只能通過對(duì)象訪問成員,并且通過對(duì)象只能訪問 public 屬性的成員,不能訪問 private、protected 屬性的成員。
private和protected的區(qū)別是,子類的對(duì)象也可以訪問private,但只有本類的對(duì)象可以訪問protected。
15、C++中struct和class的區(qū)別?
在C++中,可以用struct和class定義類,都可以繼承。區(qū)別在于:struct的默認(rèn)繼承權(quán)限和默認(rèn)訪問權(quán)限是public,而class的默認(rèn)繼承權(quán)限和默認(rèn)訪問權(quán)限是private。
16、一個(gè)C++源文件從文本到可執(zhí)行文件經(jīng)歷的過程?
對(duì)于C++源文件,從文本到可執(zhí)行文件一般需要四個(gè)過程:
- 預(yù)處理階段:對(duì)源代碼文件中文件包含關(guān)系(頭文件)、預(yù)編譯語句(宏定義)進(jìn)行分析和替換,生成預(yù)編譯文件。
- 編譯階段:將經(jīng)過預(yù)處理后的預(yù)編譯文件轉(zhuǎn)換成特定匯編代碼,生成匯編文件
- 匯編階段:將編譯階段生成的匯編文件轉(zhuǎn)化成機(jī)器碼,生成可重定位目標(biāo)文件
鏈接階段:將多個(gè)目標(biāo)文件及所需要的庫連接成最終的可執(zhí)行目標(biāo)文件
17、include頭文件的順序以及雙引號(hào)””和尖括號(hào)<>的區(qū)別?
Include頭文件的順序:對(duì)于include的頭文件來說,如果在文件a.h中聲明一個(gè)在文件b.h中定義的變量,而不引用b.h。那么要在a.c文件中引用b.h文件,并且要先引用b.h,后引用a.h,否則匯報(bào)變量類型未聲明錯(cuò)誤。
雙引號(hào)和尖括號(hào)的區(qū)別:編譯器預(yù)處理階段查找頭文件的路徑不一樣。
對(duì)于使用雙引號(hào)包含的頭文件,查找頭文件路徑的順序?yàn)椋?/p>
當(dāng)前頭文件目錄
編譯器設(shè)置的頭文件路徑(編譯器可使用-I顯式指定搜索路徑)
系統(tǒng)變量CPLUS_INCLUDE_PATH/C_INCLUDE_PATH指定的頭文件路徑
對(duì)于使用尖括號(hào)包含的頭文件,查找頭文件的路徑順序?yàn)椋?/p>
編譯器設(shè)置的頭文件路徑(編譯器可使用-I顯式指定搜索路徑)
系統(tǒng)變量CPLUS_INCLUDE_PATH/C_INCLUDE_PATH指定的頭文件路徑
18、malloc的原理,另外brk系統(tǒng)調(diào)用和mmap系統(tǒng)調(diào)用的作用分別是什么?
Malloc函數(shù)用于動(dòng)態(tài)分配內(nèi)存。為了減少內(nèi)存碎片和系統(tǒng)調(diào)用的開銷,malloc其采用內(nèi)存池的方式,先申請(qǐng)大塊內(nèi)存作為堆區(qū),然后將堆區(qū)分為多個(gè)內(nèi)存塊,以塊作為內(nèi)存管理的基本單位。當(dāng)用戶申請(qǐng)內(nèi)存時(shí),直接從堆區(qū)分配一塊合適的空閑塊。Malloc采用隱式鏈表結(jié)構(gòu)將堆區(qū)分成連續(xù)的、大小不一的塊,包含已分配塊和未分配塊;同時(shí)malloc采用顯示鏈表結(jié)構(gòu)來管理所有的空閑塊,即使用一個(gè)雙向鏈表將空閑塊連接起來,每一個(gè)空閑塊記錄了一個(gè)連續(xù)的、未分配的地址。
當(dāng)進(jìn)行內(nèi)存分配時(shí),Malloc會(huì)通過隱式鏈表遍歷所有的空閑塊,選擇滿足要求的塊進(jìn)行分配;當(dāng)進(jìn)行內(nèi)存合并時(shí),malloc采用邊界標(biāo)記法,根據(jù)每個(gè)塊的前后塊是否已經(jīng)分配來決定是否進(jìn)行塊合并。
Malloc在申請(qǐng)內(nèi)存時(shí),一般會(huì)通過brk或者mmap系統(tǒng)調(diào)用進(jìn)行申請(qǐng)。其中當(dāng)申請(qǐng)內(nèi)存小于128K時(shí),會(huì)使用系統(tǒng)函數(shù)brk在堆區(qū)中分配;而當(dāng)申請(qǐng)內(nèi)存大于128K時(shí),會(huì)使用系統(tǒng)函數(shù)mmap在映射區(qū)分配。
19、C++的內(nèi)存管理是怎樣的?
在C++中,虛擬內(nèi)存分為代碼段、數(shù)據(jù)段、BSS段、堆區(qū)、文件映射區(qū)以及棧區(qū)六部分。
代碼段:包括只讀存儲(chǔ)區(qū)和文本區(qū),其中只讀存儲(chǔ)區(qū)存儲(chǔ)字符串常量,文本區(qū)存儲(chǔ)程序的機(jī)器代碼。
數(shù)據(jù)段:存儲(chǔ)程序中已初始化的全局變量和靜態(tài)變量
bss 段:存儲(chǔ)未初始化的全局變量和靜態(tài)變量(局部+全局),以及所有被初始化為0的全局變量和靜態(tài)變量。
堆區(qū):調(diào)用new/malloc函數(shù)時(shí)在堆區(qū)動(dòng)態(tài)分配內(nèi)存,同時(shí)需要調(diào)用delete/free來手動(dòng)釋放申請(qǐng)的內(nèi)存。
映射區(qū):存儲(chǔ)動(dòng)態(tài)鏈接庫以及調(diào)用mmap函數(shù)進(jìn)行的文件映射
棧區(qū):使用??臻g存儲(chǔ)函數(shù)的返回地址、參數(shù)、局部變量、返回值
20、如何判斷內(nèi)存泄漏?
內(nèi)存泄漏通常是由于調(diào)用了malloc/new等內(nèi)存申請(qǐng)的操作,但是缺少了對(duì)應(yīng)的free/delete。為了判斷內(nèi)存是否泄露,我們一方面可以使用linux環(huán)境下的內(nèi)存泄漏檢查工具Valgrind,另一方面我們?cè)趯懘a時(shí)可以添加內(nèi)存申請(qǐng)和釋放的統(tǒng)計(jì)功能,統(tǒng)計(jì)當(dāng)前申請(qǐng)和釋放的內(nèi)存是否一致,以此來判斷內(nèi)存是否泄露。
21、什么時(shí)候會(huì)發(fā)生段錯(cuò)誤?
段錯(cuò)誤通常發(fā)生在訪問非法內(nèi)存地址的時(shí)候,具體來說分為以下幾種情況:
使用野指針
試圖修改字符串常量的內(nèi)容
22、new和malloc的區(qū)別?
1、new分配內(nèi)存按照數(shù)據(jù)類型進(jìn)行分配,malloc分配內(nèi)存按照指定的大小分配;
2、new返回的是指定對(duì)象的指針,而malloc返回的是void*,因此malloc的返回值一般都需要進(jìn)行類型轉(zhuǎn)化。
3、new不僅分配一段內(nèi)存,而且會(huì)調(diào)用構(gòu)造函數(shù),malloc不會(huì)。
4、new分配的內(nèi)存要用delete銷毀,malloc要用free來銷毀;delete銷毀的時(shí)候會(huì)調(diào)用對(duì)象的析構(gòu)函數(shù),而free則不會(huì)。
5、new是一個(gè)操作符可以重載,malloc是一個(gè)庫函數(shù)。
6、malloc分配的內(nèi)存不夠的時(shí)候,可以用realloc擴(kuò)容。擴(kuò)容的原理?new沒用這樣操作。
7、new如果分配失敗了會(huì)拋出bad_malloc的異常,而malloc失敗了會(huì)返回NULL。
8、申請(qǐng)數(shù)組時(shí): new[]一次分配所有內(nèi)存,多次調(diào)用構(gòu)造函數(shù),搭配使用delete[],delete[]多次調(diào)用析構(gòu)函數(shù),銷毀數(shù)組中的每個(gè)對(duì)象。而malloc則只能sizeof(int) * n。
23、A* a = new A; a->i = 10;在內(nèi)核中的內(nèi)存分配上發(fā)生了什么?
1)A *a:a是一個(gè)局部變量,類型為指針,故而操作系統(tǒng)在程序棧區(qū)開辟4/8字節(jié)的空間(0x000m),分配給指針a。
2)new A:通過new動(dòng)態(tài)的在堆區(qū)申請(qǐng)類A大小的空間(0x000n)。
3)a = new A:將指針a的內(nèi)存區(qū)域填入棧中類A申請(qǐng)到的地址的地址。即*(0x000m)=0x000n。
4)a->i:先找到指針a的地址0x000m,通過a的值0x000n和i在類a中偏移offset,得到a->i的地址0x000n + offset,進(jìn)行*(0x000n + offset) = 10的賦值操作,即內(nèi)存0x000n + offset的值是10。
24、一個(gè)類,里面有static,virtual,之類的,來說一說這個(gè)類的內(nèi)存分布?
1、static修飾符
1)static修飾成員變量
對(duì)于非靜態(tài)數(shù)據(jù)成員,每個(gè)類對(duì)象都有自己的拷貝。而靜態(tài)數(shù)據(jù)成員被當(dāng)做是類的成員,無論這個(gè)類被定義了多少個(gè),靜態(tài)數(shù)據(jù)成員都只有一份拷貝,為該類型的所有對(duì)象所共享(包括其派生類)。所以,靜態(tài)數(shù)據(jù)成員的值對(duì)每個(gè)對(duì)象都是一樣的,它的值可以更新。
因?yàn)殪o態(tài)數(shù)據(jù)成員在全局?jǐn)?shù)據(jù)區(qū)分配內(nèi)存,屬于本類的所有對(duì)象共享,所以它不屬于特定的類對(duì)象,在沒有產(chǎn)生類對(duì)象前就可以使用。
2)static修飾成員函數(shù)
與普通的成員函數(shù)相比,靜態(tài)成員函數(shù)由于不是與任何的對(duì)象相聯(lián)系,因此它不具有this指針。從這個(gè)意義上來說,它無法訪問屬于類對(duì)象的非靜態(tài)數(shù)據(jù)成員,也無法訪問非靜態(tài)成員函數(shù),只能調(diào)用其他的靜態(tài)成員函數(shù)。
Static修飾的成員函數(shù),在代碼區(qū)分配內(nèi)存。
2、C++繼承和虛函數(shù)
C++多態(tài)分為靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài)。靜態(tài)多態(tài)是通過重載和模板技術(shù)實(shí)現(xiàn),在編譯的時(shí)候確定。動(dòng)態(tài)多態(tài)通過虛函數(shù)和繼承關(guān)系來實(shí)現(xiàn),執(zhí)行動(dòng)態(tài)綁定,在運(yùn)行的時(shí)候確定。
動(dòng)態(tài)多態(tài)實(shí)現(xiàn)有幾個(gè)條件:
(1) 虛函數(shù);
(2) 一個(gè)基類的指針或引用指向派生類的對(duì)象;
基類指針在調(diào)用成員函數(shù)(虛函數(shù))時(shí),就會(huì)去查找該對(duì)象的虛函數(shù)表。虛函數(shù)表的地址在每個(gè)對(duì)象的首地址。查找該虛函數(shù)表中該函數(shù)的指針進(jìn)行調(diào)用。
每個(gè)對(duì)象中保存的只是一個(gè)虛函數(shù)表的指針,C++內(nèi)部為每一個(gè)類維持一個(gè)虛函數(shù)表,該類的對(duì)象的都指向這同一個(gè)虛函數(shù)表。
虛函數(shù)表中為什么就能準(zhǔn)確查找相應(yīng)的函數(shù)指針呢?因?yàn)樵陬愒O(shè)計(jì)的時(shí)候,虛函數(shù)表直接從基類也繼承過來,如果覆蓋了其中的某個(gè)虛函數(shù),那么虛函數(shù)表的指針就會(huì)被替換,因此可以根據(jù)指針準(zhǔn)確找到該調(diào)用哪個(gè)函數(shù)。
3、virtual修飾符
如果一個(gè)類是局部變量則該類數(shù)據(jù)存儲(chǔ)在棧區(qū),如果一個(gè)類是通過new/malloc動(dòng)態(tài)申請(qǐng)的,則該類數(shù)據(jù)存儲(chǔ)在堆區(qū)。
如果該類是virutal繼承而來的子類,則該類的虛函數(shù)表指針和該類其他成員一起存儲(chǔ)。虛函數(shù)表指針指向只讀數(shù)據(jù)段中的類虛函數(shù)表,虛函數(shù)表中存放著一個(gè)個(gè)函數(shù)指針,函數(shù)指針指向代碼段中的具體函數(shù)。
如果類中成員是virtual屬性,會(huì)隱藏父類對(duì)應(yīng)的屬性。
25、靜態(tài)變量什么時(shí)候初始化?
靜態(tài)變量存儲(chǔ)在虛擬地址空間的數(shù)據(jù)段和bss段,C語言中其在代碼執(zhí)行之前初始化,屬于編譯期初始化。而C++中由于引入對(duì)象,對(duì)象生成必須調(diào)用構(gòu)造函數(shù),因此C++規(guī)定全局或局部靜態(tài)對(duì)象當(dāng)且僅當(dāng)對(duì)象首次用到時(shí)進(jìn)行構(gòu)造。
26、TCP怎么保證可靠性?
TCP保證可靠性:
(1)序列號(hào)、確認(rèn)應(yīng)答、超時(shí)重傳
數(shù)據(jù)到達(dá)接收方,接收方需要發(fā)出一個(gè)確認(rèn)應(yīng)答,表示已經(jīng)收到該數(shù)據(jù)段,并且確認(rèn)序號(hào)會(huì)說明了它下一次需要接收的數(shù)據(jù)序列號(hào)。如果發(fā)送發(fā)遲遲未收到確認(rèn)應(yīng)答,那么可能是發(fā)送的數(shù)據(jù)丟失,也可能是確認(rèn)應(yīng)答丟失,這時(shí)發(fā)送方在等待一定時(shí)間后會(huì)進(jìn)行重傳。這個(gè)時(shí)間一般是2*RTT(報(bào)文段往返時(shí)間)+一個(gè)偏差值。
(2)窗口控制與高速重發(fā)控制/快速重傳(重復(fù)確認(rèn)應(yīng)答)
TCP會(huì)利用窗口控制來提高傳輸速度,意思是在一個(gè)窗口大小內(nèi),不用一定要等到應(yīng)答才能發(fā)送下一段數(shù)據(jù),窗口大小就是無需等待確認(rèn)而可以繼續(xù)發(fā)送數(shù)據(jù)的最大值。如果不使用窗口控制,每一個(gè)沒收到確認(rèn)應(yīng)答的數(shù)據(jù)都要重發(fā)。
使用窗口控制,如果數(shù)據(jù)段1001-2000丟失,后面數(shù)據(jù)每次傳輸,確認(rèn)應(yīng)答都會(huì)不停地發(fā)送序號(hào)為1001的應(yīng)答,表示我要接收1001開始的數(shù)據(jù),發(fā)送端如果收到3次相同應(yīng)答,就會(huì)立刻進(jìn)行重發(fā);但還有種情況有可能是數(shù)據(jù)都收到了,但是有的應(yīng)答丟失了,這種情況不會(huì)進(jìn)行重發(fā),因?yàn)榘l(fā)送端知道,如果是數(shù)據(jù)段丟失,接收端不會(huì)放過它的,會(huì)瘋狂向它提醒......
(3)擁塞控制
如果把窗口定的很大,發(fā)送端連續(xù)發(fā)送大量的數(shù)據(jù),可能會(huì)造成網(wǎng)絡(luò)的擁堵(大家都在用網(wǎng),你在這狂發(fā),吞吐量就那么大,當(dāng)然會(huì)堵),甚至造成網(wǎng)絡(luò)的癱瘓。所以TCP在為了防止這種情況而進(jìn)行了擁塞控制。
慢啟動(dòng):定義擁塞窗口,一開始將該窗口大小設(shè)為1,之后每次收到確認(rèn)應(yīng)答(經(jīng)過一個(gè)rtt),將擁塞窗口大小*2。
擁塞避免:設(shè)置慢啟動(dòng)閾值,一般開始都設(shè)為65536。擁塞避免是指當(dāng)擁塞窗口大小達(dá)到這個(gè)閾值,擁塞窗口的值不再指數(shù)上升,而是加法增加(每次確認(rèn)應(yīng)答/每個(gè)rtt,擁塞窗口大小+1),以此來避免擁塞。
將報(bào)文段的超時(shí)重傳看做擁塞,則一旦發(fā)生超時(shí)重傳,我們需要先將閾值設(shè)為當(dāng)前窗口大小的一半,并且將窗口大小設(shè)為初值1,然后重新進(jìn)入慢啟動(dòng)過程。
快速重傳:在遇到3次重復(fù)確認(rèn)應(yīng)答(高速重發(fā)控制)時(shí),代表收到了3個(gè)報(bào)文段,但是這之前的1個(gè)段丟失了,便對(duì)它進(jìn)行立即重傳。
然后,先將閾值設(shè)為當(dāng)前窗口大小的一半,然后將擁塞窗口大小設(shè)為慢啟動(dòng)閾值+3的大小。
這樣可以達(dá)到:在TCP通信時(shí),網(wǎng)絡(luò)吞吐量呈現(xiàn)逐漸的上升,并且隨著擁堵來降低吞吐量,再進(jìn)入慢慢上升的過程,網(wǎng)絡(luò)不會(huì)輕易的發(fā)生癱瘓。
27、紅黑樹和AVL樹的定義,特點(diǎn),以及二者區(qū)別
平衡二叉樹(AVL樹):
平衡二叉樹又稱為AVL樹,是一種特殊的二叉排序樹。其左右子樹都是平衡二叉樹,且左右子樹高度之差的絕對(duì)值不超過1。一句話表述為:以樹中所有結(jié)點(diǎn)為根的樹的左右子樹高度之差的絕對(duì)值不超過1。將二叉樹上結(jié)點(diǎn)的左子樹深度減去右子樹深度的值稱為平衡因子BF,那么平衡二叉樹上的所有結(jié)點(diǎn)的平衡因子只可能是-1、0和1。只要二叉樹上有一個(gè)結(jié)點(diǎn)的平衡因子的絕對(duì)值大于1,則該二叉樹就是不平衡的。
紅黑樹:
紅黑樹是一種二叉查找樹,但在每個(gè)節(jié)點(diǎn)增加一個(gè)存儲(chǔ)位表示節(jié)點(diǎn)的顏色,可以是紅或黑(非紅即黑)。通過對(duì)任何一條從根到葉子的路徑上各個(gè)節(jié)點(diǎn)著色的方式的限制,紅黑樹確保沒有一條路徑會(huì)比其它路徑長(zhǎng)出兩倍,因此,紅黑樹是一種弱平衡二叉樹,相對(duì)于要求嚴(yán)格的AVL樹來說,它的旋轉(zhuǎn)次數(shù)少,所以對(duì)于搜索,插入,刪除操作較多的情況下,通常使用紅黑樹。
性質(zhì):
1. 每個(gè)節(jié)點(diǎn)非紅即黑
2. 根節(jié)點(diǎn)是黑的;
3. 每個(gè)葉節(jié)點(diǎn)(葉節(jié)點(diǎn)即樹尾端NULL指針或NULL節(jié)點(diǎn))都是黑的;
4. 如果一個(gè)節(jié)點(diǎn)是紅色的,則它的子節(jié)點(diǎn)必須是黑色的。
5. 對(duì)于任意節(jié)點(diǎn)而言,其到葉子點(diǎn)樹NULL指針的每條路徑都包含相同數(shù)目的黑節(jié)點(diǎn);
區(qū)別:
AVL 樹是高度平衡的,頻繁的插入和刪除,會(huì)引起頻繁的rebalance,導(dǎo)致效率下降;紅黑樹不是高度平衡的,算是一種折中,插入最多兩次旋轉(zhuǎn),刪除最多三次旋轉(zhuǎn)。
28、map和unordered_map優(yōu)點(diǎn)和缺點(diǎn)
對(duì)于map,其底層是基于紅黑樹實(shí)現(xiàn)的,優(yōu)點(diǎn)如下:
1)有序性,這是map結(jié)構(gòu)最大的優(yōu)點(diǎn),其元素的有序性在很多應(yīng)用中都會(huì)簡(jiǎn)化很多的操作
2)map的查找、刪除、增加等一系列操作時(shí)間復(fù)雜度穩(wěn)定,都為logn
缺點(diǎn)如下:
1)查找、刪除、增加等操作平均時(shí)間復(fù)雜度較慢,與n相關(guān)
對(duì)于unordered_map來說,其底層是一個(gè)哈希表,優(yōu)點(diǎn)如下:
查找、刪除、添加的速度快,時(shí)間復(fù)雜度為常數(shù)級(jí)O(c)
缺點(diǎn)如下:
因?yàn)閡nordered_map內(nèi)部基于哈希表,以(key,value)對(duì)的形式存儲(chǔ),因此空間占用率高
Unordered_map的查找、刪除、添加的時(shí)間復(fù)雜度不穩(wěn)定,平均為O(c),取決于哈希函數(shù)。極端情況下可能為O(n)
29、Top(K)問題
1、直接全部排序(只適用于內(nèi)存夠的情況)
當(dāng)數(shù)據(jù)量較小的情況下,內(nèi)存中可以容納所有數(shù)據(jù)。則最簡(jiǎn)單也是最容易想到的方法是將數(shù)據(jù)全部排序,然后取排序后的數(shù)據(jù)中的前K個(gè)。
這種方法對(duì)數(shù)據(jù)量比較敏感,當(dāng)數(shù)據(jù)量較大的情況下,內(nèi)存不能完全容納全部數(shù)據(jù),這種方法便不適應(yīng)了。即使內(nèi)存能夠滿足要求,該方法將全部數(shù)據(jù)都排序了,而題目只要求找出top K個(gè)數(shù)據(jù),所以該方法并不十分高效,不建議使用。
2、快速排序的變形 (只使用于內(nèi)存夠的情況)
這是一個(gè)基于快速排序的變形,因?yàn)榈谝环N方法中說到將所有元素都排序并不十分高效,只需要找出前K個(gè)最大的就行。
這種方法類似于快速排序,首先選擇一個(gè)劃分元,將比這個(gè)劃分元大的元素放到它的前面,比劃分元小的元素放到它的后面,此時(shí)完成了一趟排序。如果此時(shí)這個(gè)劃分元的序號(hào)index剛好等于K,那么這個(gè)劃分元以及它左邊的數(shù),剛好就是前K個(gè)最大的元素;如果index > K,那么前K大的數(shù)據(jù)在index的左邊,那么就繼續(xù)遞歸的從index-1個(gè)數(shù)中進(jìn)行一趟排序;如果index < K,那么再從劃分元的右邊繼續(xù)進(jìn)行排序,直到找到序號(hào)index剛好等于K為止。再將前K個(gè)數(shù)進(jìn)行排序后,返回Top K個(gè)元素。這種方法就避免了對(duì)除了Top K個(gè)元素以外的數(shù)據(jù)進(jìn)行排序所帶來的不必要的開銷。
3、最小堆法
這是一種局部淘汰法。先讀取前K個(gè)數(shù),建立一個(gè)最小堆。然后將剩余的所有數(shù)字依次與最小堆的堆頂進(jìn)行比較,如果小于或等于堆頂數(shù)據(jù),則繼續(xù)比較下一個(gè);否則,刪除堆頂元素,并將新數(shù)據(jù)插入堆中,重新調(diào)整最小堆。當(dāng)遍歷完全部數(shù)據(jù)后,最小堆中的數(shù)據(jù)即為最大的K個(gè)數(shù)。
4、分治法
將全部數(shù)據(jù)分成N份,前提是每份的數(shù)據(jù)都可以讀到內(nèi)存中進(jìn)行處理,找到每份數(shù)據(jù)中最大的K個(gè)數(shù)。此時(shí)剩下N*K個(gè)數(shù)據(jù),如果內(nèi)存不能容納N*K個(gè)數(shù)據(jù),則再繼續(xù)分治處理,分成M份,找出每份數(shù)據(jù)中最大的K個(gè)數(shù),如果M*K個(gè)數(shù)仍然不能讀到內(nèi)存中,則繼續(xù)分治處理。直到剩余的數(shù)可以讀入內(nèi)存中,那么可以對(duì)這些數(shù)使用快速排序的變形或者歸并排序進(jìn)行處理。
5、Hash法
如果這些數(shù)據(jù)中有很多重復(fù)的數(shù)據(jù),可以先通過hash法,把重復(fù)的數(shù)去掉。這樣如果重復(fù)率很高的話,會(huì)減少很大的內(nèi)存用量,從而縮小運(yùn)算空間。處理后的數(shù)據(jù)如果能夠讀入內(nèi)存,則可以直接排序;否則可以使用分治法或者最小堆法來處理數(shù)據(jù)。
30、棧和堆的區(qū)別,以及為什么棧要快?
堆和棧的區(qū)別:
堆是由低地址向高地址擴(kuò)展;棧是由高地址向低地址擴(kuò)展
堆中的內(nèi)存需要手動(dòng)申請(qǐng)和手動(dòng)釋放;棧中內(nèi)存是由OS自動(dòng)申請(qǐng)和自動(dòng)釋放,存放著參數(shù)、局部變量等內(nèi)存
堆中頻繁調(diào)用malloc和free,會(huì)產(chǎn)生內(nèi)存碎片,降低程序效率;而棧由于其先進(jìn)后出的特性,不會(huì)產(chǎn)生內(nèi)存碎片
堆的分配效率較低,而棧的分配效率較高
棧的效率高的原因:
棧是操作系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)底層對(duì)棧提供了一系列支持:分配專門的寄存器存儲(chǔ)棧的地址,壓棧和入棧有專門的指令執(zhí)行;而堆是由C/C++函數(shù)庫提供的,機(jī)制復(fù)雜,需要一系列分配內(nèi)存、合并內(nèi)存和釋放內(nèi)存的算法,因此效率較低。
31、寫個(gè)函數(shù)在main函數(shù)執(zhí)行前先運(yùn)行
__attribute((constructor))void before() { printf("before main\n"); }
32、extern“C”的作用?
C++調(diào)用C函數(shù)需要extern C,因?yàn)镃語言沒有函數(shù)重載。
33、STL迭代器刪除元素
1.對(duì)于序列容器vector,deque來說,使用erase(itertor)后,后邊的每個(gè)元素的迭代器都會(huì)失效,但是后邊每個(gè)元素都會(huì)往前移動(dòng)一個(gè)位置,但是erase會(huì)返回下一個(gè)有效的迭代器;
2.對(duì)于關(guān)聯(lián)容器map set來說,使用了erase(iterator)后,當(dāng)前元素的迭代器失效,但是其結(jié)構(gòu)是紅黑樹,刪除當(dāng)前元素的,不會(huì)影響到下一個(gè)元素的迭代器,所以在調(diào)用erase之前,記錄下一個(gè)元素的迭代器即可。
3.對(duì)于list來說,它使用了不連續(xù)分配的內(nèi)存,并且它的erase方法也會(huì)返回下一個(gè)有效的iterator。
34、vector和list的區(qū)別與應(yīng)用有哪些?
1、概念:
1)Vector
連續(xù)存儲(chǔ)的容器,動(dòng)態(tài)數(shù)組,在堆上分配空間
底層實(shí)現(xiàn):數(shù)組
兩倍容量增長(zhǎng):
vector 增加(插入)新元素時(shí),如果未超過當(dāng)時(shí)的容量,則還有剩余空間,那么直接添加到最后(插入指定位置),然后調(diào)整迭代器。
如果沒有剩余空間了,則會(huì)重新配置原有元素個(gè)數(shù)的兩倍空間,然后將原空間元素通過復(fù)制的方式初始化新空間,再向新空間增加元素,最后析構(gòu)并釋放原空間,之前的迭代器會(huì)失效。
性能:
訪問:O(1)
插入:在最后插入(空間夠):很快
在最后插入(空間不夠):需要內(nèi)存申請(qǐng)和釋放,以及對(duì)之前數(shù)據(jù)進(jìn)行拷貝。
在中間插入(空間夠):內(nèi)存拷貝
在中間插入(空間不夠):需要內(nèi)存申請(qǐng)和釋放,以及對(duì)之前數(shù)據(jù)進(jìn)行拷貝。
刪除:在最后刪除:很快
在中間刪除:內(nèi)存拷貝
適用場(chǎng)景:經(jīng)常隨機(jī)訪問,且不經(jīng)常對(duì)非尾節(jié)點(diǎn)進(jìn)行插入刪除。
2、List
動(dòng)態(tài)鏈表,在堆上分配空間,每插入一個(gè)元數(shù)都會(huì)分配空間,每刪除一個(gè)元素都會(huì)釋放空間。
底層:雙向鏈表
性能:
訪問:隨機(jī)訪問性能很差,只能快速訪問頭尾節(jié)點(diǎn)。
插入:很快,一般是常數(shù)開銷
刪除:很快,一般是常數(shù)開銷
適用場(chǎng)景:經(jīng)常插入刪除大量數(shù)據(jù)
2、區(qū)別:
1)vector底層實(shí)現(xiàn)是數(shù)組;list是雙向 鏈表。
2)vector支持隨機(jī)訪問,list不支持。
3)vector是順序內(nèi)存,list不是。
4)vector在中間節(jié)點(diǎn)進(jìn)行插入刪除會(huì)導(dǎo)致內(nèi)存拷貝,list不會(huì)。
5)vector一次性分配好內(nèi)存,不夠時(shí)才進(jìn)行2倍擴(kuò)容;list每次插入新節(jié)點(diǎn)都會(huì)進(jìn)行內(nèi)存申請(qǐng)。
6)vector隨機(jī)訪問性能好,插入刪除性能差;list隨機(jī)訪問性能差,插入刪除性能好。
3、應(yīng)用
vector擁有一段連續(xù)的內(nèi)存空間,因此支持隨機(jī)訪問,如果需要高效的隨即訪問,而不在乎插入和刪除的效率,使用vector。
list擁有一段不連續(xù)的內(nèi)存空間,如果需要高效的插入和刪除,而不關(guān)心隨機(jī)訪問,則應(yīng)使用list。
35、STL里resize和reserve的區(qū)別?
resize():改變當(dāng)前容器內(nèi)含有元素的數(shù)量(size()),eg: vector<int>v; v.resize(len);v的size變?yōu)閘en,如果原來v的size小于len,那么容器新增(len-size)個(gè)元素,元素的值為默認(rèn)為0.當(dāng)v.push_back(3);之后,則是3是放在了v的末尾,即下標(biāo)為len,此時(shí)容器是size為len+1;
reserve():改變當(dāng)前容器的最大容量(capacity),它不會(huì)生成元素,只是確定這個(gè)容器允許放入多少對(duì)象,如果reserve(len)的值大于當(dāng)前的capacity(),那么會(huì)重新分配一塊能存len個(gè)對(duì)象的空間,然后把之前v.size()個(gè)對(duì)象通過copy construtor復(fù)制過來,銷毀之前的內(nèi)存。
36、源碼到可執(zhí)行文件的過程?
1)預(yù)編譯
主要處理源代碼文件中的以“#”開頭的預(yù)編譯指令。處理規(guī)則見下
1、刪除所有的#define,展開所有的宏定義。
2、處理所有的條件預(yù)編譯指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。
3、處理“#include”預(yù)編譯指令,將文件內(nèi)容替換到它的位置,這個(gè)過程是遞歸進(jìn)行的,文件中包含其他文件。
4、刪除所有的注釋,“//”和“/**/”。
5、保留所有的#pragma 編譯器指令,編譯器需要用到他們,如:#pragma once 是為了防止有文件被重復(fù)引用。
6、添加行號(hào)和文件標(biāo)識(shí),便于編譯時(shí)編譯器產(chǎn)生調(diào)試用的行號(hào)信息,和編譯時(shí)產(chǎn)生編譯錯(cuò)誤或警告是能夠顯示行號(hào)。
2)編譯
把預(yù)編譯之后生成的xxx.i或xxx.ii文件,進(jìn)行一系列詞法分析、語法分析、語義分析及優(yōu)化后,生成相應(yīng)的匯編代碼文件。
1、詞法分析:利用類似于“有限狀態(tài)機(jī)”的算法,將源代碼程序輸入到掃描機(jī)中,將其中的字符序列分割成一系列的記號(hào)。
2、語法分析:語法分析器對(duì)由掃描器產(chǎn)生的記號(hào),進(jìn)行語法分析,產(chǎn)生語法樹。由語法分析器輸出的語法樹是一種以表達(dá)式為節(jié)點(diǎn)的樹。
3、語義分析:語法分析器只是完成了對(duì)表達(dá)式語法層面的分析,語義分析器則對(duì)表達(dá)式是否有意義進(jìn)行判斷,其分析的語義是靜態(tài)語義——在編譯期能分期的語義,相對(duì)應(yīng)的動(dòng)態(tài)語義是在運(yùn)行期才能確定的語義。
4、優(yōu)化:源代碼級(jí)別的一個(gè)優(yōu)化過程。
5、目標(biāo)代碼生成:由代碼生成器將中間代碼轉(zhuǎn)換成目標(biāo)機(jī)器代碼,生成一系列的代碼序列——匯編語言表示。
6、目標(biāo)代碼優(yōu)化:目標(biāo)代碼優(yōu)化器對(duì)上述的目標(biāo)機(jī)器代碼進(jìn)行優(yōu)化:尋找合適的尋址方式、使用位移來替代乘法運(yùn)算、刪除多余的指令等。
3)匯編
將匯編代碼轉(zhuǎn)變成機(jī)器可以執(zhí)行的指令(機(jī)器碼文件)。 匯編器的匯編過程相對(duì)于編譯器來說更簡(jiǎn)單,沒有復(fù)雜的語法,也沒有語義,更不需要做指令優(yōu)化,只是根據(jù)匯編指令和機(jī)器指令的對(duì)照表一一翻譯過來,匯編過程有匯編器as完成。經(jīng)匯編之后,產(chǎn)生目標(biāo)文件(與可執(zhí)行文件格式幾乎一樣)xxx.o(Windows下)、xxx.obj(Linux下)。
4)鏈接
將不同的源文件產(chǎn)生的目標(biāo)文件進(jìn)行鏈接,從而形成一個(gè)可以執(zhí)行的程序。鏈接分為靜態(tài)鏈接和動(dòng)態(tài)鏈接:
1、靜態(tài)鏈接:
函數(shù)和數(shù)據(jù)被編譯進(jìn)一個(gè)二進(jìn)制文件。在使用靜態(tài)庫的情況下,在編譯鏈接可執(zhí)行文件時(shí),鏈接器從庫中復(fù)制這些函數(shù)和數(shù)據(jù)并把它們和應(yīng)用程序的其它模塊組合起來創(chuàng)建最終的可執(zhí)行文件。
空間浪費(fèi):因?yàn)槊總€(gè)可執(zhí)行程序中對(duì)所有需要的目標(biāo)文件都要有一份副本,所以如果多個(gè)程序?qū)ν粋€(gè)目標(biāo)文件都有依賴,會(huì)出現(xiàn)同一個(gè)目標(biāo)文件都在內(nèi)存存在多個(gè)副本;
更新困難:每當(dāng)庫函數(shù)的代碼修改了,這個(gè)時(shí)候就需要重新進(jìn)行編譯鏈接形成可執(zhí)行程序。
運(yùn)行速度快:但是靜態(tài)鏈接的優(yōu)點(diǎn)就是,在可執(zhí)行程序中已經(jīng)具備了所有執(zhí)行程序所需要的任何東西,在執(zhí)行的時(shí)候運(yùn)行速度快。
2、動(dòng)態(tài)鏈接:
動(dòng)態(tài)鏈接的基本思想是把程序按照模塊拆分成各個(gè)相對(duì)獨(dú)立部分,在程序運(yùn)行時(shí)才將它們鏈接在一起形成一個(gè)完整的程序,而不是像靜態(tài)鏈接一樣把所有程序模塊都鏈接成一個(gè)單獨(dú)的可執(zhí)行文件。
共享庫:就是即使需要每個(gè)程序都依賴同一個(gè)庫,但是該庫不會(huì)像靜態(tài)鏈接那樣在內(nèi)存中存在多分,副本,而是這多個(gè)程序在執(zhí)行時(shí)共享同一份副本;
更新方便:更新時(shí)只需要替換原來的目標(biāo)文件,而無需將所有的程序再重新鏈接一遍。當(dāng)程序下一次運(yùn)行時(shí),新版本的目標(biāo)文件會(huì)被自動(dòng)加載到內(nèi)存并且鏈接起來,程序就完成了升級(jí)的目標(biāo)。
性能損耗:因?yàn)榘焰溄油七t到了程序運(yùn)行時(shí),所以每次執(zhí)行程序都需要進(jìn)行鏈接,所以性能會(huì)有一定損失。
37、tcp握手為什么兩次不可以?為什么不用四次?
兩次不可以:tcp是全雙工通信,兩次握手只能確定單向數(shù)據(jù)鏈路是可以通信的,并不能保證反向的通信正常
不用四次:
本來握手應(yīng)該和揮手一樣都是需要確認(rèn)兩個(gè)方向都能聯(lián)通的,本來模型應(yīng)該是:
1.客戶端發(fā)送syn0給服務(wù)器
2.服務(wù)器收到syn0,回復(fù)ack(syn0+1)
3.服務(wù)器發(fā)送syn1
4.客戶端收到syn1,回復(fù)ack(syn1+1)
因?yàn)閠cp是全雙工的,上邊的四部確認(rèn)了數(shù)據(jù)在兩個(gè)方向上都是可以正確到達(dá)的,但是2,3步?jīng)]有沒有上下的聯(lián)系,可以將其合并,加快握手效率,所有就變成了3步握手。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
- 這篇文章主要介紹了騰訊公司c++面試小結(jié),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2020-03-02
華為校招 C++崗面試經(jīng)歷總結(jié)【筆試+一面+二面+Offer】
這篇文章主要介紹了華為校招 C++崗面試經(jīng)歷,總結(jié)分析了華為校招C++崗位的筆試題,以及一面、二面到最終拿到Offer的經(jīng)歷與相關(guān)經(jīng)驗(yàn)感想,需要的朋友可以參考下2019-11-28- 這篇文章主要介紹了C++面試常見算法題與參考答案,總結(jié)分析了C++面試中遇到的常見算法題與相應(yīng)的參考答案,需要的朋友可以參考下2019-11-20
- 這篇文章主要介紹了C++必備面試題與參考答案,結(jié)合大量經(jīng)典實(shí)例總結(jié)分析了C++面試過程中經(jīng)常遇到的各種概念、原理、算法相關(guān)問題及參考答案,需要的朋友可以參考下2019-10-31
- 這篇文章主要介紹了C/C++經(jīng)典面試題(附答案),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-10-23
- 這篇文章主要介紹了C/C++求職者必備的20道面試題與參考答案,總結(jié)分析了C/C++相關(guān)的常見概念、原理、知識(shí)點(diǎn)與注意事項(xiàng),需要的朋友可以參考下2019-10-10
騰訊的外包c(diǎn)++面試經(jīng)歷總結(jié)
這篇文章主要介紹了騰訊的外包c(diǎn)++面試經(jīng)歷,總結(jié)記錄了一次騰訊C++面試的經(jīng)歷,包括面試的流程、面試題目與相應(yīng)的參考答案,需要的朋友可以參考下2019-09-29- 這篇文章主要介紹了阿里面試必會(huì)的20道C++面試題與參考答案,涉及C++指針、面向?qū)ο蟆⒑瘮?shù)等相關(guān)特性與使用技巧,需要的朋友可以參考下2019-09-26
- 這篇文章主要介紹了經(jīng)典C++筆試題目與參考答案,總結(jié)分析了C++常見的各種面試題目,包含C++常見知識(shí)點(diǎn)、技術(shù)難點(diǎn)、算法等,需要的朋友可以參考下2019-09-10
- 這篇文章主要介紹了華為筆試算法面試題與參考答案,結(jié)合實(shí)例形式分析了基于C++的字符串轉(zhuǎn)換、判斷、排序等算法相關(guān)操作技巧,需要的朋友可以參考下2019-09-05