C++學習總結

一些需要知道的基礎知識點:

在程式程式碼中是通過變數名對記憶體單元進行存取操作的,但是程式碼經過編譯後將變數名轉換為該變數在記憶體中的存放地址,對變數值的存取都是通過地址進行的。比如i+j的運算,如果i等於3,j等於4,程式是先根據變數名與地址的對應關係,找到變數i的地址,從第一個地址開始順序讀取四個位元組數據放到CPU暫存器中;再找到j的地址,依次讀取四個放到暫存器中,然後通過CPU的加法中斷計算出結果。

在低級的彙編語言中都是直接通過地址來訪問記憶體單元的,而在高級語言中才使用變數名訪問記憶體單元,C語言作為高級語言卻提供了通過地址來訪問記憶體單元的方法,C++也繼承了這一特性。地址可以形象地稱為指針,意思是通過指針能找到記憶體單元,因為原本是通過地址找到記憶體單元的,所以一個變數的地址稱為該變數的指針。如果有一個變數專門存放另一個變數的地址,它就是指針變數。在C++語言中,專門用來存放記憶體單元地址的變數類型,稱為指針類型。

指針是一種數據類型,通常所說的指針就是指針變數,是專門用來存放地址的變數。而變數的指針說的就是變數在記憶體中的地址。變數地址在編寫程式碼時無法獲得,只有在程式運行時才可以得到。

指針跟常規的變數為賦值相同,沒有具體指向的指針不會導致編譯出錯,但是可能會導致難以預料的錯誤,所以一旦定義指針,一定要讓它有一個具體的指向,也就是說要有一個地址賦給它。

另外,定義指針變數的時候,跟其他常規變數為了區分,要加*號,但其實真正的指針變數是沒有*號的,在使用的時候要注意。

指針進行運算其實就是地址進行運算,指針的加減運算是跟指針的類型有關的,比如int類型的指針加1,地址值並不是加1而是加4,因為int類型佔四個位元組。

※指針還可以指向空類型void。空類型指針可以接受任何類型的數據,例如void *p = NULL

這裡有點問題,NULL代替空指針存在二義性,所以後來用nullptr來代替空指針。總之,只要記住nullptr代表空指針就可以了,而NULL在C++中都把它理解為0就可以了。

※const int *p = &i  表示這個指針是指向常量的指針,只能用來「讀」記憶體數據,無法通過*p的方式更改記憶體的內容,即更改變數的值,但是可以改變自身的地址值,就是指向別的記憶體地址;

※int* const p = &i  表示這個指針是一個常量指針,什麼意思呢,以為指針變數里存放的是地址,定義為常量指針的話,說明這個指針變數存放的地址值是不可以改變的,但是呢,可以通過*p的方式改變記憶體的數據;

※const int* const p = &i  表示這個指針是指向常量的常量指針,有點拗口,跟上面的對比,反正就是只能用來「讀」記憶體數據,也無法通過*p的方式更改記憶體的內容,也無法改變自身的地址值。

 

指針和一維數組

一維數組和二維數組在記憶體中的存放結構都是線性的。數組第一元素的地址就是整個數組的存儲首地址,該地址存放在數組名中,看吧,其實數組名就相當於一個指針變數了,裡頭存放的是數組的首地址。訪問數組元素的方式有下標法和指針法。用數組名[i]這個方式可以訪問數組內容,其實也可以通過*(a+i)的方式,當然我們也可以再單獨定義一個指針變數,將數組的首地址存放到這個指針變數中,當然了,可以用&a[0]也可以直接用a即數組名,因為我們說過了數組名本來就存放的是數組的首地址。

*(p++)相當於a[i++],先對p進行*運算,再使p自增。

指針與二維數組

a[0]是二維數組第一個元素的地址,可以賦值給一個指針變數。

a代表二維數組的地址,通過指針運算符可以獲取數組中的元素。

a+n表示第n行的首地址;

&a[0][0]既可以看做數組0行0列的首地址,還可以看做是二維數組的首地址;

&a[0]是第0行的首地址;

a[0]+n是第0行第n個元素的地址;

*(*(a+n)+m)表示第n行第m列元素;

*(a[n]+m)表示第n行第m列元素。

數組指針和指針數組

這個還沒有理解好,尤其是數組指針,指針數組還好理解,就是存儲指針變數的數組。

指針和字元數組

字元數組就是一個字元串,通過字元指針可以指向一個字元串,然後通過地址的加減實現讀取。

傳遞地址

之前接觸的函數都是實參傳遞進函數體後,生成的是實參的副本,函數體內改變副本的值並不會影響到實參,但是如果傳遞進去的是指針,即使是副本指針,指向的地址還是一樣的,因此可以改變指針指向地址的內容。

指向函數的指針

指針變數也可以指向一個函數。一個函數在編譯時被分配給了一個入口地址,這個地址可以理解為存放在函數名中,可以定義函數指針,指向這個函數,通過指針來調用函數。

比如:

int sum(int x, int y)

int *a(int, int);

a = sum;

調用的時候這樣:

int c,d;

(*a)(c,d);

還可以定義指針函數,返回值是指針,也就是地址了。

空指針調用函數

空類型指針指向任意類型函數或者將任意類型的函數指針賦值給空類型指針都是合法的。使用空指針調用自身所指向的函數仍然按照強制轉換的形式使用。

指針數組

 

構造數據類型

結構體

C++ struct結構體變數其實跟數組有點像,只不過數組是相同類型元素的集合,而結構體變數可以是不同類型數據的集合。

定義結構體變數有兩種方式:1、在定義結構體的時候定義;2、定義完之後定義。

struct PersonInfo
{
    int index;
    char name[30];
    short age;
}XiaoMing;

或者
struct PersonInfo
{
    int index;
    char name[30];
    short age;
}
PersonInfo XiaoMing;

引用方式:1.使用成員運算符.來引用;2.定義結構體指針變數之後,使用指向運算符->來引用

 

結構體還可以進行嵌套,結構體的大小一般情況下都是結構體內成員的大小之和。

使用typedef可以給一個複雜的數據類型定義一個別名,比如int(*)(int i)很複雜,就可以用一個別名來代替。

 

枚舉類型enum的應用

枚舉類型就是用大括弧將不同標識符放到一起,變數的值只能取自括弧內的值,在定義時,編譯器默認將標識符自動賦上整形常數。還可以自行修改整形常數的值,說白了就是一個標識符對應一個常數,用於一些判斷或者什麼,用起來方便一些。賦值的時候,不能直接賦整型數,但是可以通過強制類型轉換來賦值。枚舉類型的變數,實質其實是整型的數字,所以可以進行比較和運算。

結構體

結構體變數還可以做函數的參數,還可以使用結構體指針,使用結構體指針減少了時間和空間上的開銷,能夠提高程式的運行效率。

結構體還可以創建結構體數組,跟創建結構體變數一樣,在定義結構體時創建或者定義完了之後用結構體名這種數據類型創建。可以定義結構體指針,指向數組,使用指向符號->進行讀取,但是要注意,指針加1,指向的就是數組裡的下一個結構體了。

共用體

共用體union數據類型其實和結構體很像,聲明共用體數據類型變數和聲明共用體變數一樣,都有三種方式,前兩種應該知道,第三種是省略了共用體數據類型變數名,在定義共用體之後接著聲明變數。

要注意共用體跟結構體最大的區別在於記憶體方面。共用體變數所佔的記憶體長度等於最長的成員的長度,一個共用體變數不能同時存放多個成員的值,某一時刻只能存放其實一個成員的值,這就是最後賦予他的值。

共用體的特點是:1.使用共用體變數的目的是希望用同一個記憶體段存放幾種不同類型的數據,但是某個瞬間只能存放一種,而不是同時存放幾種;2.能夠被訪問的是最後一個被賦值的變數,對新的賦值後原來的就失去作用了;3.共用體變數的地址和它的各成員的地址都是同一地址。4.不能對共用體變數名賦值,不能引用變數名來得到哦一個值,不能在定義共用體變數時對它初始化,不能作為函數參數。

自定義數據類型

跟前面的數據類型重命名一樣的,使用typedef標識符進行類型的重命名,或者說是自定義數據類型,其實是為了寫起來方便或者用特定的單詞代表特殊的意思。

宏定義

使用#define 可以對程式中經常出現的參數進行宏定義,這樣在之後寫程式碼的時候就可以用宏定義替換,一定程度上減輕了寫程式碼的複雜度,程式在編譯的時候自動進行替換,比如PI,一般標識符習慣用大寫字母表示,以和變數名進行區分,要注意宏定義不是C語言,不需要在後面加分號,還可以定義數組和運算,定義運算的時候,括弧建議都加上,以防止出現錯誤。如果字元串長於一行,可以在該行末尾用反斜杠\來續行。一般來講,定義只有,作用域即有效範圍指的是定義命令之後到此源文件結束,但也可通過#undef命令終止宏定義的作用域。

 

Tags: