關于c++的一些知識點

內存分類

在C++中菜秦,內存分成5個區(qū),他們分別是堆尔店、棧褪尝、自由存儲區(qū)、全局/靜態(tài)存儲區(qū)和常量存儲區(qū)避诽。

  • 棧:在執(zhí)行函數(shù)時沙庐,函數(shù)內局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結束時這些存儲單元自動被釋放棉安。棧內存分配運算內置于處理器的指令集中贡耽,效率很高鹊汛,但是分配的內存容量有限刁憋。
  • 堆:就是那些由 new分配的內存塊滥嘴,他們的釋放編譯器不去管若皱,由我們的應用程序去控制尘颓,一般一個new就要對應一個 delete疤苹。如果程序員沒有釋放掉痰催,那么在程序結束后,操作系統(tǒng)會自動回收逸吵。
  • 自由存儲區(qū):就是那些由malloc等分配的內存塊扫皱,他和堆是十分相似的捷绑,不過它是用free來結束自己的生命的粹污。
  • 全局/靜態(tài)存儲區(qū):全局變量和靜態(tài)變量被分配到同一塊內存中壮吩,在以前的C語言中加缘,全局變量又分為初始化的和未初始化的拣宏,在C++里面沒有這個區(qū)分了勋乾,他們共同占用同一塊內存區(qū)嗡善。
  • 常量存儲區(qū):這是一塊比較特殊的存儲區(qū)滤奈,他們里面存放的是常量蜒程,不允許修改伺帘。

堆棧區(qū)別

void f() { int* p=new int[5]; }

這條短短的一句話就包含了堆與棧伪嫁,看到new张咳,我們首先就應該想到脚猾,我們分配了一塊堆內存,那么指針p呢砰奕?他分配的是一塊棧內存军援,所以這句話的意思就是:在棧內存中存放了一個指向一塊堆內存的指針p称勋。在程序會先確定在堆中分配內存的大小赡鲜,然后調用operator new分配內存,然后返回這塊內存的首地址醉鳖,放入棧中哮内,

主要的區(qū)別由以下幾點:

  1. 管理方式不同
    棧由編譯器自動釋放北发,堆必須由我們手動釋放琳拨,或者程序結束后系統(tǒng)回收狱庇。
  2. 空間大小不同
    堆一般沒有限制(32位系統(tǒng)理論是4G)颜启,棧有限制很小
  3. 能否產生碎片不同
    堆因為頻繁的new缰盏、delete導致地址不連續(xù)口猜,產生碎片降低程序效率暮的。棧因為數(shù)據(jù)結構設計淌实,先進后出拆祈,不存在此問題放坏。
  4. 生長方向不同
    對于堆來講,生長方向是向上的钧敞,也就是向著內存地址增加的方向溉苛;對于棧來講愚战,它的生長方向是向下的寂玲,是向著內存地址減小的方向增長。
  5. 分配方式不同
    堆都是動態(tài)分配的想许,沒有靜態(tài)分配的堆流纹。棧有2種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的较雕,比如局部變量的分配挚币。動態(tài)分配由alloca函數(shù)進行分配妆毕,但是棧的動態(tài)分配和堆是不同的笛粘,他的動態(tài)分配是由編譯器進行釋放薪前,無需我們手工實現(xiàn)示括。
  6. 分配效率不同
    棧是機器系統(tǒng)提供的數(shù)據(jù)結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址鳍侣,壓棧出棧都有專門的指令執(zhí)行倚聚,這就決定了棧的效率比較高秉沼。堆則是C/C++函數(shù)庫提供的唬复,它的機制是很復雜的敞咧,例如為了分配一塊內存休建,庫函數(shù)會按照一定的算法(具體的算法可以參考數(shù)據(jù)結構/操作系統(tǒng))在堆內存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由于內存碎片太多)茵烈,就有可能調用系統(tǒng)功能去增加程序數(shù)據(jù)段的內存空間呜投,這樣就有機會分到足夠大小的內存仑荐,然后進行返回粘招。顯然洒扎,堆的效率比棧要低得多逊笆。

內存錯誤的幾種姿勢及對策

  1. 內存分配未成功难裆,卻使用了它。需要使用assert(p!=NULL)或if (p!=NULL)進行防錯
  2. 內存分配雖然成功褂痰,但是尚未初始化就引用它缩歪。需要對分配的內存進行初始化匪蝙。
  3. 內存分配成功并且已經(jīng)初始化逛球,但操作越過了內存的邊界颤绕。
  4. 忘記了釋放內存奥务,造成內存泄露氯葬。需要配套使用malloc和free帚称,new和delete
  5. 釋放了內存卻繼續(xù)使用它世杀。
  • 程序中的對象調用關系過于復雜肝集,實在難以搞清楚某個對象究竟是否已經(jīng)釋放了內存杏瞻,此時應該重新設計數(shù)據(jù)結構捞挥,從根本上解決對象管理的混亂局面砌函。
  • 函數(shù)的return語句寫錯了讹俊,注意不要返回指向“棧內存”的“指針”或者“引用”仍劈,因為該內存在函數(shù)體結束時被自動銷毀贩疙。
  • 使用free或delete釋放了內存后这溅,沒有將指針設置為NULL芍躏。導致產生“野指針”对竣。

野指針(也就是指向不可用內存區(qū)域的指針)

如何避免:

  • 規(guī)則1:用malloc或new申請內存之后否纬,應該立即檢查指針值是否為NULL临燃。防止使用指針值為NULL的內存膜廊。
  • 規(guī)則2:不要忘記為數(shù)組和動態(tài)內存賦初值爪瓜。防止將未被初始化的內存作為右值使用铆铆。
  • 規(guī)則3:避免數(shù)組或指針的下標越界薄货,特別要當心發(fā)生“多1”或者“少1”操作谅猾。
  • 規(guī)則4:動態(tài)內存的申請與釋放必須配對,防止內存泄漏贼涩。
  • 規(guī)則5:用free或delete釋放了內存之后遥倦,立即將指針設置為NULL袒哥,防止產生“野指針”堡称。

指針和數(shù)組

  • 修改內容
    指針和數(shù)組對字符串的操作很像却紧,但是有區(qū)別晓殊,它們在內存中的存儲區(qū)域不同
    如下,字符數(shù)組存儲在全局數(shù)據(jù)區(qū)或棧區(qū)介汹,內容可修改嘹承。而字符串存在常量區(qū)如庭,只讀不能改豪娜。
char a[] = “hello”;
a[0] = ‘X’;
cout << a << endl;
char *p = “world”; // 注意p指向常量字符串
p[0] = ‘X’; // 編譯器不能發(fā)現(xiàn)該錯誤
cout << p << endl;
  • 內容復制和比較
    不能對數(shù)組名進行直接復制與比較,應該用strcpy和strcmp
// 數(shù)組…    
char a[] = "hello";
char b[10];
strcpy(b, a); // 不能用 b = a;
if(strcmp(b, a) == 0) // 不能用 if (b == a)
…
// 指針…
//tip:字符串是特殊情況卖擅,測試請使用int a[]={1惩阶,2断楷,3冬筒,4恐锣,5}
int len = strlen(a);
char *p = (char *)malloc(sizeof(char)*(len+1));
strcpy(p,a); // 不要用 p = a;因為p=a只是把a的地址賦給p,而不是內容舞痰;
if(strcmp(p, a) == 0) // 不要用 if (p == a)土榴,p==a也是比較的地址,非內容
…
  • 計算內存容量
char a[] = "hello world";
char *p = a;
// 字符數(shù)組用sizeof得到數(shù)組的總容量响牛,注意玷禽,若a[10]則是10,動態(tài)指定的會加上一個字節(jié)的空字符
cout<< sizeof(a) << endl; // 12字節(jié)
//p是一個指針,sizeof(p)得到的是對sizeof(char *)的容量矢赁,不是p所指的內存容量。
cout<< sizeof(p) << endl; // 4字節(jié)
注意當數(shù)組作為函數(shù)的參數(shù)進行傳遞時贬丛,該數(shù)組自動退化為同類型的指針坯台。如下示例中,不論數(shù)組a的容量是多少瘫寝,sizeof(a)始終等于sizeof(char *)蜒蕾。
void Func(char a[100]){
    cout<< sizeof(a) << endl; // 4字節(jié)而不是100字節(jié)
}

指針作為參數(shù)傳遞

如果函數(shù)的參數(shù)是一個指針,不要指望用該指針去申請動態(tài)內存焕阿。如下示例中咪啡,Test函數(shù)的語句GetMemory(str, 200)并沒有使str獲得期望的內存,
str依舊是NULL暮屡,為什么撤摸?

void GetMemory(char *p, int num){
    p = (char *)malloc(sizeof(char) * num);
}
void Test(void){
    char *str = NULL;
    GetMemory(str, 100); // str 仍然為 NULL
    strcpy(str, "hello"); // 運行錯誤
}

毛病出在函數(shù)GetMemory中。編譯器總是要為函數(shù)的每個參數(shù)制作臨時副本褒纲,指針參數(shù)p的副本是 _p准夷,編譯器使 _p=p。如果函數(shù)體內的程序修改了_p的內容莺掠,就導致參數(shù)p的內容作相應的修改衫嵌。這就是指針可以用作輸出參數(shù)的原因。在本例中彻秆,_p申請了新的內存楔绞,只是把 _p所指的內存地址改變了结闸,但是p絲毫未變。所以函數(shù)GetMemory并不能輸出任何東西酒朵。事實上桦锄,每執(zhí)行一次GetMemory就會泄露一塊內存,因為沒有用free釋放內存蔫耽。
如果非得要用指針參數(shù)去申請內存结耀,那么應該改用“指向指針的指針”,見示例:

void GetMemory2(char **p, int num){
    *p = (char *)malloc(sizeof(char) * num);
}
void Test2(void){
    char *str = NULL;
    GetMemory2(&str, 100); // 注意參數(shù)是 &str匙铡,而不是str
    strcpy(str, "hello");
    cout<< str << endl;
    
    free(str);
}

由于“指向指針的指針”這個概念不容易理解饼记,我們可以用函數(shù)返回值來傳遞動態(tài)內存。這種方法更加簡單慰枕,見示例:

char *GetMemory3(int num){
    char *p = (char *)malloc(sizeof(char) * num);
    return p;
}
void Test3(void){
    char *str = NULL;
    str = GetMemory3(100);
    
    strcpy(str, "hello");
    cout<< str << endl;
    free(str);
}

用函數(shù)返回值來傳遞動態(tài)內存這種方法雖然好用具则,但是常常有人把return語句用錯了。這里強調不要用return語句返回指向“棧內存”的指針具帮,因為該內存在函數(shù)結束時自動消亡博肋,見示例:

char *GetString(void){
    char p[] = "hello world";
    return p; // 編譯器將提出警告
}
void Test4(void){
    char *str = NULL;
    str = GetString(); // str 的內容是垃圾
    cout<< str << endl;
}

用調試器逐步跟蹤Test4,發(fā)現(xiàn)執(zhí)行str = GetString語句后str不再是NULL指針蜂厅,但是str的內容不是“hello world”而是垃圾匪凡。
如果把上述示例改寫成如下示例,會怎么樣掘猿?

char *GetString2(void){
    char *p = "hello world";
    return p;
}
void Test5(void){
    char *str = NULL;
    str = GetString2();
    cout<< str << endl;
}

野指針

“野指針”不是NULL指針病游,是指向“垃圾”內存的指針。人們一般不會錯用NULL指針稠通,因為用if語句很容易判斷衬衬。但是“野指針”是很危險的,if語句對它不起作用改橘。 “野指針”的成因主要有三種:

  1. 指針變量沒有被初始化滋尉。任何指針變量剛被創(chuàng)建時不會自動成為NULL指針,它的缺省值是隨機的飞主,它會亂指一氣狮惜。所以,指針變量在創(chuàng)建的同時應當被初始化碌识,要么將指針設置為NULL碾篡,要么讓它指向合法的內存。例如:
char *p = NULL;
char *str = (char *) malloc(100);
  1. 指針p被free或者delete之后筏餐,沒有置為NULL开泽,讓人誤以為p是個合法的指針。
  2. 指針操作超越了變量的作用域范圍胖烛。這種情況讓人防不勝防眼姐,示例程序如下:
class A{
    public:
    void Func(void){ cout << “Func of class A” << endl; }
};
void Test(void){
    A *p;
    {
    A a;
    p = &a; // 注意 a 的生命期
    }
    p->Func(); // p是“野指針”
}

函數(shù)Test在執(zhí)行語句p->Func()時,對象a已經(jīng)消失佩番,而p是指向a的众旗,所以p就成了“野指針”。但奇怪的是我運行這個程序時居然沒有出錯趟畏,這可能與編譯器有關贡歧。

malloc/free和new/delete

malloc與free是C++/C語言的標準庫函數(shù),new/delete是C++的運算符赋秀。它們都可用于申請動態(tài)內存和釋放內存利朵。
對于非內部數(shù)據(jù)類型的對象而言,光用maloc/free無法滿足動態(tài)對象的要求猎莲。對象在創(chuàng)建的同時要自動執(zhí)行構造函數(shù)绍弟,對象在消亡之前要自動執(zhí)行析構函數(shù)。由于malloc/free是庫函數(shù)而不是運算符著洼,不在編譯器控制權限之內樟遣,不能夠把執(zhí)行構造函數(shù)和析構函數(shù)的任務強加于malloc/free。
因此C++語言需要一個能完成動態(tài)內存分配和初始化工作的運算符new身笤,以及一個能完成清理與釋放內存工作的運算符delete豹悬。注意new/delete不是庫函數(shù)。我們先看一看malloc/free和new/delete如何實現(xiàn)對象的動態(tài)內存管理液荸,見示例:

class Obj{
    public :
  Obj(void){ cout << “Initialization” << endl; }
  ~Obj(void){ cout << “Destroy” << endl; }
  void Initialize(void){ cout << “Initialization” << endl; }
  void Destroy(void){ cout << “Destroy” << endl; }
};
void UseMallocFree(void){
    Obj *a = (obj *)malloc(sizeof(obj)); // 申請動態(tài)內存
    a->Initialize(); // 初始化
    //…
    
    a->Destroy(); // 清除工作
    free(a); // 釋放內存
}
void UseNewDelete(void){
    Obj *a = new Obj; // 申請動態(tài)內存并且初始化
    //…
    delete a; // 清除并且釋放內存
}

類Obj的函數(shù)Initialize模擬了構造函數(shù)的功能瞻佛,函數(shù)Destroy模擬了析構函數(shù)的功能。函數(shù)UseMallocFree中娇钱,由于malloc/free不能執(zhí)行構造函數(shù)與析構函數(shù)伤柄,必須調用成員函數(shù)Initialize和Destroy來完成初始化與清除工作。函數(shù)UseNewDelete則簡單得多文搂。
所以我們不要企圖用malloc/free來完成動態(tài)對象的內存管理响迂,應該用new/delete。由于內部數(shù)據(jù)類型的“對象”沒有構造與析構的過程细疚,對它們而言malloc/free和new/delete是等價的蔗彤。
既然new/delete的功能完全覆蓋了malloc/free,為什么C++不把malloc/free淘汰出局呢疯兼?這是因為C++程序經(jīng)常要調用C函數(shù)然遏,而C程序只能用malloc/free管理動態(tài)內存。
如果用free釋放“new創(chuàng)建的動態(tài)對象”吧彪,那么該對象因無法執(zhí)行析構函數(shù)而可能導致程序出錯待侵。如果用delete釋放“malloc申請的動態(tài)內存”,結果也會導致程序出錯姨裸,但是該程序的可讀性很差秧倾。所以new/delete必須配對使用怨酝,malloc/free也一樣。

內存耗盡

如果在申請動態(tài)內存時找不到足夠大的內存塊那先,malloc和new將返回NULL指針农猬,宣告內存申請失敗。通常有三種方式處理“內存耗盡”問題售淡。

  1. 判斷指針是否為NULL斤葱,如果是則馬上用return語句終止本函數(shù)。例如:
void Func(void){
    A *a = new A;
    if(a == NULL)
        return;
    …
}
  1. 判斷指針是否為NULL揖闸,如果是則馬上用exit(1)終止整個程序的運行揍堕。例如:
void Func(void){
    A *a = new A;
    if(a == NULL){
        cout << “Memory Exhausted” << endl;
        exit(1);
    }
    …
}
  1. 為new和malloc設置異常處理函數(shù)。例如Visual C++可以用_set_new_hander函數(shù)為new設置用戶自己定義的異常處理函數(shù)汤纸,也可以讓malloc享用與new相同的異常處理函數(shù)衩茸。詳細內容請參考C++使用手冊。
    上述 (1)贮泞、(2) 方式使用最普遍递瑰。如果一個函數(shù)內有多處需要申請動態(tài)內存,那么方式 (1) 就顯得力不從心(釋放內存很麻煩)隙畜,應該用方式 (2) 來處理抖部。
    很多人不忍心用exit(1),問:“不編寫出錯處理程序议惰,讓操作系統(tǒng)自己解決行不行慎颗?”
    不行。如果發(fā)生“內存耗盡”這樣的事情言询,一般說來應用程序已經(jīng)無藥可救俯萎。如果不用exit(1) 把壞程序殺死,它可能會害死操作系統(tǒng)运杭。道理如同:如果不把歹徒擊斃夫啊,歹徒在老死之前會犯下更多的罪。
    有一個很重要的現(xiàn)象要告訴大家辆憔。對于32位以上的應用程序而言撇眯,無論怎樣使用malloc與new,幾乎不可能導致“內存耗盡”虱咧。對于32位以上的應用程序熊榛,“內存耗盡”錯誤處理程序毫無用處。這下可把Unix和Windows程序員們樂壞了:反正錯誤處理程序不起作用腕巡,我就不寫了玄坦,省了很多麻煩。
    必須強調:不加錯誤處理將導致程序的質量很差,千萬不可因小失大煎楣。
void main(void){
    float *p = NULL;
    while(TRUE){
        p = new float[1000000];
        cout << “eat memory” << endl;
        if(p==NULL)
            exit(1);
    }
}

malloc/free的使用要點

函數(shù)malloc的原型如下:

void * malloc(size_t size);

用malloc申請一塊長度為length的整數(shù)類型的內存豺总,程序如下:

int *p = (int *) malloc(sizeof(int) * length);

我們應當把注意力集中在兩個要素上:“類型轉換”和“sizeof”。
*malloc返回值的類型是void*择懂,所以在調用malloc時要顯式地進行類型轉換喻喳,將void\ *轉換成所需要的指針類型。
*malloc函數(shù)本身并不識別要申請的內存是什么類型休蟹,它只關心內存的總字節(jié)數(shù)沸枯。我們通常記不住int, float等數(shù)據(jù)類型的變量的確切字節(jié)數(shù)日矫。例如int變量在16位系統(tǒng)下是2個字節(jié)赂弓,在32位下是4個字節(jié);而float變量在16位系統(tǒng)下是4個字節(jié)哪轿,在32位下也是4個字節(jié)盈魁。最好用以下程序作一次測試:

cout << sizeof(char) << endl;
cout << sizeof(int) << endl;
cout << sizeof(unsigned int) << endl;
cout << sizeof(long) << endl;
cout << sizeof(unsigned long) << endl;
cout << sizeof(float) << endl;
cout << sizeof(double) << endl;
cout << sizeof(void *) << endl;

在malloc的“()”中使用sizeof運算符是良好的風格,但要當心有時我們會昏了頭窃诉,寫出 p = malloc(sizeof(p))這樣的程序來杨耙。
函數(shù)free的原型如下:

void free( void * memblock );

為什么free函數(shù)不象malloc函數(shù)那樣復雜呢?這是因為指針p的類型以及它所指的內存的容量事先都是知道的飘痛,語句free(p)能正確地釋放內存珊膜。如果p是NULL指針,那么free對p無論操作多少次都不會出問題宣脉。如果p不是NULL指針车柠,那么free對p連續(xù)操作兩次就會導致程序運行錯誤。

new/delete的使用要點

運算符new使用起來要比函數(shù)malloc簡單得多塑猖,例如:

int *p1 = (int *)malloc(sizeof(int) * length);
int *p2 = new int[length];

這是因為new內置了sizeof竹祷、類型轉換和類型安全檢查功能。對于非內部數(shù)據(jù)類型的對象而言羊苟,new在創(chuàng)建動態(tài)對象的同時完成了初始化工作塑陵。如果對象有多個構造函數(shù),那么new的語句也可以有多種形式蜡励。例如:

class Obj{
    public :
    Obj(void); // 無參數(shù)的構造函數(shù)
    Obj(int x); // 帶一個參數(shù)的構造函數(shù)
    …
}
void Test(void){
    Obj *a = new Obj;
    Obj *b = new Obj(1); // 初值為1
    …
    delete a;
    delete b;
}

如果用new創(chuàng)建對象數(shù)組令花,那么只能使用對象的無參數(shù)構造函數(shù)。例如:

Obj *objects = new Obj[100]; // 創(chuàng)建100個動態(tài)對象

不能寫成:

Obj *objects = new Obj[100](1);// 創(chuàng)建100個動態(tài)對象的同時賦初值1

在用delete釋放對象數(shù)組時凉倚,留意不要丟了符號‘[]’彭则。例如:

delete []objects; // 正確的用法
delete objects; // 錯誤的用法

指針和引用

指針指向一塊內存,它的內容是所指內存的地址占遥;引用是某塊內存的別名俯抖。

  1. 從現(xiàn)象上看,指針在運行時可以改變其所指向的值瓦胎,而引用一旦和某個對象綁定后就不再改變芬萍。這句話可以理解為:指針可以被重新賦值以指向另一個不同的對象尤揣。但是引用則總是指向在初始化時被指定的對象,以后不能改變柬祠,但是指定的對象其內容可以改變北戏。

  2. 從內存分配上看,程序為指針變量分配內存區(qū)域漫蛔,而不為引用分配內存區(qū)域嗜愈,因為引用聲明時必須初始化,從而指向一個已經(jīng)存在的對象莽龟。引用不能指向空值蠕嫁。

  3. 從編譯上看,程序在編譯時分別將指針和引用添加到符號表上毯盈,符號表上記錄的是變量名及變量所對應地址剃毒。指針變量在符號表上對應的地址值為指針變量的地址值,而引用在符號表上對應的地址值為引用對象的地址值搂赋。符號表生成后就不會再改赘阀,因此指針可以改變指向的對象(指針變量中的值可以改),而引用對象不能改脑奠。這是使用指針不安全而使用引用安全的主要原因基公。從某種意義上來說引用可以被認為是不能改變的指針。

  4. 不存在指向空值的引用這個事實宋欺,意味著使用引用的代碼效率比使用指針的要高轰豆。因為在使用引用之前不需要測試它的合法性。相反迄靠,指針則應該總是被測試秒咨,防止其為空。

因此如果你有一個變量是用于指向另一個對象掌挚,但是它可能為空雨席,這時你應該使用指針;如果變量總是指向一個對象吠式,你的設計不允許變量為空陡厘,這時你應該使用引用。

  1. 理論上特占,對于指針的級數(shù)沒有限制糙置,但是引用只能是一級。如下:
int** p1;         // 合法是目。指向指針的指針
int*& p2;         // 合法谤饭。指向指針的引用
int&* p3;         // 非法。指向引用的指針是非法的
int&& p4;         // 非法。指向引用的引用是非法的
  1. 指針是一個實體揉抵,而引用僅是個別名亡容;

  2. 引用使用時無需解引用(*),指針需要解引用冤今;

  3. 引用只能在定義時被初始化一次闺兢,之后不可變;指針可變戏罢;引用“從一而終

  4. 引用沒有 const屋谭,指針有 const,const 的指針不可變龟糕;

  5. 引用不能為空桐磁,指針可以為空;

  6. “sizeof 引用”得到的是所指向的變量(對象)的大小翩蘸,而“sizeof 指針”得到的是指針本身(所指向的變量或對象的地址)的大兴狻淮逊;
    typeid(T) == typeid(T&) 恒為真催首,sizeof(T) == sizeof(T&) 恒為真,但是當引用作為成員時泄鹏,其占用空間與指針相同(沒找到標準的規(guī)定)郎任。

  7. 指針和引用的自增(++)運算意義不一樣;

常量指針(指向常量的指針备籽,值不能變舶治,但是地址可以變)

int i = 10;
const int *p = &i; //=》 int const *p = &i
*p = 20;    //error
 p = &num1  //success

常量引用(指向常量的引用)

int i = 10车猬;
const int &p = i;
p=20; //error

指針常量(表示指針本身是常量霉猛。在定義指針常量時必須初始化,地址不能變珠闰,但是值可以變)

int i = 10;
int j = 20惜浅;
int* const q = &i; //error,沒有初始化
int* const p = &i; //success
p   =  &num1; //error
*p  =  num1; //success

沒有引用常量的說法伏嗜,因為引用本身就是不可變的

常量指針常量(地址和值都不能改變)

int i =10坛悉;
const int* const p = &i;

指針和引用傳遞參數(shù)

    1.  指針傳遞參數(shù)本質上是值傳遞的方式承绸,它所傳遞的是一個地址值裸影。
     2. 值傳遞過程中,被調函數(shù)的形式參數(shù)作為被調函數(shù)的局部變量處理军熏,即在棧中開辟了內存空間以存放由主調函數(shù)放進來的實參的值轩猩,從而成為了實參的一個副本。
     3. 值傳遞的特點是被調函數(shù)對形式參數(shù)的任何操作都是作為局部變量進行,不會影響主調函數(shù)的實參變量的值均践。

     4. 引用傳遞過程中画饥,被調函數(shù)的形式參數(shù)也作為局部變量在棧中開辟了內存空間,但是這時存放的是由主調函數(shù)放進來的實參變量的地址浊猾。
     5. 被調函數(shù)對形參的任何操作都被處理成間接尋址抖甘,即通過棧中存放的地址訪問主調函數(shù)中的實參變量。正因為如此葫慎,被調函數(shù)對形參做的任何操作都影響了主調函數(shù)中的實參變量衔彻。

     6. 引用傳遞和指針傳遞是不同的,雖然它們都是在被調函數(shù)椡蛋欤空間上的一個局部變量凑耻,但是任何對于引用參數(shù)的處理都會通過一個間接尋址的方式操作到主調函數(shù)中的相關變量。而對于指針傳遞的參數(shù)配紫,如果改變被調函數(shù)中的指針地址碉就,它將影響不到主調函數(shù)的相關變量。如果想通過指針參數(shù)傳遞來改變主調函數(shù)中的相關變量废岂, 那就得使用指向指針的指針祖搓,或者指針引用。

     7. 從概念上講湖苞。指針從本質上講就是存放變量地址的一個變量拯欧,在邏輯上是獨立的,它可以被改變财骨,包括其所指向的地址的改變和其指向的地址中所存放的數(shù)據(jù)的改變镐作。
     8. 而引用是一個別名,它在邏輯上不是獨立的隆箩,它的存在具有依附性该贾,所以引用必須在一開始就被初始化,而且其引用的對象在其整個生命周期中是不能被改變的(自始至終只能依附于同一個變量)捌臊。

最后杨蛋,總結一下指針和引用的相同點和不同點:

相同點:

  • 都是地址的概念;
  • 指針指向一塊內存娃属,它的內容是所指內存的地址六荒;而引用則是某塊內存的別名。

不同點:

  • 指針是一個實體矾端,而引用僅是個別名掏击;
  • 引用只能在定義時被初始化一次,之后不可變秩铆;指針可變砚亭;引用“從一而終”灯变,指針可以“見異思遷”;
    *引用沒有const捅膘,指針有const添祸,const的指針不可變;
    具體指沒有int& const a這種形式寻仗,而const int& a是有的刃泌,前者指引用本身即別名不可以改變,這是當然的署尤,所以不需要這種形式耙替,后者指引用所指的值不可以改變)
    * 引用不能為空,指針可以為空曹体;
    * “sizeof 引用”得到的是所指向的變量(對象)的大小俗扇,而“sizeof 指針”得到的是指針本身的大小箕别;
    * 指針和引用的自增(++)運算意義不一樣铜幽;
  • 引用是類型安全的,而指針不是 (引用比指針多了類型檢查)

一個有意思的例子

/*一個有意思的例子
常量不能修改串稀,const_cast去除常量修飾除抛,打印出來地址一樣,但值卻不一樣厨诸。
原因:編譯器優(yōu)化導致镶殷;加上volatile 修飾后發(fā)現(xiàn) a的地址為1禾酱?
*/
 const int a = 1;    //volatile const int a=1微酬,可以確保不讓編譯器優(yōu)化,每次去內存取值
 int *p = const_cast<int*>(&a);
 *p = 2;
 cout << "value a="<< a << endl;
 cout << "value *p=" <<*p << endl;
 cout << "address a=" <<&a << endl;
 cout << "address p=" <<p << endl;

請關注我的訂閱號

訂閱號.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末颤陶,一起剝皮案震驚了整個濱河市颗管,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌滓走,老刑警劉巖垦江,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異搅方,居然都是意外死亡比吭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門姨涡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衩藤,“玉大人,你說我怎么就攤上這事涛漂∩捅恚” “怎么了检诗?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瓢剿。 經(jīng)常有香客問我逢慌,道長,這世上最難降的妖魔是什么间狂? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任攻泼,我火速辦了婚禮,結果婚禮上鉴象,老公的妹妹穿的比我還像新娘坠韩。我一直安慰自己,他們只是感情好炼列,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布只搁。 她就那樣靜靜地躺著,像睡著了一般俭尖。 火紅的嫁衣襯著肌膚如雪氢惋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天稽犁,我揣著相機與錄音焰望,去河邊找鬼。 笑死已亥,一個胖子當著我的面吹牛熊赖,可吹牛的內容都是我干的。 我是一名探鬼主播虑椎,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼震鹉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了捆姜?” 一聲冷哼從身側響起传趾,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泥技,沒想到半個月后浆兰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡珊豹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年簸呈,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片店茶。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜕便,死狀恐怖,靈堂內的尸體忽然破棺而出忽妒,到底是詐尸還是另有隱情玩裙,我是刑警寧澤兼贸,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站吃溅,受9級特大地震影響溶诞,放射性物質發(fā)生泄漏。R本人自食惡果不足惜决侈,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一螺垢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赖歌,春花似錦枉圃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至展父,卻和暖如春返劲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背栖茉。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工篮绿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吕漂。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓亲配,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惶凝。 傳聞我的和親對象是個殘疾皇子吼虎,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內容

  • (JG-2014-08-20)(前半部分經(jīng)過網(wǎng)上多篇文章對比整理)(后半部分根據(jù)ExceptionalCpp、C+...
    JasonGao閱讀 5,605評論 2 23
  • 幾種語言的特性 匯編程序:將匯編語言源程序翻譯成目標程序編譯程序:將高級語言源程序翻譯成目標程序解釋程序:將高級語...
    囊螢映雪的螢閱讀 2,889評論 1 5
  • 收集非原創(chuàng)文章,如遇原作者坡贺,請私聊我,我會表明出處箱舞! 1--10 1. C++中什么數(shù)據(jù)分配在棻榉兀或堆,靜態(tài)存儲區(qū)以...
    Juinjonn閱讀 4,940評論 0 30
  • 1. 基礎知識 1.1晴股、 基本概念愿伴、 功能 馮諾伊曼體系結構1、計算機處理的數(shù)據(jù)和指令一律用二進制數(shù)表示2电湘、順序執(zhí)...
    yunpiao閱讀 5,309評論 1 22
  • 我喜歡早上醒來聽到樓下大人溜小孩的說話聲瘾晃,玩具聲,哄笑聲幻妓,小孩子來回跑動的腳步聲蹦误,這讓我覺得很有生活氣息。 可以肉津,...
    端木瑞桓閱讀 318評論 1 1