一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

指針與數(shù)組之間的恩恩怨怨

 amysue 2011-11-25

講c中指針和數(shù)組的關系,寫得很好,轉載下

4.3,指針與數(shù)組之間的恩恩怨怨
很多初學者弄不清指針和數(shù)組到底有什么樣的關系。我現(xiàn)在就告訴你:他們之間沒有
任何關系!只是他們經(jīng)常穿著相似的衣服來逗你玩罷了。
指針就是指針,指針變量在32 位系統(tǒng)下,永遠占4 個byte,其值為某一個內(nèi)存的地址。
指針可以指向任何地方,但是不是任何地方你都能通過這個指針變量訪問到。
數(shù)組就是數(shù)組,其大小與元素的類型和個數(shù)有關。定義數(shù)組時必須指定其元素的類型
和個數(shù)。數(shù)組可以存任何類型的數(shù)據(jù),但不能存函數(shù)。
既然它們之間沒有任何關系,那為何很多人把數(shù)組和指針混淆呢?甚至很多人認為指
針和數(shù)組是一樣的。這就與市面上的C 語言的書有關,幾乎沒有一本書把這個問題講透徹,
講明白了。
4.3.1,以指針的形式訪問和以下標的形式訪問
下面我們就詳細討論討論它們之間似是而非的一些特點。例如,函數(shù)內(nèi)部有如下定義:
A),char *p = “abcdef”;
B),char a[] = “123456”;
4.3.1.1,以指針的形式訪問和以下標的形式訪問指針
例子A)定義了一個指針變量p,p 本身在棧上占4 個byte,p 里存儲的是一塊內(nèi)存的首
地址。這塊內(nèi)存在靜態(tài)區(qū),其空間大小為7 個byte,這塊內(nèi)存也沒有名字。對這塊內(nèi)存的訪
問完全是匿名的訪問。比如現(xiàn)在需要讀取字符‘e’,我們有兩種方式:
1),以指針的形式:*(p+4)。先取出p 里存儲的地址值,假設為0x0000FF00,然后加
上4 個字符的偏移量,得到新的地址0x0000FF04。然后取出0x0000FF04 地址上的值。
2),以下標的形式:p[4]。編譯器總是把以下標的形式的操作解析為以指針的形式的操
作。p[4]這個操作會被解析成:先取出p 里存儲的地址值,然后加上中括號中4 個元素的偏
移量,計算出新的地址,然后從新的地址中取出值。也就是說以下標的形式訪問在本質上
與以指針的形式訪問沒有區(qū)別,只是寫法上不同罷了。
4.3.1.2,以指針的形式訪問和以下標的形式訪問數(shù)組
例子B)定義了一個數(shù)組a,a 擁有7 個char 類型的元素,其空間大小為7。數(shù)組a 本身
在棧上面。對a 的元素的訪問必須先根據(jù)數(shù)組的名字a 找到數(shù)組首元素的首地址,然后根據(jù)
偏移量找到相應的值。這是一種典型的“具名+匿名”訪問。比如現(xiàn)在需要讀取字符‘5’,
我們有兩種方式:
1),以指針的形式:*(a+4)。a 這時候代表的是數(shù)組首元素的首地址,假設為0x0000FF00,
然后加上4 個字符的偏移量,得到新的地址0x0000FF04。然后取出0x0000FF04 地址上的
值。
2),以下標的形式:a[4]。編譯器總是把以下標的形式的操作解析為以指針的形式的操
作。a[4]這個操作會被解析成:a 作為數(shù)組首元素的首地址,然后加上中括號中4 個元素的
偏移量,計算出新的地址,然后從新的地址中取出值。
由上面的分析,我們可以看到,指針和數(shù)組根本就是兩個完全不一樣的東西。只是它們
都可以“以指針形式”或“以下標形式”進行訪問。一個是完全的匿名訪問,一個是典型
的具名+匿名訪問。一定要注意的是這個“以XXX 的形式的訪問”這種表達方式。
另外一個需要強調(diào)的是:上面所說的偏移量4 代表的是4 個元素,而不是4 個byte。只
不過這里剛好是char 類型數(shù)據(jù)1 個字符的大小就為1 個byte。記住這個偏移量的單位是元
素的個數(shù)而不是byte 數(shù),在計算新地址時千萬別弄錯了。
4.3.2,a 和&a 的區(qū)別
通過上面的分析,相信你已經(jīng)明白數(shù)組和指針的訪問方式了,下面再看這個例子:
main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
打印出來的值為多少呢? 這里主要是考查關于指針加減操作的理解。
對指針進行加1 操作,得到的是下一個元素的地址,而不是原有地址值直接加1。所以,
一個類型為T 的指針的移動,以sizeof(T) 為移動單位。因此,對上題來說,a 是一個一
維數(shù)組,數(shù)組中有5 個元素; ptr 是一個int 型的指針。
&a + 1: 取數(shù)組a 的首地址,該地址的值加上sizeof(a) 的值,即&a + 5*sizeof(int),也
就是下一個數(shù)組的首地址,顯然當前指針已經(jīng)越過了數(shù)組的界限。
(int *)(&a+1): 則是把上一步計算出來的地址,強制轉換為int * 類型,賦值給ptr。
*(a+1): a,&a 的值是一樣的,但意思不一樣,a 是數(shù)組首元素的首地址,也就是a[0]的
首地址,&a 是數(shù)組的首地址,a+1 是數(shù)組下一元素的首地址,即a[1]的首地址,&a+1 是下一
個數(shù)組的首地址。所以輸出2
*(ptr-1): 因為ptr 是指向a[5],并且ptr 是int * 類型,所以*(ptr-1) 是指向a[4] ,
輸出5。
這些分析我相信大家都能理解,但是在授課時,學生向我提出了如下問題:
在Visual C++6.0 的Watch 窗口中&a+1 的值怎么會是(x0012ff6d(0x0012ff6c+1)呢?

a 在這里代表是的數(shù)組首元素的地址即a[0]的首地址,其值為0x0012ff6c。
&a 代表的是數(shù)組的首地址,其值為0x0012ff6c。
a+1 的值是0x0012ff6c+1*sizeof(int),等于0x0012ff70。
問題就是&a+1 的值怎么會是(x0012ff6d(0x0012ff6c+1)呢?
按照我們上面的分析應該為0x0012ff6c+5*sizeof(int)。其實很好理解。當你把&a+1
放到Watch 窗口中觀察其值時,表達式&a+1 已經(jīng)脫離其上下文環(huán)境,編譯器就很簡單的把
它解析為&a 的值然后加上1byte。而a+1 的解析就正確,我認為這是Visual C++6.0 的一個
bug。既然如此,我們怎么證明證明&a+1 的值確實為0x0012ff6c+5*sizeof(int)呢?很好辦,
用printf 函數(shù)打印出來。這就是我在本書前言里所說的,有的時候我們確實需要printf 函數(shù)
才能解決問題。你可以試試用printf("%x",&a+1);打印其值,看是否為0x0012ff6c+5*sizeof
(int)。注意如果你用的是printf("%d",&a+1);打印,那你必須在十進制和十六進制之間換算
一下,不要冤枉了編譯器。
另外我要強調(diào)一點:不到非不得已,盡量別使用printf 函數(shù),它會使你養(yǎng)成只看結果不
問為什么的習慣。比如這個列子,*(a+1)和*(ptr-1)的值完全可以通過Watch 窗口來查看。
平時初學者很喜歡用“printf("%d,%d",*(a+1),*(ptr-1));”這類的表達式來直接打印出值,
如果發(fā)現(xiàn)值是正確的就歡天喜地。這個時候往往認為自己的代碼沒有問題,根本就不去查
看其變量的值,更別說是內(nèi)存和寄存器的值了。更有甚者,printf 函數(shù)打印出來的值不正確,
就措手無策,舉手問“老師,我這里為什么不對?。俊薄iL此以往就養(yǎng)成了很不好的習慣,
只看結果,不重調(diào)試。這就是為什么同樣的幾年經(jīng)驗,有的人水平很高,而有的人水平卻
很低。其根本原因就在于此,往往被一些表面現(xiàn)象所迷惑。printf 函數(shù)打印出來的值是對的
就能說明你的代碼一定沒問題嗎?我看未必。曾經(jīng)一個學生,我讓其實現(xiàn)直接插入排序算
法。很快他把函數(shù)寫完了,把值用printf 函數(shù)打印出來給我看。我看其代碼卻發(fā)現(xiàn)他使用的
算法本質上其實是冒泡排序,只是寫得像直接插入排序罷了。等等這種情況數(shù)都數(shù)不過來,
往往犯了錯誤還以為自己是對的。所以我平時上課之前往往會強調(diào),不到非不得已,不允
許使用printf 函數(shù),而要自己去查看變量和內(nèi)存的值。學生的這種不好的習慣也與目前市面
上的教材、參考書有關,這些書甚至花大篇幅來介紹scanf 和printf 這類的函數(shù),卻幾乎不
講解調(diào)試技術。甚至有的書還在講TruboC 2.0 之類的調(diào)試器!如此教材教出來的學生質量
可想而知。
4.3.3,指針和數(shù)組的定義與聲明
4.3.3.1,定義為數(shù)組,聲明為指針
文件1 中定義如下:
char a[100];
文件2 中聲明如下(關于extern 的用法,以及定義和聲明的區(qū)別,請復習第一章):
extern char *a;
這里,文件1 中定義了數(shù)組a,文件2 中聲明它為指針。這有什么問題嗎?平時不是總說數(shù)
組與指針相似,甚至可以通用嗎?但是,很不幸,這是錯誤的。通過上面的分析我們也能
明白一些,但是“革命尚未成功,同志仍需努力”。你或許還記得我上面說過的話:數(shù)組就
是數(shù)組,指針就是指針,它們是完全不同的兩碼事!他們之間沒有任何關系,只是經(jīng)常穿
著相似的衣服來迷惑你罷了。下面就來分析分析這個問題:
在第一章的開始,我就強調(diào)了定義和聲明之間的區(qū)別,定義分配的內(nèi)存,而聲明沒有。
定義只能出現(xiàn)一次,而聲明可以出現(xiàn)多次。這里extern 告訴編譯器a 這個名字已經(jīng)在別的文
件中被定義了,下面的代碼使用的名字a 是別的文件定義的。再回顧到前面對于左值和右值
的討論,我們知道如果編譯器需要某個地址(可能還需要加上偏移量)來執(zhí)行某種操作的
話,它就可以直接通過開鎖動作(使用“*”這把鑰匙)來讀或者寫這個地址上的內(nèi)存,并不
需要先去找到儲存這個地址的地方。相反,對于指針而言,必須先去找到儲存這個地址的
地方,取出這個地址值然后對這個地址進行開鎖(使用“*”這把鑰匙)。

這就是為什么extern char a[]與extern char a[100]等價的原因。因為這只是聲明,不分配
空間,所以編譯器無需知道這個數(shù)組有多少個元素。這兩個聲明都告訴編譯器a 是在別的文
件中被定義的一個數(shù)組,a 同時代表著數(shù)組a 的首元素的首地址,也就是這塊內(nèi)存的起始地
址。數(shù)組內(nèi)陸任何元素的的地址都只需要知道這個地址就可以計算出來。
但是,當你聲明為extern char *a 時,編譯器理所當然的認為a 是一個指針變量,在32 位系
統(tǒng)下,占4 個byte。這4 個byte 里保存了一個地址,這個地址上存的是字符類型數(shù)據(jù)。雖
然在文件1 中,編譯器知道a 是一個數(shù)組,但是在文件2 中,編譯器并不知道這點。大多數(shù)
編譯器是按文件分別編譯的,編譯器只按照本文件中聲明的類型來處理。所以,雖然a 實際
大小為100 個byte,但是在文件2 中,編譯器認為a 只占4 個byte。
我們說過,編譯器會把存在指針變量中的任何數(shù)據(jù)當作地址來處理。所以,如果需要
訪問這些字符類型數(shù)據(jù),我們必須先從指針變量a 中取出其保存的地址.

4.3.3.2,定義為指針,聲明為數(shù)組
顯然,按照上面的分析,我們把文件1 中定義的數(shù)組在文件2 中聲明為指針會發(fā)生錯誤。
同樣的,如果在文件1 中定義為指針,而在文件中聲明為數(shù)組也會發(fā)生錯誤:
文件1
char *p = “abcdefg”;
文件2
extern char p[];
在文件1 中,編譯器分配4 個byte 空間,并命名為p。同時p 里保存了字符串常量“abcdefg”
的首字符的首地址。這個字符串常量本身保存在內(nèi)存的靜態(tài)區(qū),其內(nèi)容不可更改。在文件2
中,編譯器認為p 是一個數(shù)組,其大小為4 個byte,數(shù)組內(nèi)保存的是char 類型的數(shù)據(jù)。

4.3.4,指針和數(shù)組的對比
通過上面的分析,相信你已經(jīng)知道數(shù)組與指針的的確確是兩碼事了。他們之間是不可
以混淆的,但是我們可以“以XXXX 的形式”訪問數(shù)組的元素或指針指向的內(nèi)容。以后一
定要確認你的代碼在一個地方定義為指針,在別的地方也只能聲明為指針;在一個的地方
定義為數(shù)組,在別的地方也只能聲明為數(shù)組。切記不可混淆。下面再用一個表來總結一下
指針和數(shù)組的特性:

指針:
保存數(shù)據(jù)的地址,任何存入指針變量p 的數(shù)據(jù)都會被當作地址來處理。p 本身的地址由
編譯器另外存儲,存儲在哪里,我們并不知道.

間接訪問數(shù)據(jù),首先取得指針變量p 的內(nèi)容,把它作為地址,然后從這個地址提取數(shù)據(jù)或向這個地址寫入數(shù)據(jù)。指針可以以指針的形式訪問*(p+i);也可以以下標的形式訪問p[i]。但其本質都是先取p 的內(nèi)容然后加上i*sizeof(類型)個byte 作為數(shù)據(jù)的真正地址。

通常用于動態(tài)數(shù)據(jù)結構
相關的函數(shù)為malloc 和free。

通常指向匿名數(shù)據(jù)(當然也可指向具名數(shù)據(jù))

數(shù)組:

保存數(shù)據(jù),數(shù)組名a 代表的是數(shù)組首元素的首地址而不是數(shù)組的首地址。&a 才是整個數(shù)
組的首地址。a 本身的地址由編譯器另外存儲,存儲在哪里,我們并不知道。

直接訪問數(shù)據(jù),數(shù)組名a 是整個數(shù)組的名字,數(shù)組內(nèi)每個元素并沒有名字。只能通過“具
名+匿名”的方式來訪問其某個元素,不能把數(shù)組當一個整體來進行讀寫操作。數(shù)組可以
以指針的形式訪問*(a+i);也可以以下標的形式訪問a[i]。但其本質都是a 所代表的數(shù)組首
元素的首地址加上i*sizeof(類型)個byte 作為數(shù)據(jù)的真正地址。

通常用于存儲固定數(shù)目且數(shù)據(jù)類型相同的元素。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    久久大香蕉一区二区三区| 成人精品视频一区二区在线观看| 97人摸人人澡人人人超碰| 欧美大胆女人的大胆人体| 91天堂素人精品系列全集| 精品人妻一区二区三区四区久久| 欧美av人人妻av人人爽蜜桃| 国产麻豆精品福利在线| 日本午夜免费福利视频| 亚洲妇女作爱一区二区三区| 日韩av生活片一区二区三区| 色综合伊人天天综合网中文| 国内精品偷拍视频久久| 护士又紧又深又湿又爽的视频| 开心激情网 激情五月天| 91欧美日韩中在线视频| 亚洲欧美视频欧美视频| 国产亚洲成av人在线观看| 中文人妻精品一区二区三区四区| 国产av一二三区在线观看| 日韩无套内射免费精品| 日本丁香婷婷欧美激情| 亚洲黄香蕉视频免费看| 国产老熟女超碰一区二区三区| 爱在午夜降临前在线观看| 五月婷婷六月丁香亚洲| 亚洲男人的天堂就去爱| 草草夜色精品国产噜噜竹菊| 亚洲精品国产美女久久久99 | 亚洲视频在线观看你懂的| 亚洲专区中文字幕在线| 国产精品免费福利在线| 精品一区二区三区中文字幕| 久久精品国产亚洲av麻豆| 青青操视频在线播放免费| 亚洲男人天堂成人在线视频| 在线视频免费看你懂的| 九九热精品视频免费在线播放| 美女被草的视频在线观看| 嫩草国产福利视频一区二区| 在线观看欧美视频一区|