本文爲我的讀書筆記,僅供記錄學習過程當中遇到的往後須要留意的問題,若有相關版權問題請及時通知做者。
指針
指針的內存佈局
一個基本的數據類型,包括結構體等自定義類型加上 * 號就構成了一個指針類型,這個類型的大小是必定的,與 * 號前面的數據類型無關。* 號前面的數據類型只是說明指針所指向的內存裏存儲的數據類型。因此,在32位系統下(64位測試相同結果),無論什麼樣的指針類型,其大小都爲4byte。
int *p=NULL與*p=NULL
int *p=NULL;
定義一個指針變量p,其指向的內存裏面保存的是int類型的數據;在定義變量p的同時把p的值設置爲0x00000000,而不是把*p的值設置爲0x000000.這個過程叫作初始化,是編譯時進行的。(我的理解:也就說把指針位置初始化,而不是把指針指向的內容清空)
int *p;
*p=NULL;
定義一個指針變量p,其指向的內存裏保存的是int類型的數據;此時p自己的值不肯定,多是一個非法的地址。第二行給*p賦值爲NULL,即給P指向的內存賦值爲NULL;可是因爲p指向的內存多是非法的,因此調試的時候,編譯器可能會報告一個內存訪問錯誤。
NULL就是NULL,被宏定義爲0,但和0不一樣。要注意大小寫。
如何將數值存儲到指定的內存地址
int *p=(int*)0x12ff7c
*p=0x100;
爲了取一個合法的地址,能夠創建變量編譯時查看地址,目的是避免使用了不合法的地址。
*(int*)0x12ff7c=0x100;同理
數組
數組的內存佈局
int a[5];
sizeof(a)的值爲sizeof(int)*5 =20
sizeof(a[0])的值爲sizeof(int)=4
sizeof(a[5])的值爲4,沒有出錯。由於sizeof是關鍵字,不是函數。函數求值是在運行的時候,而關鍵字sizeof求值是在編譯的時候。雖然並不存在a[5]這個元素,可是這裏也沒有真正訪問a[5],而是僅僅根據數組元素的類型來肯定其值。
sizeof(&a[0]) 值爲4。取元素a[0]的首地址。
sizeof(&a)值爲4 。取數組a的首地址 。但VC++6.0的結果是20.
數組名做爲左值與右值的區別
x=y;
左值:在這個上下文環境中,編譯器認爲x的含義是x所表明的地址。由編譯器管理。
右值:在這個上下文環境中,編譯器認爲y的含義是y所表明的地址裏面的內容。
C語言規定,出如今賦值符左邊的符號所表明的低智商的內容必定是能夠被修改的。也就是說,只能給非只讀變量賦值。
a做爲右值時等價於&a[0],表明的是數組首元素的首地址,而不是數組的首地址。但這僅僅是表明,編譯器並無爲數組a分配一塊內存來存其地址。
a不能做左值。只能訪問數組的某個元素而沒法把數組當一個整體進行訪問。
指針與數組的關係。
指針與數組沒有關係。
指針變量在32位系統下,永遠佔4個byte,其值爲某一個內存的地址,指針能夠指向任何地方,但不是任何地方都能經過指針變量訪問到。
數組的大小與元素的類型和個數有關。定義數組時必須指定元素類型和個數。數組能夠存任何類型的數據,但不能存函數。
以指針的形式和如下標的形式訪問指針。
對於 char *p=「abcdef」;
若是要取字符e,使用*(p+4)與p[4]均可以實現。編譯器老是把如下表的形式的操做解析爲以指針的形式的操做。
如下標的形式訪問在本質上與以指針的形式訪問沒有區別,只是寫法不一樣。
以指針的形式和如下標的形式訪問數組
數組自己在棧上。對數組的訪問必須現根據數組的名字找到數組首元素的首地址,而後根據偏移量找到相應的值。這是一種典型的「具名+匿名」的訪問。
指針和數組均可以 以指針形式 或 如下標形式進行訪問。但指針是徹底的匿名訪問,數組是典型的具名+匿名訪問。
a和&a的區別
main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
對指針進行加1操做,獲得的是下一個元素的地址。一個類型爲T的指針的移動,以sizeof(T)爲單位。
&a是數組的首地址,所以&a+1至關於&a+5*sizeof(a),即&a+5*sizeof(int),也就是下一個數組的首地址。
a是數組首元素的首地址,也就是a[0]的首地址,a+1就是數組下一元素的地址,即a[1]的地址。
指針和數組的定義與聲明
定義爲數組,聲明爲指針
文件1:char a[100];
文件2:extern char *a;
是錯的。當聲明extern char *a;時,編譯器認爲a是一個指針變量,佔4個byte。這4個byte裏存放了一個地址,這個地址上存的是字符類型數據。
定義爲指針,聲明爲數組
一樣錯誤。
指針和數組的對比。
在一個地方定義爲指針,在別的地方也只能聲明爲指針;在一個地方定義爲數組,在別的地方也只能聲明爲數組。
指針 |
數組 |
保存數據的地址,任何存入指針變量的數據都會被當作地址來處理。指針自己的地址由編譯器另外存儲,存儲位置未知 |
保存數據,數組名a表明數組首元素的首地址。&a表明整個數組的首地址。a自己的地址由編譯器另外存儲,存儲位置未知 |
間接訪問數據,首先取得指針變量的內容,把它做爲地址,而後從這個地址提取數據或向這個地址寫入數據。指針能夠以指針或下標的形式訪問。但本質上都是先取p的內容而後再加偏移量*sizeof(類型)個byte |
直接訪問數據,數組名a是整個數組的名字,數組內每一個元素沒有名字。只能經過具名+匿名的方式來訪問某個元素。數組能夠以指針的形式或下表的形式訪問。但本質上都是a所表明的數組首元素的首地址加上偏移量*sizeof(類型)個byte |
一般用於動態數據結構 |
一般用於存儲固定數目且數據類型相同的元素 |
相關函數 malloc free |
隱式分配和刪除 |
一般指向匿名數據,也能夠指向具名數據 |
自身即爲數組名 |
指針數組和數組指針
指針數組:是一個數組,數組的元素都是指針。數組佔多少個字節由數組自己決定。全稱是 存儲指針的數組
數組指針:是一個指針,指向一個數組。32位系統下永遠是4個字節,指向的數組佔多少個字節未知。全稱是 指向數組的指針。
int *p1[10];
int (*p2)[10];
[ ]的優先級高於*,因此p1先與[ ]結合,構成一個數組的定義,因此它是一個名爲p1的數組,數組元素是int *。也就是指針數組。
()的優先級比[ ]高,因此p2與*結合構成指針的定義,指針變量名爲p2,int修飾的是數組的內容。數組在這裏沒有名字,是個匿名數組。因此p2是一個指針,指向一個包含10個int類型數據的數組,即數組指針。
數組指針本來的定義形式爲:int (*)[10] p2;
由於數組指針是指向數組的,因此賦值應當=&a 而不是 =a
地址的強制轉換
指針變量與一個證書相加減並非用指針變量裏的地址直接加減這個整數。這個整數的單位是元素個數。
多維數組與多級指針
二維數組初始化是花括號嵌套花括號
二級指針
二級指針保存的是一級指針的地址
數組參數與指針參數
一維數組參數
沒法向函數傳遞一個數組。
C語言中,當一維數組做爲函數參數的時候,編譯器老是把它解析成一個指向其首元素首地址的指針。
函數自己是沒有類型的,右值函數的返回值纔有類型。
函數的返回值也不能是一個數組,而只能是指針。
實際傳遞的數組大小與函數形參指定的數組大小沒有關係。
一級指針參數
沒法把指針變量自己傳遞給一個函數,傳遞過去的是實參的一個備份。若是須要在函數中操做指針,如malloc,那麼須要在函數中把指針變量return回來。
二維數組參數與二維指針參數
數組參數 |
等效的指針參數 |
數組的數組:char a[3][4] |
數組的指針:char (*p) |
指針數組:char *a[5] |
指針的指針:char **P |
C語言中,當一維數組做爲函數參數的時候,編譯器老是把它解析成一個指向其首元素首地址的指針。這條規則並非遞歸的,也就是說只有一維數組纔是如此,當數組超過一維時,將第一維改寫爲指向數組首元素首地址的指針後,後面的維不再可改寫。
函數指針
函數指針就是函數的指針。是一個指針,指向一個函數
VC++6.0裏,給函數指針賦值時,能夠用&fun或直接用函數名fun。這是由於函數名被編譯以後就是一個地址。因此這兩種用法沒有本質的差異。
函數指針數組
char * (*pf)(char *p)定義的是一個函數指針pf。既然pf是一個指針,那就能夠存儲在一個數組裏。
char *(*pf[3])(char *p);
這是定義一個函數指針數組。它是一個數組,名爲pf,數組內存儲了3個指向函數的指針。這些指針指向一些返回值類型爲指向字符的指針、參數爲一個紙箱字符的指針的函數。
函數指針數組的指針
char *(*(*pf)[3])(char *p);
這裏的pf是指針,指向一個包含了3個元素的數組;這個數組裏存的是指向函數的指針;這些指針指向一些返回值類型爲指向字符的指針、參數爲一個紙箱字符的指針的函數。
,,