下面這些內容存在很高的總結性,可能一句話就需要很大的篇幅和例子進行說明。所以,如果你有疑問可以自己寫代碼測試(個人認爲自己寫代碼測試時最好的學習方法),當然也可以在下面評論交流。
1、指針是什麼?指針變量是什麼?
指針是內存單元之間的一種指向關係,也是CPU尋址方式的一種體現。CPU在尋址時,如果直接給出存儲單元A的首地址,這種尋址方式叫做直接尋址,而如果給出的不是存儲單元A的首地址,而是先把存儲單元A的首地址存放在另一個存儲單元B中,然後給出B的地址,然後依靠B的地址去訪問A的內容,這種尋址方式就叫做間接尋址。此時B保存A的地址,我們說B指向A,也就是B中保存的是指向A的指針。當然B中的內容是A的地址。但地址和指針不可以混爲一談。
指針變量是一個變量,它保存的是地址,它體現出的是兩個存儲空間的指向關係。
2、& * 的說明:
首先這是兩個編譯器來識別的運算符。
& 代表變量的地址,首先變量的地址在每一次運行時可能不是固定的,但是它相關於一個基礎地址的偏移量肯定都是一樣的,這個跟程序的載入有關。
* 表示的是取該地址中的值。*p 就表示以p的內容爲地址的存儲空間的內容。即 p指向的空間的內容。
3、指針和數組的一致性
即p = a; a[5] == *(p+5),需要多說一句的是,p+5 表示的首地址按數學方法計算的話是p+5*sizeof(*p)
4、如果a 是數組名,那麼編譯器認爲a,&a是同樣的。
5、編譯器關於int a[3][4]的解釋,其實真正的是給定a[1][2](a[i][j]) 的尋址方式,a 是一個int ** 類型的變量,&a[1][2] = a + 1*sizeof(*a) + 2*sizeof(**a) 而sizeof(*a) = 4* sizeof(**a), 同樣的,int (*p)[4];本質上也就是包含兩個信息,p 是 int ** 類型, p+1 = p + 4*sizeof(int).用下標和*表示數組元素的關係,a[1][2] = *(*(p+1)+2)
6、指針可以進行賦值、比較、加減常數運算。
7、不知道爲什麼,很多人都喜歡對那幾個字符串處理函數翻來覆去的搞,雖然效率上會有輕微的提升(用了寄存器尋址間接尋址),但是從代碼的可讀性上來說,卻並不算是什麼好事。指針可以提高執行效率,但是代碼的可讀性變差,指針是面向內存的根本,也是C語言的靈魂,但是個人感覺還是隻在必要的時候使用。
8、函數指針:指向一個函數的入口地址。定義一個函數指針: 返回類型 (*p)(參數); eg int (*p) (int); p(1)或(*p)(1)
很多封裝好的語言(java、C#)用這種方式來處理事件,MFC中也大量使用。基本的處理方法就是把一個函數的指針作爲參數傳遞到其他的函數中去。當然這種方法對作爲參數的函數有嚴格的格式限制。通常如果一個帶有參數的函數名作爲參數傳遞時,可以把它的函數名和參數分別作爲子函數的參數。eg: void fun(int a){} ==> int fun1(int a, void(*p)()){}; fun1(a,fun);
9、區分下面兩個式子中指針p的含義:
int (*p)[4] 與 int* p[4],這兩個式子中,如果和數組做比較的話,第一個相當於intp[][4],第二個則是int p[4][](當然這樣寫是不符合語法的), p都相當於二級指針,但編譯器對其類型的解釋是:下標的結合性優於*,[]符號可以提前,第一個*p的類型是int[4] 類型,p則是int[4]* 類型,第二個p 是int*[4]類型,如果這樣說仍不太容易理解的話,我們就看編譯器是如何使用他們計算的吧,對於第一個p+1 == p + 4*sizeof(int),*p +1 == *p +sizeof(int), 對於第二個,p+1 == p + sizeof(int *),而*p + 1 == p + sizeof(int) 需要再次說明的是,這些定義的方式是由編譯器所要求,不同的定義,編譯器就有不同的解釋和使用方式,但指針所代表的含義在本質上是一致的,我們可以用強轉的方式去隨意使用他們,當然這樣做也有不好的地方。
10、從上面的理解中,我們再看一個多級指針如何理解
int **p; 它相當於int p[][] ,p + 1 要如何解釋呢? p 是int ** 類型 即 p + sizeof(int*)
對於以上的寫法和概念不應該去死記硬背,而是要理解他們的意義,做到靈活使用就可以了。
int(*p)[4]數組指針,指向一個長度爲4的數組的指針,既然是指針,那麼它佔用的內存空間的長度就只有4個字節(32位機)
int*p[4] 指針數組,定義一個數組,數組的元素爲指針,佔用的內存空間數就是length * 4,就是一個常變量。
int** p 指向指針的指針,當然它就是一個指針,佔4個字節。
在給他們分配完內存空間後,剩下的其實就是編譯器來處理的內容了,規範他們的使用方式。即按下標(數組),或指針加減的方式。在對其賦值時,是否具有一致的尋址算法(即加1表示的意義是否一致)
使用指針的技巧:保持指針級數相同即可,也就是熟練區分一級指針,二級指針,數組指針等。
11、執行程序時,給定參數的方法。
首先在main函數中,要添加兩個參數(int argc,char* argv[])
執行程序時,給定命令參數,argc會自動保存命令中字符串的個數,命令本身也算一個,argv[] 數組,則保存各個參數字符串的地址。注意argv 是指針數組。
雖然從內存的角度解釋指針的含義已經簡單了很多,但是由於編譯器對指針的語法限制過於複雜,想要熟練使用,我還需要多加練習。