(送給初學c的同學)徹底搞定c++和c的指針

學號:16030140019

姓名:莫益彰

【嵌牛導讀】初學C或C++的你,想必肯定被書上所講的指針搞得頭皮發麻,而這篇總結將帶你深入淺出地了解指針。

【嵌牛提問】指針的應用有哪些?指針和系統棧堆的關系?

【嵌牛正文】

1.語言中變量的實質

要理解C指針,我認為一定要理解C中“變量”的存儲實質, 所以我就從“變量”這個東西開始講起吧!

先來理解理解內存空間吧!請看下圖:

內存地址→  6      7   8      9   10      11      12       13

-----------------------------------------------------------------

。。。 |   |   |   |   |  |   |   |.。

------------------------------- ----------------------------------

如圖所示,內存只不過是一個存放數據的空間,就好像我 的看電影時的電影院中的座位一樣。每個座位都要編號,我們的內存要存放各種各樣的數據,當然我們 要知道我們的這些數據存放在什么位置吧!所以內存也要象座位一樣進行編號了,這就是我們所說的內 存編址。座位可以是按一個座位一個號碼的從一號開始編號,內存則是按一個字節一個字節進行編址, 如上圖所示。每個字節都有個編號,我們稱之為內存地址。好了,我說了這么多,現在你能理解內存空 間這個概念嗎?

我們繼續看看以下的C、C++語言變量申明:

int I;

char a;

每次我們要使用某變量時都要事先這樣申明它,它其實是內存中申請了一個名為i的整型變量寬 度的空間(DOS下的16位編程中其寬度為二個字節),和一個名為a的字符型變量寬度的空間(占一個字 節)。

我們又如何來理解變量是如何存在的呢。當我們如下申明變量時:

int I;

char a;

內存中的映象可能如下圖:

內存地址→   6      7   8       9      10      11    12      13

----------------------- -------------------------------------------

。。。|   |   |   |   |   |   |   |.。

------------------------------------------------------------------

變量名|→i    ←|→a  ←|

圖中可看出,i在內存起始地址為6上申請了 兩個字節的空間(我這里假設了int的寬度為16位,不同系統中int的寬度是可能不一樣的),并命名為 i. a在內存地址為8上申請了一字節的空間,并命名為a.這樣我們就有兩個不同類型的變量了。

2.賦值給變量

再看下面賦值:

i=30

a=‘t’

你當然知 道個兩個語句是將30存入i變量的內存空間中,將‘t’字符存入a變量的內存空間中。我們可 以這樣的形象理解啦:

內存地址→   6      7   8      9      10       11    12      13

------------------------------------------------ -----------------------

。。。 |   30      |  ‘t’  |   |   |    |   |.。

-------------------------------------------------------------------- ---

|→i    ←|→a  ←|

3.變量在哪里?(即我想知道變量的地 址)

好了,接下來我們來看看&i是什么意思?

是取i變量所在的地址編號嘛!我們可 以這樣讀它:返回i變量的地址編號。你記住了嗎?

我要在屏幕上顯示變量的地址值的話,可以 寫如下代碼:

printf(“%d”,&i);

以上圖的內存映象所例,屏幕上 顯示的不是i值30,而是顯示i的內存地址編號6了。當然實際你操作的時,i變量的地址值不會是這個數 了。

這就是我認為作為初學者們所應想象的變量存儲實質了。請這樣理解吧!

最后總結代碼如下:

int main()

{

int i=39;

printf(“%d\n”,i);    //①

printf(“%d\n”, &i);  //②

}

現在你可知道 ①、②兩個printf分別在屏幕上輸出的是i的什么東西啊?

好啦!下面我們就開始真正進入指針 的學習了。

二、指針是什么東西

想說弄懂你不容易啊!我們許多初學指針的人都要這樣的感慨。我常常在思索它,為什么呢?其實生活中處處都有指針。我們也處處在使用它。有了它我們的生活才更加方便 了。沒有指針,那生活才不方便。不信?你看下面的例子。

這是一個生活中的例子:比如說你要 我借給你一本書,我到了你宿舍,但是你人不在宿舍,于是我把書放在你的2層3號的書架上,并寫了一 張紙條放在你的桌上。紙條上寫著:你要的書在第2層3號的書架上。當你回來時,看到這張紙條。你就 知道了我借與你的書放在哪了。你想想看,這張紙條的作用,紙條本身不是書,它上面也沒有放著書。 那么你又如何知道書的位置呢?因為紙條上寫著書的位置嘛!其實這張紙條就是一個指針了。它上面的 內容不是書本身,而是書的地址,你通過紙條這個指針找到了我借給你的本書。

那么我們C,C++ 中的指針又是什么呢?請繼續跟我來吧,看下面看一個申明一整型指針變量的語句如下:

int * pi;

pi是一個指針,當然我們知道啦,但是這樣說,你就以為pi一定是個多么特別的東西了。其 實,它也只過是一個變量而已。與上一篇中說的變量并沒有實質的區別。不信你看下面圖。

內存 地址→6     7   8      9     10     11      12     13      14

--------------------------------------------------------------

...|    30      |  ‘t’ |      |      |      |      |       |      |……

--------------------------------------------------- -----------

變量 |→i   ←|→a   ←|       |→ pi       ←|

(說明:這里我假設了指針只占2個字節寬度,實際上在32位系統中,指針的寬度 是4個字節寬的,即32位。)由圖示中可以看出,我們使用int *Pi申明指針變量; 其實是在內存的某處 申明一個一定寬度的內存空間,并把它命名為Pi.你能在圖中看出pi與前面的i,a 變量有什么本質區別 嗎,沒有,當然沒有!pi也只不過是一個變量而已嘛!那么它又為什么會被稱為指針?關鍵是我們要讓 這個變量所存儲的內容是什么。現在我要讓pi成為真正有意義上的指針。請接著看下面語句:

pi=&i;

你應該知道 &i是什么意思吧!再次提醒你啦:這是返回i變量的地址編 號。整句的意思就是把i地址的編號賦值給pi,也就是你在pi上寫上i的地址編號。結果如下圖所示:

內存地址→6     7   8   9   10     11    12     13     14

------------------------------------------------------------------

...|      30      |  ‘t’  |      |      |     6      |       |      |……

----------------------------------------------- -------------------

變量 |→i   ←|→a    ←|       |→ pi     ←|

你看,執行完pi=&i;后,在圖示中的系統中,pi的值是6.這 個6就是i變量的地址編號,這樣pi就指向了變量i了。你看,pi與那張紙條有什么區別?pi不就是那張紙 條嘛!上面寫著i的地址,而i就是那個本書。你現在看懂了嗎?因此,我們就把pi稱為指針。所以你要 記住,指針變量所存的內容就是內存的地址編號!好了,現在我們就可以通過這個指針pi來訪問到i這個 變量了,不是嗎?。看下面語句:

printf(“%d”,*pi);

那么*pi什么意 思呢?你只要這樣讀它:pi內容所指的地址的內容(嘻嘻,看上去好像在繞口令了),就pi這張“ 紙條”上所寫的位置上的那本 “書”——i .你看,Pi內容是6,也就是說 pi指向內存編號為6的地址。*pi嘛!就是它所指地址的內容,即地址編號6上的內容了。當然就是30的值 了。所以這條語句會在屏幕上顯示30.也就是說printf(“%d”,*pi);語句等價于printf ( “%d”, i ) ,請結合上圖好好體會吧!各位還有什么疑問,可以發Email: yyf977@163.com.

到此為止,你掌握了類似&i , *pi寫法的含義和相關操作嗎。總的一句話 ,我們的紙條就是我們的指針,同樣我們的pi也就是我們的紙條!剩下的就是我們如何應用這張紙條了 。最后我給你一道題:

程序如下

char  a,*pa

a=10

pa=&a

*pa=20

printf( “%d”, a)

你能直接看出輸出的結果是什么嗎?如 果你能,我想本篇的目的就達到了。好了,就說到這了。Happy to Study!在下篇中我將談談“指 針的指針”即對int * * ppa;中ppa 的理解。

1.數組元素

看下面代碼

int i,a[]={3,4,5,6,7,3,7,4,4,6};

for (i=0;i<=9;i++)

{

printf ( “%d”, a[i] );

}

很顯然,它是顯示a 數組的各元素值。

我們還可以這樣訪問元素,如下

int i,a[]={3,4,5,6,7,3,7,4,4,6};

for (i=0;i<=9;i++)

{

printf ( “%d”,  *(a+i) );

}

它的結果和作用完全一樣

2. 通過指針訪問數組元素

int i,*pa,a[]={3,4,5,6,7,3,7,4,4,6};

pa =a  ;//請注意數組名a直接賦值給指針 pa

for (i=0;i<=9;i++)

{

printf ( “%d”, pa[i] );

}

很顯然,它也是顯示a 數組的各元素值。

另外與數組名一樣也可如下:

int i,*pa,a[]={3,4,5,6,7,3,7,4,4,6};

pa =a;

for (i=0;i<=9;i++)

{

printf ( “%d”, *(pa+i) );

}

看pa=a即數組名賦值給指針,以及通過數組名、指針對元素的訪問形式看,它們并沒有什么區別,從 這里可以看出數組名其實也就是指針。難道它們沒有任何區別?有,請繼續。

3. 數組名與指針變量的區別

請看下面的代碼:

int i,*pa,a[]={3,4,5,6,7,3,7,4,4,6};

pa =a;

for (i=0;i<=9;i++)

{

printf ( “%d”, *pa );

pa++ ;  //注意這里,指針值被修改

}

可以看出,這段代碼也是將數組各元素值輸出。不過,你把{}中的pa改成a試試。你會發現程序編譯 出錯,不能成功。看來指針和數組名還是不同的。其實上面的指針是指針變量,而數組名只是一個指針 常量。這個代碼與上面的代碼不同的是,指針pa在整個循環中,其值是不斷遞增的,即指針值被修改了 。數組名是指針常量,其值是不能修改的,因此不能類似這樣操作:a++.前面4,5節中pa[i],*(pa+i )處,指針pa的值是使終沒有改變。所以變量指針pa與數組名a可以互換。

4. 申明指針常量

再請看下面的代碼:

int i, a[]={3,4,5,6,7,3,7,4,4,6};

int * const pa=a;//注意const的位置:不是 const int * pa,

for (i=0;i<=9;i++)

{

printf ( “%d”, *pa );

pa++ ;  //注意這里,指針值被修改

}

這時候的代碼能成功編譯嗎?不能。因為pa指針被定義為常量指針了。這時與數組名a已經沒有不同 。這更說明了數組名就是常量指針。但是…

int * const a={3,4,5,6,7,3,7,4,4,6};//不行

int a[]={3,4,5,6,7,3,7,4,4,6};//可以,所以初始化數組時必定要這樣。

以上都是在VC6.0上實驗。

1 int i 說起

你知道我們申明一個變量時象這樣int i ;這個i是可能在它處重新變賦值的。 如下:

int i=0;

//…

i=20;//這里重新賦值了

不過有一天我的程 序可能需要這樣一個變量(暫且稱它變量),在申明時就賦一個初始值。之后我的程序在其它任何處都 不會再去重新對它賦值。那我又應該怎么辦呢?用const .

//**************

const int ic =20;

//…

ic=40;//這樣是不可以的,編譯時是無法通過,因為我們不能對 const 修飾的ic重新賦值的。

//這樣我們的程序就會更早更容易發現問題了。

//**************

有了const修飾的ic 我們不稱它為變量,而稱符號常量,代表著20這 個數。這就是const 的作用。ic是不能在它處重新賦新值了。

認識了const 作用之后,另外,我 們還要知道格式的寫法。有兩種:const int ic=20;與int const ic=20;。它們是完全相同的。這一 點我們是要清楚。總之,你務必要記住const 與int哪個寫前都不影響語義。有了這個概念后,我們來看 這兩個家伙:const int * pi與int const * pi ,按你的邏輯看,它們的語義有不同嗎?呵呵,你只要 記住一點,int 與const 哪個放前哪個放后都是一樣的,就好比const int ic;與int const ic;一樣 。也就是說,它們是相同的。

好了,我們現在已經搞定一個“雙包胎”的問題。那么 int * const pi與前兩個式子又有什么不同呢?我下面就來具體分析它們的格式與語義吧!

2 const int * pi的語義

我先來說說const int * pi是什么作用 (當然int const * pi也是一樣 的,前面我們說過,它們實際是一樣的)。看下面的例子:

//*************代碼開始 ***************

int i1=30;

int i2=40;

const int * pi=&i1;

pi=&i2;    //4.注意這里,pi可以在任意時候重新賦值一個新內存地址

i2=80;    //5.想想看:這里能用*pi=80;來代替嗎?當然不能

printf( “%d”, *pi ) ;  //6. 輸出是80

//*************代碼結束***************

語義分析:

看出來了 沒有啊,pi的值是可以被修改的。即它可以重新指向另一個地址的,但是,不能通過*pi來修改i2的值。 這個規則符合我們前面所講的邏輯嗎?當然符合了!

首先const  修飾的是整個*pi(注意,我 寫的是*pi而不是pi)。所以*pi是常量,是不能被賦值的(雖然pi所指的i2是變量,不是常量)。

其次,pi前并沒有用const 修飾,所以pi是指針變量,能被賦值重新指向另一內存地址的。你可 能會疑問:那我又如何用const 來修飾pi呢?其實,你注意到int * const pi中const 的位置就大概可 以明白了。請記住,通過格式看語義。哈哈,你可能已經看出了規律吧?那下面的一節也就沒必要看下 去了。不過我還得繼續我的戰斗!

3 再看int * const pi

確實,int * const pi與前面 的int const * pi會很容易給混淆的。注意:前面一句的const 是寫在pi前和*號后的,而不是寫在*pi 前的。很顯然,它是修飾限定pi的。我先讓你看例子:

//*************代碼開始 ***************

int i1=30;

int i2=40;

int * const pi=&i1;

//pi=&i2;    4.注意這里,pi不能再這樣重新賦值了,即不能再指向另一個新地址。

//所以我已經注釋了它。

i1=80;    //5.想想看:這里能用*pi=80;來代替嗎?可以,這 里可以通過*pi修改i1的值。

//請自行與前面一個例子比較。

printf( “% d”, *pi ) ;  //6.輸出是80

//***************代碼結束 *********************

語義分析:

看了這段代碼,你明白了什么?有沒有發現 pi值是不能重新賦值修改了。它只能永遠指向初始化時的內存地址了。相反,這次你可以通過*pi來修改 i1的值了。與前一個例子對照一下吧!看以下的兩點分析

1)pi因為有了const 的修飾,所以只 是一個指針常量:也就是說pi值是不可修改的(即pi不可以重新指向i2這個變量了)(看第4行)。

2)整個*pi的前面沒有const 的修飾。也就是說,*pi是變量而不是常量,所以我們可以通過 *pi來修改它所指內存i1的值(看5行的注釋)

總之一句話,這次的pi是一個指向int變量類型數 據的指針常量。

我最后總結兩句:

1) 如果const 修飾在*pi前則不能改的是*pi(即不能 類似這樣:*pi=50;賦值)而不是指pi.

2) 如果const 是直接寫在pi前則pi不能改(即不能類似 這樣:pi=&i;賦值)。

請你務必先記住這兩點,相信你一定不會再被它們給搞糊了。現在 再看這兩個申明語句int const *pi和int * const pi時,呵呵,你會頭昏腦脹還是很輕松愜意?它們各 自申明的pi分別能修改什么,不能修改什么?再問問自己,把你的理解告訴我吧,可以發帖也可以發到 我的郵箱(我的郵箱yyf977@163.com)!我一定會答復的。

3)  補充三種情況。

這里, 我再補充以下三種情況。其實只要上面的語義搞清楚了,這三種情況也就已經被包含了。不過作為三種 具體的形式,我還是簡單提一下吧!

情況一:int * pi指針指向const int i常量的情況

//**********begin*****************

const int i1=40;

int *pi;

pi=&i1; //這樣可以嗎?不行,VC下是編譯錯。

//const int 類型的i1的地址 是不能賦值給指向int 類型地址的指針pi的。否則pi豈不是能修改i1的值了嗎!

pi=(int* ) &i1;  // 這樣可以嗎?強制類型轉換可是C所支持的。

//VC下編譯通過,但是仍 不能通過*pi=80來修改i1的值。去試試吧!看看具體的怎樣。

//***********end***************

情況二:const int * pi指針指向const int i1的 情況

//*********begin****************

const int i1=40;

const int * pi;

pi=&i1;//兩個類型相同,可以這樣賦值。很顯然,i1的值無論是通過pi還是i1都不能修 改的。

//*********end*****************

情況三:用const int * const pi申明 的指針

//***********begin****************

int i

const int * const pi=&i;//你能想象pi能夠作什么操作嗎?pi值不能改,也不能通過pi修改i的值。因為不管是*pi還 是pi都是const的。

//************end****************

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容