要是看C語言的書,上面一定會說指針運算僅對數組有意義,這話似乎也不錯,當然如果你打算對計算機系統徹底研究就不能拘泥於數組中的指針運算了。下面我們來看看幾個例子。
定義一個指針,然它指向內存中的0x10000000位置。
void * ptr = (void *)0x10000000;
然后對它賦值:
這樣嗎?
*ptr = 100;
當然不行,ptr是無類型(void)的指針,不能這樣操作,要轉化各類型才行,比如:
int * ptr_1 = (int *)ptr;
*ptr_1 = 100;
讀它的值也是一樣:
int value = 0;
value = *(int *)ptr;
指針指向0x10000000的內存單元,想讓它指向下一個內存單元即0x10000001:
ptr++;
??
不對!ptr是無類型(void)的指針,不能計算!要把它轉換成一個指向單個字節的指針,C語言中char類型占一個字節,所以要這樣:
char *ptr_1 = NULL;
ptr_1 = (char *)ptr;
ptr_1++;
這樣ptr_1就指向0x10000001了。
下面有一段代碼,我們來研究一下。。。
(環境:英特爾x86,32位處理器)
struct
BBB
... {
long num;
char *name;
short int data;
char ha;
short ba[5];
} * p;
printf( " sizeof(BBB)=%d " , sizeof (BBB));//①
p = (BBB * ) 0x10000000 ;
printf( " p=0x%p " , p);//②
printf( " p+0x200=0x%p " , p + 0x200 );//③
printf( " p=0x%X " , p);//④
printf( " (long *)p+0x200=0x%p " , ( long * )p + 0x200 );//⑤
printf( " p=0x%X " , p);
printf( " (char *)p+0x200=0x%p " , ( char * )p + 0x200 );//⑥
printf( " p=0x%p " , p);
printf( " (long)p+0x200=0x%p " , ( long )p + 0x200 );//⑦
pintf( " p=0x%p " , p);
printf( " (char)p+0x200=0x%p " , ( char )p + 0x200 );//⑧
... {
long num;
char *name;
short int data;
char ha;
short ba[5];
} * p;
printf( " sizeof(BBB)=%d " , sizeof (BBB));//①
p = (BBB * ) 0x10000000 ;
printf( " p=0x%p " , p);//②
printf( " p+0x200=0x%p " , p + 0x200 );//③
printf( " p=0x%X " , p);//④
printf( " (long *)p+0x200=0x%p " , ( long * )p + 0x200 );//⑤
printf( " p=0x%X " , p);
printf( " (char *)p+0x200=0x%p " , ( char * )p + 0x200 );//⑥
printf( " p=0x%p " , p);
printf( " (long)p+0x200=0x%p " , ( long )p + 0x200 );//⑦
pintf( " p=0x%p " , p);
printf( " (char)p+0x200=0x%p " , ( char )p + 0x200 );//⑧
①處會是多少?因為這里是32位機器,按4字節對齊,所以是
24;
②處:給指針p賦了值0x10000000,那麼這裡當然是
0x10000000了;
③處:指針+0x200,根據C語言的定義,這裡的一個*p占24個字節(因為BBB結構占24字節),那麼p + 0x200 = 0x10000000 + 0x200 * 24 = 0x10000000 + 0x3000 =
0x10003000;
④處:擔心什麼?p的值沒有改動啊~所以還是
0x10000000;
⑤處:(long *)p + 0x200?先想想符號優先級。。。應該是先 (long *)p 了再 + 0x200。指向BBB結構的指針轉化成指向long型的指針,BBB結構佔24個字節,long佔4個字節,所以(long *)p + 0x200 = 0x10000000 + 0x200 * 4 = 0x10000000 + 0x800 =
0x10000800;
⑥處:把p轉化成(佔一個字節的char類型)的指針,所以(char *)p + 0x200 = 0x10000000 + 0x200 * 1 =
0x10000200;
⑦處:嗯?(long)p?什麽意思?把p轉化成long型?。。。的確如此!p以前不是指針嗎?的確,但是不管他以前是什麽,它現在都是一個長型(long)的數了,它的值是多少?。。。看看②処,p就是0x10000000,不管他是什麽類型,它的本質就是0x10000000,所以現在p還是這個數,那麽(long)p + 0x200就等於。。。0x10000000 + 0x200,對就是這麽簡單的一個算數,最後等於
0x10000200;
⑧處:⑦弄明白了,這裡就好辦了,所以它是。。。0x10000000 + 0x200 = 0x10000200。不對!這囘看清楚了,p是個char,你認爲它能是0x10000000這麽大個數嗎?這時有同學說了,那⑦処怎麽就可以呢?
我們看看罪已開始給p賦值時的語句:
p = (BBB *)0x10000000;
這裡的0x10000000(化成二進制有32位)是個long能夠表示的數,所以p就等於0x10000000了,然後把它表示成長型,長型也有32位,能夠表示這麼大的一個數所以(long)p == 0x10000000;char占一個字節,所以轉化時就會“舍去”很多東西。英特爾的x86時小端排序,比如放置0x1234這樣一個數的時候他會在內存中這樣安排:34 12,那麼0x10000000會被搞成00 00 00 01,然後取char,就是頭上一個字節00,所以這時的p == 0。那麼0 + 0x200 =
0x200。
若不信,就把上面一段代碼編譯出來,運行驗證一把吧!