天天看點

C語言數組與指針詳解

好多同學都說:“老師,我看到指針就暈!”,說實話,見過暈血的,見過暈車的,暈指針的到是第一次聽說!

       我們先來分析一下暈車的原理,再來對比一下為什麼暈指針。

       暈車,是因為耳朵裡的一個器官對外界的振動太敏感,導緻身體調節功能紊亂,系統不能正常工作,輕則,暈點,中則,吐點,重則,“重新開機”(倒地)。時間長了,知道自己暈車,于是,看到公共汽車,taxi,火車,自行車,都暈!這樣的病就大了,上升到心理疾病了。這可得治。我們都明白上面的道理,怎麼克服暈車呢?你不可能避免做車,你要去面對,怎麼克服暈車也很簡單,先是功心,“口服不如心服”,是以先治心病:建立信心。經常試着去做晃動不大的車,最好聽着mp3,這樣能減緩心理對暈車的敏感度。時間長了,慢慢的克服了心理作用,然後就要上升到治根的階段了,要去試着挑戰做公交,taxi,船。

       上面都是個人的一些見解,上大學時,要是不學計算機就去學醫了!白衣王子嗎(天使算不上,爺們嗎,王子就行了)。

       回來我們開始治“暈指針”這号病,其實很多同學暈指針,也是因為自己基礎不好(身體素質不好),剛開始學的時候,不用心,還沒有弄的很明白,一程式設計(有的同學4年都沒編過一個程式)就錯,形成了恐懼心理,其實c指針非常靈活,它對學生的要求也比較高,說白了,它要求有計算機的組成原理一些基礎,如果有的話,你回頭看指針,so easy!

       我們先開始“攻心”。

1.         暈指針,唐式偏方一:“投石問路”

#include <stdio.h>

int main(void)

{

       char* str = "abcdefghijkl";

       int* pint = (int*)str;

       printf("%c\n%c\n",*(str+1),*(char *)(pint+1));

       return 0;

}

       上面的程式如果你要是灰常easy的回答出來,那你的功底已經不錯了!心病是估計沒有了,可以進入吃藥保養階段了(做題)。

如果上面的題看起來就特别費勁,看來你暈的不輕,得治。要不問題會很嚴重。

基于上面的小測試,進入唐式第二方:“理氣靜心”

       我們先看把c指針的基礎打好。在這之前先來複習下變量和常量,有的同學會問,這是為什麼,我隻能告訴你,你暈c指針,是因為你從剛開始有問題就沒有重視,或者你忽略了問題的嚴重性,導緻現在的情況,這也沒有辦法,中國的教育就不重視這一塊,說實話,大學裡講c語言的,很多都沒有太多的講到變量和常量,這是很xxx的。我們開始治療。

 常量:其值不發生改變的量稱謂常量。常量又稱為字面量,表述常數。它們可以和資料類型結合起來分類,比如:整形常量,浮點型常量,字元常量= =,常量是可以不經過定義和初始化,而直接引用的。

常量又分為:直接常量和符号常量。

直接常量又叫做:字面常量。如12,0,4.6,‘a‘,“abcd”

符号常量如宏定義的:#define pi 3.14

特點:常量的值在其作用域内不會發生改變,也不能再被指派。其在出現時就被當作一個立即數來使用。也就是說,隻能被通路,被讀,而不能被寫,被指派。

 變量:其值可以改變的量稱這變量。一個變量應該有一個名字,在記憶體中占據一定的存儲單元。變量在使用前必須要定義。

 變量名和變量的值:

變量名是在,變量的聲明的時候,該名字就和記憶體中一塊位址綁定在一起了。可以通過變量名直接找到對應的記憶體區域,也可以通過位址找到其記憶體區域。是以有了引入指針的依據。

變量的值是變量所對應的記憶體區域記憶體放的二進制序列。當該變量被聲明成整形時,記憶體區域的二進制序列被以整形的形式翻譯出來。比如:int a = 97; 其在記憶體中是以97的二進制形式存放的,當使用時,他會被以10進制形式表現出來。同樣的char a = ‘a’; a的asiic碼是97,也是以97的二進制存放的,使用時,會被以字元a的形式表現出來。

如果變量是一個指針變量,那麼指針變量裡的二進制序列被翻譯成一個位址,

比如:

       int a = 10;

       int * p;

       p = &a;

這裡的指針變量p的值是a的位址(p = &a),它是什麼啊?看下圖:

(說實話哥們圖畫的不錯,大學自學過ps,還tmd的拿過獎)

C語言數組與指針詳解

聲明了一個變量a,它是整型,被指派為10(它的值被翻譯成整數),要形成這種思維,時間長了你就知道這樣做的好處了,又聲明了一個指針變量p,它是int類型的(它指向的位址裡面要裝int),然後将a變量的位址(ox2c406b24)給了p,這兒注意下。現在通路a裡面的值有了兩種方式(其實本來也有這兩種),一個是通過變量名a(綁定的),一個是通過位址ox2c406b24,位址ox2c406b24給了p了,p指針變量(指針變量是變量,這個思維很重要)裡面存放的是ox2c406b24(a的位址),那麼現在通路a可以通過:

       printf("%d\n", a);    //通過變量名

       printf("%d\n", *p);    //通過指針變量

如果你現在上面的都很明白了,那你有了暈指針好轉的迹象,隻是迹象,(迹象だけです^_^)。現在驗證下是不是真的有好轉:

1、

char ch = 'a';

int a = (int)ch;

printf("%d %c\n", a, ch);

ch是什麼? ch 裡面是什麼? a是什麼? a裡面又是什麼?列印什麼?

2、

int add = 0x123456;

int * p = (int*)add;

add是什麼?add裡是什麼? p是什麼? p裡面是什麼? *p 又是什麼?

嘿嘿,暈不?别慌,再來。。。

3、

#define pi 3.14

int a = pi;

printf("%d\n", a);

上面的程式有沒有問題?

4、

printf("%d\n", pi);

程式有沒有問題?

5、

pi = 1.85;         

int b = pi;

printf("%d%d\n", a, b);

差不多頭疼的不行了吧,沒事,這是藥勁,好藥都這樣。

最後一個:

6、

char *str = "abcdef";

printf("%s\n", str);

*str = "fedcba";

str[2] = 'c';     //修改第三個字元為大寫

好,第一方到此為止,老中醫要休息會。

上一方藥勁比較重,年輕人嗎,口味要重點。現在給你們點解藥:

1、主要是測試類型轉換,還有對變量的了解是否到位。

2、對變量的值的了解是否到位

3、對常量,宏替換的了解是否到位

4、對宏替換的了解是否到位

5、能否修改常量的值?

6、能否修改字元串常量的值?

通過上面我們可以學到以下内容,重點,記下,考試要考(這話在學校裡很耳熟,但是xxx的這樣很不負責任):

       1、不管什麼常量,其值是編譯是固定好的,不能再被改變

       2、變量裡的值,和其資料類型沒有關系,它隻是一個二進制序列,不要将電腦想的多聰明,它隻認識0, 1,隻不過,這一堆01被其類型限定了其代表的意義,類型為整形就是其值,指針位址類型,就是記憶體裡的一個位址,字元型,就是其無符号整形代表的ascii碼。

      3、字元串,是常量(字元指針指向一字元串,不是數組,數組和指針的差別在後面),其值不能再被改變,char * str = "abcdef";這行代碼的意思是告訴編譯器:老編啊,我這兒有個抽屜(指針變量str),你給我找個房間(存放字元串的記憶體空間),找到後,把鑰匙給我放到這個抽屜裡。老編去找客房經理,找到後問:有沒有空房間啊,有的話給我一間,一哥們要開房,客房經理去查房間入住情況(記憶體管理),最後說,這兒有一間,它是老總的房間,你要不先用着,你用沒事,你可别亂動裡面的東西,你要是動的話,估計你那哥們就被kill掉,老闆是黑社會的,不過,你用沒事。于是,老編将老總房間鑰匙給我放到了抽屜(指針變量str)裡,這樣,我打開抽屜(指針變量),拿出房間(記憶體空間)鑰匙(指針),去開房了,老編告訴的,别亂動,用沒事(讀取),一定不能破壞裡面的結構(修改資料内容),要是亂動(修改資料),會出亂子的(段錯誤),然後你會被kill的(異常結束程式)。如果有兩個人,就會double kill,老闆就god like了。

明白了常量和變量的差別後,再進行後面的學習就輕松點了!下面我們來看下指針的雙胞胎哥們,數組。一般暈車人裡面有很多也暈飛機,暈船,是以呢,暈指針的,也一般都會暈數組。

 數組:我們先咬文嚼字一下,數指資料,組,就是小組,由資料組成的小組,就叫數組(學計算機的,不光要學好計算機,也要學好國文,偶爾還要懂點醫學,你要是經常看計算機圖書,你看台灣人寫的書,都有很多文言文在裡面,我們這點做的完全沒有台胞做的好,是以人家計算機牛啊,世界上的電子代工,台灣占了絕大數比例)。

數組有幾個特點一定要注意,看到數組就要想到:

1、  數組裡面的資料類型是相同的,小組裡面的成員肯定要一樣的啊,驢堆裡站一馬,驢馬不分,雞窩裡蹲一丹頂鶴,鶴立雞群,這都是不合群的東西,計算機裡面也講“和諧”。我們順便看下資料的基本類型:int float double char 這些是基本類型,是以它們可以存在以下類似數組:int a[10], float f[10], doule d[10], char str[10]; 它們每一個都有10個元素,每一個元素的類型都是其前面聲明的類型。我們是學嵌入式的,我們不能光看到表面的東西,我們要看到底,好東西是走了光才叫好,光有好東西,别人不知道也不行。其實數組在記憶體中是連續配置設定的,如下圖:

C語言數組與指針詳解

定義了一char型數組a,它有6個元素,分别是'a', 'b', 'c', 'd', 'e', 'f',它們在記憶體裡面是連續存放的,每個元素占用一個位元組。 強烈譴責那些将'a' 當成"a"的人,前者是單個字元,後者是字元串,鄙視那些,問“字元和字元串有什麼差別?”的人,不知道字元和字元串的差別,那你吃過羊肉串嗎?單個羊肉塊能叫串嗎,多個羊肉塊串起來才叫串,是以字元是單個,字元串可以是多個字元組成的數組(最後有一個結束符号\0),羊肉塊串起來一烤就叫烤羊肉串,字元串一拷,叫拷貝串,學計算機哪有那麼簡單,要是我開個學校,入學前要體檢,測視力,量身高體重,....,三圍什麼的,不行的pass。

       仔細看上面的圖,每一個字元都有一個位址,它們的跨度是1(位元組),數組的每個元素都可以通過下标來通路,下标(index, for循環變量經常用i就是因為這個東西)其實就是他在數組中的位置,也就是他的号,拉10個人過來,報數,1,2,3.....,隻不過,c語言裡數組的下标是從0開始的,在計算機裡面能通路的最小機關就是位元組了,也就是位址隻能找到以位元組為機關,不能再精确了。數組名a和變量名道理上是一樣的,在編譯時就和數組的首位址綁定上了,a就是數組的首位址,變量名和數組名其實都是友善人們記憶而取的代号,它在代碼反彙編後,其實不存在變量名的,回想下,通路資料有兩種方式,既然反彙編後的代碼不存在變量名,隻能通過那種方式通路資料了,那就是位址。數組中的每一個元素,可以被看成一個變量(回想下變量的特點),是以其可以被讀,寫,修改,愛怎麼得瑟怎麼得瑟,你隻要不把房間拆了(記憶體空間),怎麼折騰都行。每一個元素的位址都可以通過首位址的偏移量(offset這個詞記住,四級裡沒有)來算出來,這個偏移量說白了就是下标了。比如上面的圖中:'c'所在位址0x28c5,相對首位址0x28c3的偏移量是2,那a[2]也可以通路'c'了,注意一點,a代表數組,代表數組的首位址,代表數組第一個元素的位址,這“三個代表”一定要記住。那a+1呢,a是三個代表,那它是第一代表還是第二個代表,還是第三個代表呢?這兒的a應該是第一個元素位址的意思表示的意思應該是&a[0],a+1是個位址的算術運算,而數組是個一維數組,數組中每個元素都是一個字元,a+1就是a目前位址0x28c3的下一個元素的位址0x28c4(結合圖來看),也就是'b'的位址,如果a是一個二維數組名的話,那麼二維數組被編譯器了解為一個一維數組,一維數組裡的每一個元素是一個一維數組,有點亂,别慌,屢一下,看下圖:

C語言數組與指針詳解

a是個二維數組,它有3x4=12個字元元素,而編譯器将它認為是一個一維數組,它有三個元素,分别是a[0], a[1], a[2], 每一個元素是一個含有4個字元的數組,那麼a+1的話,是&a[0]的位址,a[0]是一個一給數組,取了一個數組的位址,再加1 ,肯定就是下一個數組的位址了,就是a[1]上圖,那麼這個時候它的位址增加可不再是1了,而是列數4,也就是說是a這個二維數組中元素(一維數組)的長度。

       回到前面那個圖:a[5] - a[3] = ? 位址的運算,這裡算的也是元素的相對偏移量,結果當然是2,雖然0x28c8- 0x28c6 = 2結果也是2,不過意義不一樣,如果數組類型換成int a[10], 那麼a[5] - a[3] 還是2,不是8,這個直接用下标相減就對了。

       是以我們可以總結一下,指針相加減時,要看類型,打狗看主人,指針加減看類型,其運算的值n*sizeof(類型),比如:字元型位址加1 ,其實位址加也是sizeof(char) = 1,整形位址加1,位址加sizeof(int) = 4,結構體數組中,位址加1,位址加sizeof(結構體)。

       通過上面的分析可以看出,數組有很多地方很相似,其實,編譯器這哥們處理數組的時候就是将其看成指針來處理的,沒有辦法,編譯器隻認位址,變量名一直都是被編譯器藐視的。

       看一個程式圖:

C語言數組與指針詳解

程式圖這個名詞是我自創的,有版權的,因為好多東西說不清,道不明,一個圖全搞定。

上面的執行結果是什麼?已經很清楚了。休息會。

       上面的例子結果是:b和e