1.字符串的末尾'\0'也算一個字符,一個字節。c++
2.使用庫函數strcpy(a,b)進行拷貝b->a操做,strcpy會從源地址一直日後拷貝,直到遇到'\0'爲止。因此拷貝的長度是不定的。若是一直沒有遇到'\0'致使越界訪問非法內存,程序就崩了。git
3.strlen的結果未統計’\0’所佔用的1個字節。 程序員
4.寫出完整的strcpy函數算法
char * strcpy( char *strDest, const char *strSrc ) { assert( (strDest != NULL) && (strSrc != NULL) ); char *address = strDest; while( (*strDest++ = * strSrc++) != ‘\0’ ); return address; }
5.malloc和free要對應使用,防止內存泄漏編程
6.printf(str) 改成 printf("%s",str),不然可以使用格式化 字符串攻擊。printf會把str當成一個格式化字符串(formatstring),printf在其中尋找特殊的格式字符好比"%d"。若是碰到格式字符,一個變量的參數值就從堆棧中取出。很明顯,攻擊者至少能夠經過打印出堆棧中的這些值來偷看程序的內存。設計模式
7.malloc以後應該對內存分配是否成功進行判斷。free(p)以後應該把p=NULL,不然p會變成野指針。api
8.浮點型變量並不精確,因此不可將float變量用「==」或「!=」與數字比較,應該設法轉化成「>=」或「<=」形式。數組
9.數組名的本質以下:
(1)數組名指代一種數據結構,這種數據結構就是數組; 安全
char str[10]; cout << sizeof(str) << endl;
輸出結果爲10,str指代數據結構char[10]。
(2)數組名能夠轉換爲指向其指代實體的指針,並且是一個指針常量,不能做自增、自減等操做,不能被修改;
char str[10];
str++; //編譯出錯,提示str不是左值 數據結構
C++是面向對象的語言,而C是面向過程的結構化編程語言
語法上:
C++具備重載、繼承和多態三種特性
C++相比C,增長多許多類型安全的功能,好比強制類型轉換、
C++支持範式編程,好比模板類、函數模板等
2.使用sizeof看一個指針的大小是4,而引用則是被引用對象的大小;
3.指針能夠被初始化爲NULL,而引用必須被初始化且必須是一個已有對象 的引用;
4.做爲參數傳遞時,指針須要被解引用才能夠對對象進行操做,而直接對引 用的修改都會改變引用所指向的對象;
5.能夠有const指針,可是沒有const引用;
6.指針在使用中能夠指向其它對象,可是引用只能是一個對象的引用,不能 被改變;
7.指針能夠有多級指針(**p),而引用至於一級;
8.指針和引用使用++運算符的意義不同;
9.若是返回動態內存分配的對象或者內存,必須使用指針,引用可能引發內存泄露。
1. auto_ptr(c++98的方案,cpp11已經拋棄)
採用全部權模式。
auto_ptr< string> p1 (new string ("I reigned lonely as a cloud.」)); auto_ptr<string> p2; p2 = p1; //auto_ptr不會報錯.
此時不會報錯,p2剝奪了p1的全部權,可是當程序運行時訪問p1將會報錯。因此auto_ptr的缺點是:存在潛在的內存崩潰問題!
2. unique_ptr(替換auto_ptr)
unique_ptr實現獨佔式擁有或嚴格擁有概念,保證同一時間內只有一個智能指針能夠指向該對象。它對於避免資源泄露(例如「以new建立對象後由於發生異常而忘記調用delete」)特別有用。
採用全部權模式,仍是上面那個例子
unique_ptr<string> p3 (new string ("auto")); //#4 unique_ptr<string> p4; //#5 p4 = p3;//此時會報錯!!
若是確實想執行相似的操做,要安全的重用這種指針,可給它賦新值。C++有一個標準庫函數std::move(),讓你可以將一個unique_ptr賦給另外一個。例如:
unique_ptr<string> ps1, ps2; ps1 = demo("hello"); ps2 = move(ps1); ps1 = demo("alexia"); cout << *ps2 << *ps1 << endl;
3. shared_ptr
shared_ptr實現共享式擁有概念。多個智能指針能夠指向相同對象,該對象和其相關資源會在「最後一個引用被銷燬」時候釋放。從名字share就能夠看出了資源能夠被多個指針共享,它使用計數機制來代表資源被幾個指針共享。能夠經過成員函數use_count()來查看資源的全部者個數。除了能夠經過new來構造,還能夠經過傳入auto_ptr, unique_ptr,weak_ptr來構造。當咱們調用release()時,當前指針會釋放資源全部權,計數減一。當計數等於0時,資源會被釋放。
shared_ptr 是爲了解決 auto_ptr 在對象全部權上的侷限性(auto_ptr 是獨佔的), 在使用引用計數的機制上提供了能夠共享全部權的智能指針。
成員函數:
use_count 返回引用計數的個數
unique 返回是不是獨佔全部權( use_count 爲 1)
swap 交換兩個 shared_ptr 對象(即交換所擁有的對象)
reset 放棄內部對象的全部權或擁有對象的變動, 會引發原有對象的引用計數的減小
get 返回內部對象(指針), 因爲已經重載了()方法, 所以和直接使用對象是同樣的.如 shared_ptr<int> sp(new int(1)); sp 與 sp.get()是等價的
4. weak_ptr
weak_ptr 是一種不控制對象生命週期的智能指針, 它指向一個 shared_ptr 管理的對象. 進行該對象的內存管理的是那個強引用的 shared_ptr. weak_ptr只是提供了對管理對象的一個訪問手段。weak_ptr 設計的目的是爲配合 shared_ptr 而引入的一種智能指針來協助 shared_ptr 工做, 它只能夠從一個 shared_ptr 或另外一個 weak_ptr 對象構造, 它的構造和析構不會引發引用記數的增長或減小。weak_ptr是用來解決shared_ptr相互引用時的死鎖問題,若是說兩個shared_ptr相互引用,那麼這兩個指針的引用計數永遠不可能降低爲0,資源永遠不會釋放。它是對對象的一種弱引用,不會增長對象的引用計數,和shared_ptr之間能夠相互轉化,shared_ptr能夠直接賦值給它,它能夠經過調用lock函數來得到shared_ptr。
注意的是咱們不能經過weak_ptr直接訪問對象的方法,好比B對象中有一個方法print(),咱們不能這樣訪問,pa->pb_->print(); 英文pb_是一個weak_ptr,應該先把它轉化爲shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();
指針 |
數組 |
保存數據的地址 |
保存數據 |
間接訪問數據,首先得到指針的內容,而後將其做爲地址,從該地址中提取數據 |
直接訪問數據, |
一般用於動態的數據結構 |
一般用於固定數目且數據類型相同的元素 |
經過Malloc分配內存,free釋放內存 |
隱式的分配和刪除 |
一般指向匿名數據,操做匿名函數 |
自身即爲數據名 |
虛表是屬於類的,而不是屬於某個具體的對象,一個類只須要一個虛表便可。同一個類的全部對象都使用同一個虛表。
爲了指定對象的虛表,對象內部包含一個虛表的指針,來指向本身所使用的虛表。爲了讓每一個包含虛表的類的對象都擁有一個虛表指針,編譯器在類中添加了一個指針,*__vptr,用來指向虛表。這樣,當類的對象在建立時便擁有了這個指針,且這個指針的值會自動被設置爲指向類的虛表。
多繼承狀況下,派生類中有多個虛函數表,虛函數的排列方式和繼承的順序一致。派生類重寫函數將會覆蓋全部虛函數表的同名內容,派生類自定義新的虛函數將會在第一個類的虛函數表的後面進行擴充。
類析構順序:1)派生類自己的析構函數;2)對象成員析構函數;3)基類析構函數。
C++默認的析構函數不是虛函數是由於虛函數須要額外的虛函數表和虛表指針,佔用額外的內存。而對於不會被繼承的類來講,其析構函數若是是虛函數,就會浪費內存。所以C++默認的析構函數不是虛函數,而是隻有當須要看成父類時,設置爲虛函數。
函數指針是指向函數的指針變量。
函數指針自己首先是一個指針變量,該指針變量指向一個具體的函數。這正如用指針變量可指向整型變量、字符型、數組同樣,這裏是指向函數。
C在編譯時,每個函數都有一個入口地址,該入口地址就是函數指針所指向的地址。有了指向函數的指針變量後,可用該指針變量調用函數,就如同用指針變量可引用其餘類型變量同樣,在這些概念上是大致一致的。
二、用途:
調用函數和作函數的參數,好比回調函數。
三、示例:
char * fun(char * p) {…} // 函數fun char * (*pf)(char * p); // 函數指針pf pf = fun; // 函數指針pf指向函數fun pf(p); // 經過函數指針pf調用函數fun
#include <sys/types.h> #include <unistd.h> pid_t fork(void);
成功調用fork( )會建立一個新的進程,它幾乎與調用fork( )的進程如出一轍,這兩個進程都會繼續運行。在子進程中,成功的fork( )調用會返回0。在父進程中fork( )返回子進程的pid。若是出現錯誤,fork( )返回一個負值。
new運算分兩個階段:(1)調用::operator new配置內存;(2)調用對象構造函數構造對象內容
delete運算分兩個階段:(1)調用對象希構函數;(2)掉員工::operator delete釋放內存
爲了精密分工,STL allocator將兩個階段操做區分開來:內存配置有alloc::allocate()負責,內存釋放由alloc::deallocate()負責;對象構造由::construct()負責,對象析構由::destroy()負責。
同時爲了提高內存管理的效率,減小申請小內存形成的內存碎片問題,SGI STL採用了兩級配置器,當分配的空間大小超過128B時,會使用第一級空間配置器;當分配的空間大小小於128B時,將使用第二級空間配置器。第一級空間配置器直接使用malloc()、realloc()、free()函數進行內存空間的分配和釋放,而第二級空間配置器採用了內存池技術,經過空閒鏈表來管理內存。
另外,class還能夠定義模板類形參,好比template <class T, int i>。
//公有繼承 對象訪問 成員訪問 public --> public Y Y protected --> protected N Y private --> private N N //保護繼承 對象訪問 成員訪問 public --> protected N Y protected --> protected N Y private --> protected N N //私有繼承 對象訪問 成員訪問 public --> private N Y protected --> private N Y private --> private N N
預處理階段:對源代碼文件中文件包含關係(頭文件)、預編譯語句(宏定義)進行分析和替換,生成預編譯文件。
編譯階段:將通過預處理後的預編譯文件轉換成特定彙編代碼,生成彙編文件
彙編階段:將編譯階段生成的彙編文件轉化成機器碼,生成可重定位目標文件
連接階段:將多個目標文件及所須要的庫鏈接成最終的可執行目標文件
Include頭文件的順序:對於include的頭文件來講,若是在文件a.h中聲明一個在文件b.h中定義的變量,而不引用b.h。那麼要在a.c文件中引用b.h文件,而且要先引用b.h,後引用a.h,不然彙報變量類型未聲明錯誤。
雙引號和尖括號的區別:編譯器預處理階段查找頭文件的路徑不同。
對於使用雙引號包含的頭文件,查找頭文件路徑的順序爲:
當前頭文件目錄
編譯器設置的頭文件路徑(編譯器可以使用-I顯式指定搜索路徑)
系統變量CPLUS_INCLUDE_PATH/C_INCLUDE_PATH指定的頭文件路徑
對於使用尖括號包含的頭文件,查找頭文件的路徑順序爲:
編譯器設置的頭文件路徑(編譯器可以使用-I顯式指定搜索路徑)
系統變量CPLUS_INCLUDE_PATH/C_INCLUDE_PATH指定的頭文件路徑
Malloc函數用於動態分配內存。爲了減小內存碎片和系統調用的開銷,malloc其採用內存池的方式,先申請大塊內存做爲堆區,而後將堆區分爲多個內存塊,以塊做爲內存管理的基本單位。當用戶申請內存時,直接從堆區分配一塊合適的空閒塊。Malloc採用隱式鏈表結構將堆區分紅連續的、大小不一的塊,包含已分配塊和未分配塊;同時malloc採用顯示鏈表結構來管理全部的空閒塊,即便用一個雙向鏈表將空閒塊鏈接起來,每個空閒塊記錄了一個連續的、未分配的地址。
當進行內存分配時,Malloc會經過隱式鏈表遍歷全部的空閒塊,選擇知足要求的塊進行分配;當進行內存合併時,malloc採用邊界標記法,根據每一個塊的先後塊是否已經分配來決定是否進行塊合併。
Malloc在申請內存時,通常會經過brk或者mmap系統調用進行申請。其中當申請內存小於128K時,會使用系統函數brk在堆區中分配;而當申請內存大於128K時,會使用系統函數mmap在映射區分配。
在C++中,虛擬內存分爲代碼段、數據段、BSS段、堆區、文件映射區以及棧區六部分。
代碼段:包括只讀存儲區和文本區,其中只讀存儲區存儲字符串常量,文本區存儲程序的機器代碼。
數據段:存儲程序中已初始化的全局變量和靜態變量
bss 段:存儲未初始化的全局變量和靜態變量(局部+全局),以及全部被初始化爲0的全局變量和靜態變量。
堆區:調用new/malloc函數時在堆區動態分配內存,同時須要調用delete/free來手動釋放申請的內存。
映射區:存儲動態連接庫以及調用mmap函數進行的文件映射
棧:使用棧空間存儲函數的返回地址、參數、局部變量、返回值
C++/C的內存分配
使用野指針
試圖修改字符串常量的內容
內存泄漏的分類:
1. 堆內存泄漏 (Heap leak)。對內存指的是程序運行中根據須要分配經過malloc,realloc new等從堆中分配的一塊內存,再是完成後必須經過調用對應的 free或者delete 刪掉。若是程序的設計的錯誤致使這部份內存沒有被釋放,那麼此後這塊內存將不會被使用,就會產生Heap Leak.
2. 系統資源泄露(Resource Leak)。主要指程序使用系統分配的資源好比 Bitmap,handle ,SOCKET等沒有使用相應的函數釋放掉,致使系統資源的浪費,嚴重可致使系統效能下降,系統運行不穩定。
3. 沒有將基類的析構函數定義爲虛函數。當基類指針指向子類對象時,若是基類的析構函數不是virtual,那麼子類的析構函數將不會被調用,子類的資源沒有正確是釋放,所以形成內存泄露。
二、new返回的是指定對象的指針,而malloc返回的是void*,所以malloc的返回值通常都須要進行類型轉化。
三、new不只分配一段內存,並且會調用構造函數,malloc不會。
四、new分配的內存要用delete銷燬,malloc要用free來銷燬;delete銷燬的時候會調用對象的析構函數,而free則不會。
五、new是一個操做符能夠重載,malloc是一個庫函數。
六、malloc分配的內存不夠的時候,能夠用realloc擴容。擴容的原理?new沒用這樣操做。
七、new若是分配失敗了會拋出bad_malloc的異常,而malloc失敗了會返回NULL。
八、申請數組時: new[]一次分配全部內存,屢次調用構造函數,搭配使用delete[],delete[]屢次調用析構函數,銷燬數組中的每一個對象。而malloc則只能sizeof(int) * n。
1)新建共享內存shmget
int shmget(key_t key,size_t size,int shmflg);
key:共享內存鍵值,能夠理解爲共享內存的惟一性標記。
size:共享內存大小
shmflag:建立進程和其餘進程的讀寫權限標識。
返回值:相應的共享內存標識符,失敗返回-1
2)鏈接共享內存到當前進程的地址空間shmat
void *shmat(int shm_id,const void *shm_addr,int shmflg);
shm_id:共享內存標識符
shm_addr:指定共享內存鏈接到當前進程的地址,一般爲0,表示由系統來選擇。
shmflg:標誌位
返回值:指向共享內存第一個字節的指針,失敗返回-1
3)當前進程分離共享內存shmdt
int shmdt(const void *shmaddr);
4)控制共享內存shmctl
和信號量的semctl函數相似,控制共享內存
int shmctl(int shm_id,int command,struct shmid_ds *buf);
shm_id:共享內存標識符
command: 有三個值
IPC_STAT:獲取共享內存的狀態,把共享內存的shmid_ds結構複製到buf中。
IPC_SET:設置共享內存的狀態,把buf複製到共享內存的shmid_ds結構。
IPC_RMID:刪除共享內存
buf:共享內存管理結構體。
平衡二叉樹又稱爲AVL樹,是一種特殊的二叉排序樹。其左右子樹都是平衡二叉樹,且左右子樹高度之差的絕對值不超過1。一句話表述爲:以樹中全部結點爲根的樹的左右子樹高度之差的絕對值不超過1。將二叉樹上結點的左子樹深度減去右子樹深度的值稱爲平衡因子BF,那麼平衡二叉樹上的全部結點的平衡因子只多是-一、0和1。只要二叉樹上有一個結點的平衡因子的絕對值大於1,則該二叉樹就是不平衡的。
紅黑樹:
紅黑樹是一種二叉查找樹,但在每一個節點增長一個存儲位表示節點的顏色,能夠是紅或黑(非紅即黑)。經過對任何一條從根到葉子的路徑上各個節點着色的方式的限制,紅黑樹確保沒有一條路徑會比其它路徑長出兩倍,所以,紅黑樹是一種弱平衡二叉樹,相對於要求嚴格的AVL樹來講,它的旋轉次數少,因此對於搜索,插入,刪除操做較多的狀況下,一般使用紅黑樹。
性質:
1. 每一個節點非紅即黑
2. 根節點是黑的;
3. 每一個葉節點(葉節點即樹尾端NULL指針或NULL節點)都是黑的;
4. 若是一個節點是紅色的,則它的子節點必須是黑色的。
5. 對於任意節點而言,其到葉子點樹NULL指針的每條路徑都包含相同數目的黑節點;
區別:
AVL 樹是高度平衡的,頻繁的插入和刪除,會引發頻繁的rebalance,致使效率降低;紅黑樹不是高度平衡的,算是一種折中,插入最多兩次旋轉,刪除最多三次旋轉。
紅黑樹較AVL樹的優勢:
AVL 樹是高度平衡的,頻繁的插入和刪除,會引發頻繁的rebalance,致使效率降低;紅黑樹不是高度平衡的,算是一種折中,插入最多兩次旋轉,刪除最多三次旋轉。
因此紅黑樹在查找,插入刪除的性能都是O(logn),且性能穩定,因此STL裏面不少結構包括map底層實現都是使用的紅黑樹。
須要無序容器,快速查找刪除,不擔憂略高的內存時用unordered_map;有序容器穩定查找刪除效率,內存很在乎時候用map。
對於map,其底層是基於紅黑樹實現的,優勢以下:
1)有序性,這是map結構最大的優勢,其元素的有序性在不少應用中都會簡化不少的操做
2)map的查找、刪除、增長等一系列操做時間複雜度穩定,都爲logn
缺點以下:
1)查找、刪除、增長等操做平均時間複雜度較慢,與n相關
對於unordered_map來講,其底層是一個哈希表,優勢以下:
查找、刪除、添加的速度快,時間複雜度爲常數級O(c)
缺點以下:
由於unordered_map內部基於哈希表,以(key,value)對的形式存儲,所以空間佔用率高
Unordered_map的查找、刪除、添加的時間複雜度不穩定,平均爲O(c),取決於哈希函數。極端狀況下可能爲O(n)
堆是由低地址向高地址擴展;棧是由高地址向低地址擴展
堆中的內存須要手動申請和手動釋放;棧中內存是由OS自動申請和自動釋放,存放着參數、局部變量等內存
堆中頻繁調用malloc和free,會產生內存碎片,下降程序效率;而棧因爲其先進後出的特性,不會產生內存碎片
堆的分配效率較低,而棧的分配效率較高
棧的效率高的緣由:
棧是操做系統提供的數據結構,計算機底層對棧提供了一系列支持:分配專門的寄存器存儲棧的地址,壓棧和入棧有專門的指令執行;而堆是由C/C++函數庫提供的,機制複雜,須要一些列分配內存、合併內存和釋放內存的算法,所以效率較低。
棧由系統自動分配和管理,堆由程序員手動分配和管理。
2)效率:
棧由系統分配,速度快,不會有內存碎片。
堆由程序員分配,速度較慢,可能因爲操做不當產生內存碎片。
3)擴展方向
棧從高地址向低地址進行擴展,堆由低地址向高地址進行擴展。
4)程序局部變量是使用的棧空間,new/malloc動態申請的內存是堆空間,函數調用時會進行形參和返回值的壓棧出棧,也是用的棧空間。
數組的優勢:
1. 隨機訪問性強
2. 查找速度快
數組的缺點:
1. 插入和刪除效率低
2. 可能浪費內存
3. 內存空間要求高,必須有足夠的連續內存空間。
4. 數組大小固定,不能動態拓展
鏈表的優勢:
1. 插入刪除速度快
2. 內存利用率高,不會浪費內存
3. 大小沒有固定,拓展很靈活。
鏈表的缺點:
不能隨機查找,必須從第一個開始遍歷,查找效率低
開放定址法: 當發生地址衝突時,按照某種方法繼續探測哈希表中的其餘存儲單元,直到找到空位置爲止。
再哈希法:當發生哈希衝突時使用另外一個哈希函數計算地址值,直到衝突再也不發生。這種方法不易產生彙集,可是增長計算時間,同時須要準備許多哈希函數。
鏈地址法:將全部哈希值相同的Key經過鏈表存儲。key按順序插入到鏈表中
創建公共溢出區:採用一個溢出表存儲產生衝突的關鍵字。若是公共溢出區還產生衝突,再採用處理衝突方法處理。
最長公共子串長度就爲max{c[i][j]}了