1. 程式人生 > >易混淆概念(一)指標陣列與陣列指標

易混淆概念(一)指標陣列與陣列指標

5、地址的強制轉換

先看下面這個例子:
struct Test
{
   int Num;
   char *pcName;
   short sDate;
   char cha[2];
   short sBa[4];
}*p;

假設p 的值為0x100000。如下表表示式的值分別為多少?
   p + 0x1 = 0x___ ?
   (unsigned long)p + 0x1 = 0x___?
   (unsigned int*)p + 0x1 = 0x___?
我相信會有很多人一開始沒看明白這個問題是什麼意思。其實我們再仔細看看,這個知識點似曾相識。一個指標變數與一個整數相加減,到底該怎麼解析呢?

還記得前面我們的表示式“a+1”與“&a+1”之間的區別嗎?其實這裡也一樣。指標變數與一個整數相加減並不是用指標變數裡的地址直接加減這個整數。這個整數的單位不是byte 而是元素的個數。所以:p + 0x1 的值為0x100000+sizof(Test)*0x1。至於此結構體的大小為20byte,前面的章節已經詳細講解過。所以p +0x1 的值為:0x100014。


(unsigned long)p + 0x1 的值呢?這裡涉及到強制轉換,將指標變數p 儲存的值強制轉換成無符號的長整型數。任何數值一旦被強制轉換,其型別就改變了。所以這個表示式其實就是一個無符號的長整型數加上另一個整數。所以其值為:0x100001。

(unsigned int*)p + 0x1 的值呢?這裡的p 被強制轉換成一個指向無符號整型的指標。所以其值為:0x100000+sizof(unsigned int)*0x1,等於0x100004。

上面這個問題似乎還沒啥技術含量,下面就來個有技術含量的:在x86 系統下,其值為多少?
intmain()
{
   int a[4]={1,2,3,4};
   int *ptr1=(int *)(&a+1);//
指向a陣列後面的記憶體單元,&a+1表示向後移16個儲存單元
   int *ptr2=(int *)((int)a+1);//表示a的儲存單元的地址增加一個位元組
   printf("%x,%x",ptr1[-1],*ptr2);//ptr1[-1]其實指向的是a陣列的最後一個單元,*ptr1則表示a陣列的地址後移一個位元組之後的4個連續儲存單元所儲存的值
   return 0;
}
這是我講課時一個學生問我的題,他在網上看到的,據說難倒了n 個人。我看題之後告訴他,這些人肯定不懂彙編,一個懂彙編的人,這種題實在是小case。下面就來分析分析這個問題:

根據上面的講解,&a+1 與a+1 的區別已經清楚。


ptr1:將&a+1 的值強制轉換成int*型別,賦值給int* 型別的變數ptr,ptr1 肯定指到陣列a 的下一個int 型別資料了。ptr1[-1]被解析成*(ptr1-1),即ptr1 往後退4 個byte。所以其值為0x4。
ptr2:按照上面的講解,(int)a+1 的值是元素a[0]的第二個位元組的地址。然後把這個地址強制轉換成int*型別的值賦給ptr2,也就是說*ptr2 的值應該為元素a[0]的第二個位元組開始的連續4 個byte 的內容。

其記憶體佈局如下圖: 好,問題就來了,這連續4 個byte 裡到底存了什麼東西呢?也就是說元素a[0],a[1]裡面的值到底怎麼儲存的。這就涉及到系統的大小端模式了,如果懂彙編的話,這根本就不是問題。既然不知道當前系統是什麼模式,那就得想辦法測試。大小端模式與測試的方法在第一章講解union 關鍵字時已經詳細討論過了,請翻到彼處參看,這裡就不再詳述。我們可以用下面這個函式來測試當前系統的模式。
int checkSystem()
{
  union check
  {
      int i;
      char ch;
  } c;
  c.i = 1;
  return (c.ch ==1);//如果當前系統為大端模式這個函式返回0;如果為小端模式,函式返回1。
}

如果當前系統為大端模式這個函式返回0;如果為小端模式,函式返回1。也就是說如果此函式的返回值為1 的話,*ptr2 的值為0x2000000。如果此函式的返回值為0 的話,*ptr2 的值為0x100。