目錄
前言
1、案例一
1.1 答案
1.2 解析
2、案例二
2.1 答案
2.2 解析
3、案例三
3.1 答案
3.2 解析?
4、案例四
4.1 答案
4.2 解析
5、案例五
5.1 答案
5.2 解析
總結
前言
? ? ? ? “紙上得來終覺淺,絕知此事要躬行”。本篇通過對指針實際案例的分析,由淺入深,來加強我們對指針的理解。
1、案例一
#include <stdio.h>
int main()
{int a[5] = { 1,2,3,4,5 };int* ptr = (int*)(&a + 1);printf("%d,%d", *(a + 1), *(ptr - 1));return 0;
}
? ? ? ? 先自己想想,下面公布答案。
1.1 答案
????????答案是2,5。
1.2 解析
? ? ? ? 其實第一個輸出的2比較好理解,主要是第二個輸出可能會有疑問。
? ? ? ? 對于*(a+1),本身a表示數組的首元素地址,a+1表示數組的第二個元素的地址,因此解引用a+1得到的結果就是數組的第二個元素的值:2。(解引用就是*)
? ? ? ? 對于*(ptr-1),首先我們看看ptr是什么,前面定義 ptr = (int*)(&a + 1); 首先,&a表示將整個數組的地址取出來,其類型為 (int*)[5],大小是5個整型的大小,所以&a+1直接跳過整個數組,指向數組后面一個位置的地址,但是最后又通過(int*)強制將其轉化成了int*類型,步長變回一個整型的大小,所以ptr其實就是指向數組最后一個元素后面一個位置地址的指針,類型為(int*)類型,步長為1個整型數據的大小。因此,輸出時,ptr-1其實只往回跳了一個整型大小的長度,指向了數組第五個元素(最后一個元素),再解引用得到的答案就是5啦。
2、案例二
#include <stdio.h>
struct Test
{int Num;char* pcName;short cha[2];short sBa[4];
}*p = (struct Test*)0x100000;
//假設p 的值為0x100000。如下表達式的值分別為多少。
//已知,結構體Test類型的變量大小是20字節。
//x86環境
int main()
{printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1);printf("%p\n", (unsigned int*)p + 0x1);return 0;
}
????????這是一道結構體指針的運算,定義結構體*p,其值設定為0x100000。在x86環境下,進行了三種不同的運算,分析結果。
2.1 答案
????????答案是:00100014? ?00100001? 00100004
2.2 解析
? ? ? ? 對于 p+0x1,是將結構體指針p+1,由于p是結構體指針,所以,其步長為1個結構體的長度,題目說這樣一個結構體長度為20(其實可以通過自己計算結構體的內容得出),所以加1直接跳過20個字節,因此輸出結果為00100014。(16進制20就是14)。
? ? ? ? 對于(unsigned long)p + 0x1,將p強制轉換成了無符號長整型,p已經不再是指針,變成了一個長整型1,048,567。加1變成1,048,568。輸出時用的%p,也就是地址類型的輸出,因此是16進制,1,028,568轉換回16進制就是00100001。
? ? ? ? 對于(unsigned int*)p + 0x1,將p轉化成了無符號整型指針,那其步長就變為了4,p+1就指向4字節后的地址,因此對p+1輸出結果為00100004。
3、案例三
????????
#include <stdio.h>
int main()
{int a[4] = { 1,2,3,4 };int* ptr1 = (int*)(&a + 1);int* ptr2 = (int*)((int)a + 1);printf("%x,%x", ptr1[-1], *ptr2);return 0;
}
? ? ? ?%x代表16進制輸出,來想想這題的答案吧
3.1 答案
? ? ? ? 怎么樣,算對了嗎?
3.2 解析?
? ? ? ? 這道題難點在第二個輸出,下面我們一個個分析:
? ? ? ? 對于ptr1來說,定義時,(int*)(&a + 1);&a取出了整個數組的地址,+1跳過整個數組指向數組最后一個元素后面一個位置的地址,?然后再將它強制類型轉換回(int*)類型,步長變回一個整型數據的大小。輸出時,ptr[-1]其實就是輸出ptr前一個地址的訪問值,ptr前一個地址指向的就是數組最后一個元素,因此輸出為4.
? ? ? ? 對于ptr2來說,定義時,(int*)((int)a + 1);先將a強制轉換成int類型,已經不是指針了,因此+1就是存粹往a上加個1。
? ? ? ? 在內存中a[4]的存放是如上圖的,強制類型轉換后+1指向的其實是如上圖的位置,那么再對其進行解引用得到的就是02000000(因為我們用的編譯器是小端存儲,數據的低位是放在地址的低位的,存儲時反著存,讀取時要反著讀取)。
4、案例四
#include <stdio.h>
int main()
{int a[3][2] = { (0, 1),(2, 3),(4, 5) };int* p;p = a[0];printf("%d", p[0]);return 0;
}
4.1 答案
????????
? ? ? ? 答案是1,是不是沒想到
4.2 解析
? ? ? ? 其實這題在初始化是有坑的,仔細看我們在初始化的時候用到了(),我舉個例子:
(0,1)這個式子其實就是1,這是個逗號表達式,逗號表達式的值就是最后一個','后面的那個值,在這里就是1.其實a[3][2]這個數組只初始話了3個值{1 , 3 , 5}。
? ? ? ? 好了,現在再來分析p指針,對于p來講,p=a[0],將a第一行賦值給p其實就是第一行元素的首地址給了p,最后打印時p[0]其實就是訪問a第一行第一個元素的值,相當于a[0][0],最后打印出來就是1。
5、案例五
#include <stdio.h>
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;
}
5.1 答案
答案是:FFFFFFFC,-4
5.2 解析
? ? ? ? 這題,p是一個數組指針,其步長為4個整型數據大小,比a數組的每一行長度少1個整型數據大小,p和a一開始指向的是同一個地址,每當p+1時,p只能跳過4個整型數據大小的地址,而對于a來說,a+1可以跳過5個整型數組的大小的地址。所以,p[4][2]和a[4][2]比較,它們都跳過了4行,因此a[4][2]在地址上比p[4][2]多4個整型大小的值,而我們知道,指針和指針相減,得到的是兩個指針之間的元素個數,因此是-4,%p是輸出地址,其沒有原碼反碼補碼的概念,直接將內存中-4的補碼輸出,就是FFFFFFFC。
總結
? ? ? ? 本篇通過五個例子來加強我們對指針和數據存儲的理解,希望對你學習c語言的指針有所幫助!