前言:
? 小編感覺最近有點太墮落,于是我開始從事這篇文章的撰寫,現在也是進入七月份了,我現在文章開頭定一個小目標,我決定在七月份發布至少十篇文章,希望我可以說到做到(我前面就口頭欠了不少文章),好了,不多廢話了,下面進入今天的主題,對于指針習題的演練,這些題都是小編在上課時聽到的題,為了加強自己的記憶,于是我將此作為文章的主題,下面進入正文嘍!
目錄:
1.題目一
2.題目二
3.題目三
4.題目四
5.題目五
6.題目六
7.題目七
?正文:
1.題目一
int main()
{int a[5] = { 1, 2, 3, 4, 5 };int* ptr = (int*)(&a + 1);printf("%d,%d", *(a + 1), *(ptr - 1));return 0;
}
//程序的結果是什么?
? 各位先來思考一下這個題怎么做,老規矩,小編依舊是先解釋,后做題,下面進入解釋環節:首先我們先看printf函數,里面第一個元素是*(a + 1),此時a是數組名,它不是那兩種特殊情況,所以此時數組名代表的就是數組第一個元素,加1后對應著數組第二個元素的地址,在進行解引用操作后,會直接呈現第二個元素,那么就是2了;之后是第二個元素,此時涉及到了ptr,所以我們先看ptr這個指針,它代表著進行整型強制類型轉換以后的(&a + 1),不難發現,此時數組名符合了第一種特殊情況,所以代表著整個數組的地址,加1后是數組最后一個元素下行的地址,此時指針指向的應該是5后面元素的地址,此時減去1后,又回到了數組第五個元素代表的地址,所以進行解引用操作后,變成了5,為了讓讀者更好的理解這個題目,小編特意的給了圖例進行解釋:
?
? 通過上圖可以清晰的看出小編在文字解釋中所解釋的內容,下面小編展示這個代碼的運行圖:
? 可以看出小編解釋的是正確定,下面廢話不多說,進入下一個題的訓練:
2.題目二?
//在X86環境下
//假設結構體的??是20個字節
//程序輸出的結果是啥?
struct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1);printf("%p\n", (unsigned int*)p + 0x1);return 0;
}
? ? 老規矩,大家先思考幾秒,然后給出解釋,下面開始解釋環節:首先映入我們眼簾的是一個結構體,由于結構體的大小如何計算小編還沒有設計到,所以這里直接給大小了(后面小編會著重強調它的對齊方式的在結構體的文章中),這里我們已經給出了結構體的地址,不過這個地址得強制類型轉換,不然就不是結構體的地址了,下面我們正是看看函數部分,首先是第一個printf函數,這個函數內部是結構體指針加1,記住這個加的1是0x開頭的,所以應該是16進制的方式進行操作的,此時p是指針,指針加數的話代表著它的地址是要加它本身大小倍數的數,所以此時應該加20個字節,并且由于這是16進制儲存的,所最后的結果應該是0x100014;對于第二個printf函數,首先我們先看括號內容,此時括號內容是unsigned long,所以此時的p已經強制類型轉換成無符號的長整型了,此時p可以看作一個整數,對于整數加1的話,那就是真的加1,所以結果應該是0x100001;對于第三個printf語句,此時括號里面的內容是unsigned int*,代表著強制類型轉換成無符號整型的指針,所以此時指針每次加的大小已經改變了,此時加1代表著加4個字節的空間,所以結果應該是0x100004,這個問題小編認為不需要圖來了解,因為這沒有牽扯到指針的遷移,僅僅就牽扯到了指針運算,下面來看看此代碼的運行圖:
? ?讀者朋友無需關注前面是什么,光看末尾數字就好了,此時說明小編解釋沒有錯誤,那么我們加快步伐,直接開始看下一個題的講解!
3.題目三?
int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };int* p;p = a[0];printf("%d", p[0]);return 0;
}
? 老規矩,各位先看會題目,我等會做出解釋,下面開始解釋:首先這個題定義的二維數組,然后設置了一個整型指針,此時指針指向二維數組第一行的地址,然后printf函數里面的是p[0],此時其實有第二種寫法,就是*(*p + 0),此時不難看出,這個是指向數組第一個元素的地址,那么我們回頭看看這個二維數組,可能有些讀者會脫口而出,答案是0,但是其實這個說法是錯誤的,仔細看,數組里面每兩個元素用什么圍起來的,是括號,而不是大括號!所以此時我們對于括號里面的內容,其實是逗號表達式,逗號表達式的邏輯是,從左往右依次計算,不過最后的結果是右邊的數,所以其實這個數組在內存中的存放是如下圖所示的:
? 所以可以知道打印出來的結果應該是1,那么很多讀者可能忘記了我們想要規定二維數組行和列具體數應該怎么做,其實這里是要用大括號的,下面是代碼展示:
int a[3][2] = { {0,1 }, {2,3} , {4,5} };
? 下面是在調試界面此數組的存放:
? 所以各位在做相關習題的時候一定要瞪大雙眼,防止出題人給你設置成一個大坑讓你跳進去,我們要做到看到坑直接繞過去,下面不多廢話直接上運行頁:
? ?可以看出小編并沒有說錯,大家一定要在做題不要看題目過于簡單而痛失分數。下面不多廢話,調整心態,下個題來嘍:
4.題目四
//假設環境是x86環境,程序輸出的結果是啥?
int main()
{int a[5][5];int(*p)[4];p = a;printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);return 0;
}
? 這個題一看就比上面的題復雜了很多,畢竟難度是相加的,在講解這個題之前,小編還想告訴大家,大家在做指針相關習題的時候一定要記得畫圖,畫圖可以幫助我們更好的了解這個題怎么做,小編就在每次在牽扯到指針計算的時候,都會畫上圖來幫助大家理解,其實很顯然,圖文要比文字更好的來幫助人們去理解知識,所以大家在做題的時候一定要畫圖,下面來開始解釋環節了。
? 由于這個題太過復雜,小編先放上a數組的存放圖文來幫助大家去感受a數組內容:
? ?下面我們來正式開始觀看這個代碼,首先我們看到一個老朋友:int (*p)[4],讀者朋友如果已經學完了指針部分,那么很容易看得出這個是數組指針,p代表的是數組,而[4]則代表著里面存放著四個元素,下面我們可以接著看,此時把a首行的地址給了p,此時讀者朋友可能會想,a每個里面存放著5個元素,而p里面卻存放著4個元素,它們如何建立起聯系呢?其實這里不用多考慮,首先,p其實在這里我們可以看做成地頭蛇,有句老話這么說,在我的地盤,是龍只能盤著,是虎也得趴著,所以此時每個p里面都存放每個a所對應的四個元素,此時小編也將p在內存中的存放也放到下面了:
? 下面我們已經畫好了a和p的圖,此題其實已經完成了大半部分了,下面我們來看看printf函數里面的內容,這兩個元素是一樣的,只不過是兩種形式表示著,一個是地址,一個是整型,下面我們先來看看元素是什么,分別是p[4][2]和a[4][2],如果沒有畫圖,可能很難看出來這個具體表示著什么,但是現在我們有了圖,這就好辦了,下面是小編利用圖來表示這兩個元素?
? 此時printf里面存放著是這兩個數的地址,小編之前說過,指針和指針相減,得出來的結果是兩個指針之間相差的元素的個數,這個小編之前寫過的文章說過,下面放上鏈接,感到興趣的讀者朋友可以看一下:深入理解并打敗C語言難關之一————指針(1)-CSDN博客,回歸正題,這兩個數之間相差的個數是4個,但這個是前者減去后者,所以得出來的結果應該是-4,不過現在的重點是第一個元素是要取到它的地址,所以這里牽扯到了原碼反碼補碼的知識,這些知識小編也會在以后的文章說的(欠的文章+1),所以下面通過代碼頁來展示-4的反碼:
//10000000000000000000000000000100 //原碼
//11111111111111111111111111111011 //反碼 (原碼取反)
//11111111111111111111111111111100 //補碼(反碼 + 1)
? 所以的出來結果應該是下面源碼4個為一起通過16進制位數表示出來,應該是0xFFFFFFFC,下面這個題已經講解完畢,下面進入此題目的運行環節:
? 可以看出小編這里并沒有說錯,這個題有點復雜,讀者朋友們如果理解不了的話一定要多看一遍,下面趁熱打鐵,進入下個題目嘍!
?5.題目五
int main()
{int a[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int* ptr1 = (int*)(&a + 1);int* ptr2 = (int*)(*(a + 1));printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));return 0;
}
? 如果會了上一個題的話,這個題也是會迎刃而解的,老規矩,讀者朋友先思考下這個題,等會小編做出解釋,三,二,一,下面給出解釋:
? 首先,印入我們眼簾的還是一個二維數組,下面小編先來畫一下二維數組的儲存圖來幫助各位取理解:
? 這個二維數組的存放就是如圖所示的(為了直觀小編直接展開成一行了),我們開始看代碼頁,首先我們先看到的是兩個指針,ptr1和ptr2,首先第一個是先對數組名進行取地址操作,此時涉及到了小編之前提到過的特殊情況,所以此時是取到了整個數組的地址,在進行加1就是去到了10以后元素的地址,此時還對他強制類型轉化成了整型指針(可能很多讀者朋友會很奇怪,認為二維數組不也是整型指針嗎,其實二維數組實質是一個數組指針,小編以前可能提到過,如果沒提到過大家記住就好),在看ptr2,此時這個指針同樣也是對括號里面的內容強制類型轉化成整形指針了,下面我們再來看看括號里面的內容,此時是先對a進行加1操作,我們知道,二維數組數組名代表著第一行的地址,所以這個進行加1操作以后直接變成了a[1],在進行解引用操作以后直接變成了第一個元素所代表的地址了,下面為了讓讀者朋友更好的理解這個題目,直接上圖:
? 之后我們繼續往下看,我們來看看printf函數,此時里面同樣也是有兩個元素,第一個是對ptr1 - 1,此時ptr已經是整形指針了,所以減1后對應著10這個元素的地址,然后解引用后會變成10;第二個是ptr2 - 1?,此時ptr2也是整形指針,減去1后代表著5的地址,解引用后是5,下面我們先用圖文解釋,在來放運行圖:
?
? 可以看出小編并沒有說錯,大家在寫題目的時候一定要畫圖,畫圖可以保證大家做題目時更有思路,提高正確率,下面我們進入下一道題:
6.題目六
?
int main()
{char *a[] = {"work","at","alibaba"};char**pa = a;pa++;printf("%s\n", *pa);return 0;}
? 這個題可能會有一點難度,讀者朋友一定要先仔細看看這個題,畫出相應的圖這個題就好;理解多了,下面小編放上這個題的解析:
? 首先,我們看到了一個數組我,這個數組的類型是char *,所以很顯然這個數組是個指針數組,下面我們圖解這個指針數組:
? 通過上圖可以清晰的看出這個指針數組的具體內容,之后我們接著看代碼,后面的代碼是又設置了一個二級指針pa,這個二級指針儲存著此指針數組首元素地址的地址,接著看下面的代碼,此時是pa++,代表著pa + 1,所以此二級指針變成了儲存第二個元素的地址的地址,然后我們繼續看下面代碼,看出其元素是對二級指針進行解引用,我們知道,二級指針解引用后就變成了其儲存元素的地址,此時由于儲存著字符串,對字符串的打印我們需要獲取字符串的地址,此時正好是第二個元素的地址,所以應該打印出的是"at",下面小編先給圖解,然后給運行圖:
?
? 以上便是運行結果和圖解,所以小編這里又一次得強調下,對于這種題一定要畫圖,畫圖可以幫助我們解決大部分我們無法解決的問題,下面我們趁熱打鐵,迎接本篇文章最后一個習題,此習題與本習題同一個做法,大家一定要理解好這個題的做法:?
題目七
int main()
{char* c[] = { "ENTER","NEW","POINT","FIRST" };char** cp[] = { c + 3,c + 2,c + 1,c };char*** cpp = cp;printf("%s\n", **++cpp);printf("%s\n", *-- * ++cpp + 3);printf("%s\n", *cpp[-2] + 3);printf("%s\n", cpp[-1][-1] + 1);return 0;
}
? 這個題的難度比上面的難度還要大,大家先看看這個代碼,自己畫圖思考一下,下面小編著重進行解釋了:
? 首先,我們不難發現了這里牽扯到了三個指針,分別是指針數組,二級指針,三級指針,各位先看好這幾個指針,下面小編直接上這三個指針的圖解了,畢竟一個一個寫顯得我有點水字數了:
?
? 下面我們來看看printf語句里都有什么元素,首先看第一個printf里面的元素,里面是 ** ++ cpp,首先我們知道++操作符運算級是很高的,所以此時是cpp先 加 1,然后此時指向了cp[1]的地址,之后二次解引用操作后,變成指向了c[2]的地址,所以是打印了c[2]所代表的字符串,也就是"POINT",下面小編給上圖解:
? ?下面我們再來看看第二個printf函數里面的元素,這個更是復雜:*-- * ++cpp + 3,此時我們一個一個的看,此時是先進行 ++ cpp的操作,經過上面的printf里面cpp進行加1后,此時在進行加1,cpp指向了cp[2]的地址,此時進行解引用操作后就會變成cp[2],之后我們在進行--操作,此時是代表著cp[2]所指向的內容減1,所以此時cp[2]變成了指向c[0]的地址,再次進行解引用操作以后,此時就是變成了c[0],此時再次進行 + 3操作,代表著數組里面的元素加3,所以我們打印數據就開始從E進行打印,所以最后打印的結果應該是“ER”,下面給上圖解:
? ? ? 下面我們繼續來看看下一行的代碼,此時里面的元素是 :?*cpp[-2] + 3 ,我們一九從左往右看,此時是先對于cpp進行解引用操作,由于上面進行了兩次 ++ 操作,所以解引用cpp以后里面的元素變成了cp[2],此時有一個[]操作符,其實這個就是再次對cp進行解引用,不過是減2后再解引用,所以此時變成了cp[0],進行解引用后就指向了c[3],此時再次加3代表著里面元素地址加3,所以是從“S”開始打印,最后打印出來的應該是:"ST",下面依舊給上圖解來幫助大家理解:
? 下面我們再來看最后一個printf函數里面的元素,里面是:cpp[-1][-1] + 1 ,我們依舊從左往右看,此時cpp[-1]其實實際代表著* cpp - 1,所以此時是代表著cp[1],之后的[- 1],代表著 * cp[1] - 1,也就是說最后指向的是c[1],此時在進行 + 1操作后,就代表著從“E”開始進行打印,最后呈現的結果是 “EW”,下面我們依舊用圖文進行解釋:
? ?下面我們來放運行結果:
? ? ?可以看出雄小編這個題并沒有說錯,這個題大家一定要好好的去了解,難度系數還是比較大的,這個題也是同樣彰顯了畫圖的重要性,大家在做題時一定要好圖,這樣才不容易出錯,也好找到自己的錯誤。
總結:
??小編也是干完了這篇文章,這也是指針部分的強化篇,指針講解部分到這里就徹底的結束了,相信各位讀者朋友看到小編寫過的指針篇幅,大概也知道了指針的重要性,大家一定要好好掌握指針,到現在開始,我們已經返過了C語言的一座大山,希望各位已經明白了指針。最后,如果文章有什么錯誤,請大家一定在評論區指出,小編會改正自己錯誤的,那么,我們下篇博客見啦!?