如果一個世界都能在彈指一揮間灰飛煙滅,一個人的終結也就應該如露珠滾下草葉般平靜淡然。
常量指針const int *a和指針常量 int * const a 區別
const int a;
int const a;
const int *a;
int *const a;
1.int const a和const int a均表示定義常量類型a。
2.常量指針是一個指針,讀成常量的指針,指向一個只讀變量。如int const *p或const int *p。
3.指針常量是一個不能給改變指向的指針。如int *const p
int main()
{
int b = 3;
int c = 4;
const int *p = &b; //等價於 int const *p = &b;
p = &c; //修飾值,指針可變
//*p = 5;//error 修飾值,值不可變
cout << *p << endl;
int a = 5;
int * const q = &a; //修飾指針
//p = &c;//error修飾指針,指針不可變
*p = 5; //修飾指針,值可變
}
sizeof 和 strlen 的區別
1.sizeof 是一個操作符,strlen 是庫函數。
2.sizeof 的參數可以是數據的類型,也可以是變量,而 strlen 只能以結尾為‘\ 0‘的字符串作參數。
3.編譯器在編譯時就計算出了sizeof 的結果。而strlen 函數必須在運行時才能計算出來。並且 sizeof計算的是分配時數據類型占內存的大小,而 strlen 計算的是字符串實際的長度。
4.數組做 sizeof 的參數不退化,傳遞給 strlen 就退化為指針了。
5.sizeof 操作符的結果類型是 size_t,它在頭文件中 typedef 為 unsigned int 類型。該類型保證能容納實現所建立的最大對象的字節大小。
6.當適用一個結構類型或變量時, sizeof 返回實際的大小;當適用一靜態地空間數組, sizeof 歸還全部數組的尺寸;sizeof 操作符不能返回動態地被分派了的數組或外部的數組的尺寸。
C中的 malloc 和C++中的 new 有什么區別
1.new、delete 是操作符,可以重載,只能在 C++中使用。
2.malloc、free 是函數,可以覆蓋,C、C++中都可以使用。
3.new 可以調用對象的構造函數,對應的 delete 調用相應的析構函數。
4.malloc 僅僅分配內存,free 僅僅回收內存,並不執行構造和析構函數
5.分配成功時:new、delete 返回的是某種數據類型指針,malloc、free 返回的是 void 指針。
6.分配失敗時:malloc返回NULL,new默認拋出異常。
注意:malloc 申請的內存空間要用 free 釋放,而 new 申請的內存空間要用 delete 釋放,不要混用。因為兩者實現的機理不同。
c/c++中static
要理解static,就必須要先理解另一個與之相對的關鍵字auto,其實我們通常聲明的不用static修飾的變量,都是auto的,因為它是默認的。auto的含義是由程序自動控制變量的生存周期,通常指的就是變量在進入其作用域的時候被分配,離開其作用域的時候被釋放;而static就是不auto,變量在程序初始化時被分配,直到程序退出前才被釋放;也就是static是按照程序的生命周期來分配釋放變量的,而不是變量自己的生命周期;所以,像這樣的例子:
void func()
{
int a;
static int b;
}
每一次調用該函數,變量a都是新的,因為它是在進入函數體的時候被分配,退出函數體的時候被釋放,所以多個線程調用該函數,都會擁有各自獨立的變量a,因為它總是要被重新分配的;
而變量b不管你是否使用該函數,在程序初始化時就被分配的了,或者在第一次執行到它的聲明的時候分配(不同的編譯器可能不同),所以多個線程調用該函數的時候,總是訪問同一個變量b,這也是在多線程編程中必須注意的!
1.static可以修飾局部變量(靜態局部變量)、全局變量(靜態全局變量)和函數,被修飾的變量存儲位置在靜態區。
- 對於靜態局部變量,相對於一般局部變量其
生命周期延長,直到程序運行結束而非函數調用結束,且只在第一次被調用時定義; - 對於靜態全局變量,相對於全局變量其可見范圍被縮小,只能在
本文件中可見; - 修飾函數時作用和修飾全局變量相同,都是為了限定訪問域。
2.C++的static除了上述兩種用途,還可以修飾類成員(靜態成員變量和靜態成員函數),靜態成員變量和靜態成員函數不屬於任何一個對象,是所有類實例所共有。
3.static的數據記憶性可以滿足函數在不同調用期的通信,也可以滿足同一個類的多個實例間的通信。
4.未初始化時,static變量默認值為0。其實全局變量也具備這一屬性,因為全局變量也存儲在靜態數據區。在靜態數據區,內存中所有的字節默認值都是0×00,某些時候這一特點可以減少程序員的工作量。
引用和指針區別
1.引用只是別名,不占用具體存儲空間,只有聲明沒有定義;指針是具體變量,需要占用存儲空間。
2.引用在聲明時必須初始化為另一變量,一旦出現必須為typename refname = &varname形式;
3.指針聲明和定義可以分開,可以先只聲明指針變量而不初始化,等用到時再指向具體變量。
4.引用一旦初始化之后就不可以再改變(變量可以被引用為多次,但引用只能作為一個變量引用)而指針變量可以重新指向別的變量。
5.不存在指向空值的引用,必須有具體實體;但是存在指向空值的指針。
數組指針
區別以下指針類型
int *p[10]
int (*p)[10]
int *p(int)
int (*p)(int)
1.int *p[10]表示指針數組,強調數組概念,是一個數組變量,數組大小為10,數組內每個元素都是指向int類型的指針變量。
2.int (*p)[10]表示數組指針,強調是指針,只有一個變量,是指針類型,不過指向的是一個int類型的數組,這個數組大小是10。
3.int p(int)是函數聲明,函數名是p,參數是int類型的,返回值是int 類型的。
4.int (*p)(int)是函數指針,強調是指針,該指針指向的函數具有int類型參數,並且返回值是int類型的。
a和&a有什么區別?
#include <iostream>
using namespace std;
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int *ptr = (int*)(&a+1);
cout << *(ptr-1) << "\t" << *(ptr-2) << endl; // 5 4
cout << "----------------" << endl;
int *p = (int *)(a+1); //2
cout << *p << endl;
}
1.a是數組名,是數組首元素地址,+1表示地址值加上一個int類型的大小,如果a的值是0x00000001,加1操作后變為0x00000005。*(a + 1) = a[1]。
2.&a是數組的指針,其類型為int(*)[5](就是前面提到的數組指針),其加1時,系統會認為是數組首地址加上整個數組的偏移(5個int型變量),值為數組a尾元素后一個元素的地址。
3.若(int )p ,此時輸出 *p時,其值為a[0]的值,因為被轉為int 類型,解引用時按照int類型大小來讀取。
數組名和指針(這里為指向數組首元素的指針)區別?
1.二者均可通過增減偏移量來訪問數組中的元素。
2.數組名不是真正意義上的指針,可以理解為常指針,所以數組名沒有自增、自減等操作。
3.當數組名當做形參傳遞給調用函數后,就失去了原有特性,退化成一般指針,多了自增、自減操作,但sizeof運算符不能再得到原數組的大小了。
野指針是什么?
1.也叫空懸指針,不是指向null的指針,是指向垃圾內存的指針。
2.產生原因及解決辦法:
- 指針變量未及時初始化 => 定義指針變量及時初始化,要么置空。
- 指針free或delete之后沒有及時置空 => 釋放操作后立即置空。
delete和delete[]區別?
1.delete只會調用一次析構函數。
2.delete[]會調用數組中每個元素的析構函數。
3.char *p = new char[32];如果用delete p,會發生什么?
- 因為char是基本數據類型沒有析構函數,而系統也記憶了內存大小。所以用delete不會造成內存泄露。但是這種方法在C++標准里是未定義行為,要避免使用。
- 如果用malloc和free怎么申請和釋放32個字節的buffer。
char ptr = (char)malloc(32);free(ptr);ptr = nullptr; - 如果用free(p) 可以嗎?
這是未定義行為。但是對於基本數據類型是可以使用的。 - 為什么delete []就可以刪除整個數組。
因為系統分配額外空間記錄數組大小。具體來說是在p指針附近(下個位置)存儲了指針指向物體的大小的信息
堆和棧的區別?
1.申請方式不同。
- 棧由系統自動分配。
- 堆由程序員手動分配。
2.申請大小限制不同。
- 棧頂和棧底是之前預設好的,大小固定,可以通過
ulimit -a查看,由ulimit -s修改。 - 堆向高地址擴展,是不連續的內存區域,大小可以靈活調整。
3.申請效率不同。
- 棧由系統分配,速度快,不會有碎片。
- 堆由程序員分配,速度慢,且會有碎片。
4.生長方向不同
- 堆:向着內存地址增加的方向增長
- 棧:向着內存地址減小的方向增長,由內存的高地址向低地址方向增長
簡述C\C++程序編譯的內存情況分配
C、C++中內存分配方式可以分為三種:
1.從靜態存儲區域分配:內存在程序編譯時就已經分配好,這塊內存在程序的整個運行期間都存在。速度快、不容易出錯,因為有系統會善后。例如全局變量,static變量等。
2.在棧上分配:在執行函數時,函數內局部變量的存儲單元都在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。
3.從堆上分配:即動態內存分配。程序在運行的時候用 malloc 或 new 申請任意大小的內存,程序員自己負責在何時用 free 或 delete 釋放內存。動態內存的生存期由程序員決定,使用非常靈活。如果在堆上分配了空間,就有責任回收它,否則運行的程序會出現內存泄漏,另外頻繁地分配和釋放不同大小的堆空間將會產生堆內碎塊。
一個 C、C++程序編譯時內存分為 5 大存儲區:堆區、棧區、全局區、文字常量區、程序代碼區。
變量聲明和定義區別
1.聲明僅僅是把變量的聲明的位置及類型提供給編譯器,並不分配內存空間;定義要在定義的地方為其分配存儲空間。
2.相同變量可以再多處聲明(外部變量extern),但只能在一處定義。
extern關鍵字
1.extern可以置於變量或者函數前,以標示變量或者函數的定義在別的文件中,提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。
2.當它與”C”一起連用時,如:extern “C” void fun(int a, int b); 主要是解決在C++代碼中調用C代碼。告訴編譯器,被extern “C”修飾的變量和函數,按照C語言方式編譯和鏈接。
函數傳參有幾種方式?傳引用和指針有什么區別呢?
1.傳值:形參是實參的拷貝,改變形參的值並不會影響外部實參的值。從被調用函數的角度來說,值傳遞是單向的(實參->形參),參數的值只能傳入,不能傳出。
2.傳指針:形參為指向實參地址的指針,當對形參的指向操作時,就相當於對實參本身進行的操作
3.傳引用:形參相當於是實參的“別名”,對形參的操作其實就是對實參的操作,在引用傳遞過程中,被調函數的形式參數雖然也作為局部變量在棧中開辟了內存空間,但是這時存放的是由主調函數放進來的實參變量的地址。被調函數對形參的任何操作都被處理成間接尋址,即通過棧中存放的地址訪問主調函數中的實參變量。
4.傳指針和傳引用區別:主要是指針和引用的區別。首先語法就不一樣,指針要取值需要能*ptr,引用可以直接取; 另外傳進來的指針可以指向其他位置,但是引用只能綁定在傳進來的固定的值上。
“零值比較”?
1.bool類型:if(flag)
2.int類型:if(flag == 0)
3.指針類型:if(flag == nullptr)
4.float類型:if((flag >= -0.000001) && (flag <= 0. 000001))
同一類的不同對象可以互相賦值嗎?
1.可以,但含有指針成員時需要注意。
2.對比類的對象賦值時深拷貝和淺拷貝。
const的作用
1.修飾普通變量: 該變量值不可更改。頂層/底層const
2.修飾函數參數: 函數形參聲明加const保護某些值在操作過程中不會改變
3.修飾返回值:表明返回的數據是不可修改的
4.修飾成員函數: 類的成員函數加上const限定可以聲明此函數不會更改類對象的內容
宏定義和函數有何區別?
1.宏在編譯時完成替換,之后被替換的文本參與編譯,相當於直接插入了代碼,運行時不存在函數調用,執行起來更快;函數調用在運行時需要跳轉到具體調用函數。
2.宏定義沒有返回值;函數調用具有返回值。
3.宏定義參數沒有類型,不進行類型檢查;函數參數具有類型,需要檢查類型。
宏定義和內聯函數(inline)區別?
1.在使用時,宏只做簡單字符串替換(編譯前)。而內聯函數可以進行參數類型檢查(編譯時),且具有返回值。
2.內聯函數本身是函數,強調函數特性,具有重載等功能。
3.內聯函數可以作為某個類的成員函數,這樣可以使用類的保護成員和私有成員。而當一個表達式涉及到類保護成員或私有成員時,宏就不能實現了。
宏定義和const全局變量的區別?
1.宏替換發生在預編譯階段,屬於文本插入替換;const作用發生於編譯過程中。
宏不檢查類型;const會檢查數據類型。
2.宏定義的數據沒有分配內存空間,只是插入替換掉;const變量分配內存空間。
3.宏不是語句,不在最后加分號;
宏定義和typedef區別?
1.宏主要用於定義常量及書寫復雜的內容;typedef主要用於定義類型別名。
2.宏替換發生在編譯階段之前,屬於文本插入替換;typedef是編譯的一部分。
3.宏不檢查類型;typedef會檢查數據類型。
4.宏不是語句,不在最后加分號;typedef是語句,要加分號標識結束。
5.注意對指針的操作,typedef (char*) p_char和#define p_char char*在使用起來區別巨大。
條件編譯#ifdef, #else, #endif作用?
1.可以通過加#define,並通過#ifdef來判斷,將某些具體模塊包括進要編譯的內容。
2.用於子程序前加#define DEBUG用於程序調試。
3.應對硬件的設置(機器類型等)。
4.條件編譯功能if也可實現,但條件編譯可以減少被編譯語句,從而減少目標程序大小。
volatile有什么作用?
1.volatile定義變量的值是易變的,每次用到這個變量的值的時候都要去重新讀取這個變量的值,而不是讀寄存器內的備份。
2.多線程中被幾個任務共享的變量需要定義為volatile類型。
什么是常引用?
1.常引用可以理解為常量指針,形式為const typename & refname = varname。
2.常引用下,原變量值不會被別名所修改。
3.原變量的值可以通過原名修改。
4.常引用通常用作只讀變量別名或是形參傳遞。
模板的優點和缺點
優點:
1.代碼復用。
2.模板類更加的安全,因其參數類型在編譯時都是已知的。
缺點:
1.模板必須在頭文件中,這樣一旦有所變更需要重編譯所有相關工程;同時也沒有信息隱藏。
2.一些編譯器對template支持不好。
3.模板對每種類型生成額外的代碼,可能導致代碼膨脹。
模板的特化與偏特化
全特化就是限定死模板實現的具體類型,偏特化就是如果這個模板有多個類型,那么只限定其中的一部分。
