編程經驗點滴(三)——《C、C++中指針加 1 的問題》

編程經驗點滴(三)

C、C++中指針加 1 的問題

2004-11-23

作  者:楊延慶
E-mail:blankmanATtomDOTcom
出  處:http://blog.csdn.net/blankman/archive/2004/11/23/programing_experience3.aspx



    最近找工作也筆試和麪試了好幾家公司了,好多都問了指針加 1 的相關的問題,以前沒有仔細的考慮過,剛剛測試了一下,找到了相應的規律,給大家說說吧。(GCC環境下測試)

    先用貝爾的一道筆試題簡要的說明一下吧:

【題】說明以下程序。
#include <stdio.h>


int main(void)

{

    unsigned int a[3] = {0x01020304, 0x05060708, 0x090a0b0c};
    unsigned int *p = (unsigned int *)((int)a +1);
    printf("%x/n", *p);

    return 0;

}


【題目解析】

    這段程序的輸出結果應該是 8010203(即0x08010203)。
    內存存儲結構如(圖1)所示(在 little-endian 的系統中)。在處的語句中,首先把數組 a 的首地址轉化爲整型,然後再加 1 ,再強制轉化爲 (unsigned int *) ,這個時候 p 值應該是 0x1235,所以取的是從 0x1235 到 0x1238 四個字節組成的新的整數,所以就是 0x08010203。
    給出公式的話就是 (a[0]>>8 | a[1]<<24)。



圖 1 內存結構示意圖



【補充說明】

    看一下這條語句輸出是什麼呢?
        p = a;
        printf("%d  %08x  %d  %d  %d",
               (p+1)-p, *(p+1), &a[1]-&a[0],(int)(p+1)-(int)p, (char *)(p+1)-(char *)p );

    結果是: 1  05060708  1  4  4  

    第一個輸出大家可能理解爲理所當然了,我們先看後面的結果。

    第二個輸出值是數組 a 的 a[1] 元素的值,也就是說 (p+1) 指向的地址是 0x1238,而不是0x1235。爲什麼呢?因爲指針加 1 的時候,實際上是加上了他所指向數據類型的寬度,這樣指針就可以指向下一個元素而不是一個元素的一半了。彙編代碼如下:
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,                       ;注意這裏,實際加的是 4,而不是1
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx

    第三個輸出按理來說應該是 0x1238-0x1234 = 4,但是爲什麼是 1 呢?因爲返回的是這種元素的個數,而不是真正的地址差值,也就是把差值再除以元素寬度。下面是 VC 的彙編代碼,參數 &a[1]-&a[0] 的壓棧過程。
00401043   lea         ecx,[ebp-8]        ; 取 a[1] 的地址值
00401046   lea         edx,[ebp-0Ch]      ; 取 a[0] 的地址值
00401049   sub         ecx,edx            ; 計算地址值之差,並放入 ecx 中 (值爲4)
0040104B   sar         ecx,2              ; 將 ecx 中的數值右移兩位,也就是除以 4 (值爲1)
0040104E   push        ecx                ; 參數壓棧,供函數 printf() 使用

    第四個輸出是 4,因爲int類型的相減就是實際數字的相減,不需考慮其它因素,彙編代碼如下:
00401063   mov         edx,dword ptr [ebp-10h]
00401066   mov         eax,dword ptr [edx+4]
00401069   push        eax                ; 注意!壓棧之前沒有右移操作

    第五個輸出,講到這裏 我想大家應該都明白了爲什麼是4了,我只給出彙編代碼:

0040104F   mov         eax,dword ptr [ebp-10h]
00401052   add         eax,4
00401055   sub         eax,dword ptr [ebp-10h]
00401058   push        eax

    現在在回頭看一下第一個輸出結果的彙編代碼。在執行 p+1 的時候實際上是加了 4 (int類型的寬度),只是在減的時候將結果除了個 4,所以結果仍然是 1。
    ;(p+1)-p
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,4
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx
    

【後記】

    這些以前都有點印象,但是不是十分確定,昨天就試驗了一下,找出了一下規律。其實這些 C99 上應該都有的,我同學正在幫我找,找到了將鏈接貼到這裏。


【補充說明】

    看一下這條語句輸出是什麼呢?
        p = a;
        printf("%d  %08x  %d  %d  %d",
               (p+1)-p, *(p+1), &a[1]-&a[0],(int)(p+1)-(int)p, (char *)(p+1)-(char *)p );

    結果是: 1  05060708  1  4  4  

    第一個輸出大家可能理解爲理所當然了,我們先看後面的結果。

    第二個輸出值是數組 a 的 a[1] 元素的值,也就是說 (p+1) 指向的地址是 0x1238,而不是0x1235。爲什麼呢?因爲指針加 1 的時候,實際上是加上了他所指向數據類型的寬度,這樣指針就可以指向下一個元素而不是一個元素的一半了。彙編代碼如下:
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,                       ;注意這裏,實際加的是 4,而不是1
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx

    第三個輸出按理來說應該是 0x1238-0x1234 = 4,但是爲什麼是 1 呢?因爲返回的是這種元素的個數,而不是真正的地址差值,也就是把差值再除以元素寬度。下面是 VC 的彙編代碼,參數 &a[1]-&a[0] 的壓棧過程。
00401043   lea         ecx,[ebp-8]        ; 取 a[1] 的地址值
00401046   lea         edx,[ebp-0Ch]      ; 取 a[0] 的地址值
00401049   sub         ecx,edx            ; 計算地址值之差,並放入 ecx 中 (值爲4)
0040104B   sar         ecx,2              ; 將 ecx 中的數值右移兩位,也就是除以 4 (值爲1)
0040104E   push        ecx                ; 參數壓棧,供函數 printf() 使用

    第四個輸出是 4,因爲int類型的相減就是實際數字的相減,不需考慮其它因素,彙編代碼如下:
00401063   mov         edx,dword ptr [ebp-10h]
00401066   mov         eax,dword ptr [edx+4]
00401069   push        eax                ; 注意!壓棧之前沒有右移操作

    第五個輸出,講到這裏 我想大家應該都明白了爲什麼是4了,我只給出彙編代碼:

0040104F   mov         eax,dword ptr [ebp-10h]
00401052   add         eax,4
00401055   sub         eax,dword ptr [ebp-10h]
00401058   push        eax

    現在在回頭看一下第一個輸出結果的彙編代碼。在執行 p+1 的時候實際上是加了 4 (int類型的寬度),只是在減的時候將結果除了個 4,所以結果仍然是 1。
    ;(p+1)-p
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,4
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx
    

【後記】

    這些以前都有點印象,但是不是十分確定,昨天就試驗了一下,找出了一下規律。其實這些 C99 上應該都有的,我同學正在幫我找,找到了將鏈接貼到這裏。



圖 1 內存結構示意圖



【補充說明】

    看一下這條語句輸出是什麼呢?
        p = a;
        printf("%d  %08x  %d  %d  %d",
               (p+1)-p, *(p+1), &a[1]-&a[0],(int)(p+1)-(int)p, (char *)(p+1)-(char *)p );

    結果是: 1  05060708  1  4  4  

    第一個輸出大家可能理解爲理所當然了,我們先看後面的結果。

    第二個輸出值是數組 a 的 a[1] 元素的值,也就是說 (p+1) 指向的地址是 0x1238,而不是0x1235。爲什麼呢?因爲指針加 1 的時候,實際上是加上了他所指向數據類型的寬度,這樣指針就可以指向下一個元素而不是一個元素的一半了。彙編代碼如下:
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,                       ;注意這裏,實際加的是 4,而不是1
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx

    第三個輸出按理來說應該是 0x1238-0x1234 = 4,但是爲什麼是 1 呢?因爲返回的是這種元素的個數,而不是真正的地址差值,也就是把差值再除以元素寬度。下面是 VC 的彙編代碼,參數 &a[1]-&a[0] 的壓棧過程。
00401043   lea         ecx,[ebp-8]        ; 取 a[1] 的地址值
00401046   lea         edx,[ebp-0Ch]      ; 取 a[0] 的地址值
00401049   sub         ecx,edx            ; 計算地址值之差,並放入 ecx 中 (值爲4)
0040104B   sar         ecx,2              ; 將 ecx 中的數值右移兩位,也就是除以 4 (值爲1)
0040104E   push        ecx                ; 參數壓棧,供函數 printf() 使用

    第四個輸出是 4,因爲int類型的相減就是實際數字的相減,不需考慮其它因素,彙編代碼如下:
00401063   mov         edx,dword ptr [ebp-10h]
00401066   mov         eax,dword ptr [edx+4]
00401069   push        eax                ; 注意!壓棧之前沒有右移操作

    第五個輸出,講到這裏 我想大家應該都明白了爲什麼是4了,我只給出彙編代碼:

0040104F   mov         eax,dword ptr [ebp-10h]
00401052   add         eax,4
00401055   sub         eax,dword ptr [ebp-10h]
00401058   push        eax

    現在在回頭看一下第一個輸出結果的彙編代碼。在執行 p+1 的時候實際上是加了 4 (int類型的寬度),只是在減的時候將結果除了個 4,所以結果仍然是 1。
    ;(p+1)-p
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,4
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx
    

【後記】

    這些以前都有點印象,但是不是十分確定,昨天就試驗了一下,找出了一下規律。其實這些 C99 上應該都有的,我同學正在幫我找,找到了將鏈接貼到這裏。


【補充說明】

    看一下這條語句輸出是什麼呢?
        p = a;
        printf("%d  %08x  %d  %d  %d",
               (p+1)-p, *(p+1), &a[1]-&a[0],(int)(p+1)-(int)p, (char *)(p+1)-(char *)p );

    結果是: 1  05060708  1  4  4  

    第一個輸出大家可能理解爲理所當然了,我們先看後面的結果。

    第二個輸出值是數組 a 的 a[1] 元素的值,也就是說 (p+1) 指向的地址是 0x1238,而不是0x1235。爲什麼呢?因爲指針加 1 的時候,實際上是加上了他所指向數據類型的寬度,這樣指針就可以指向下一個元素而不是一個元素的一半了。彙編代碼如下:
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,                       ;注意這裏,實際加的是 4,而不是1
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx

    第三個輸出按理來說應該是 0x1238-0x1234 = 4,但是爲什麼是 1 呢?因爲返回的是這種元素的個數,而不是真正的地址差值,也就是把差值再除以元素寬度。下面是 VC 的彙編代碼,參數 &a[1]-&a[0] 的壓棧過程。
00401043   lea         ecx,[ebp-8]        ; 取 a[1] 的地址值
00401046   lea         edx,[ebp-0Ch]      ; 取 a[0] 的地址值
00401049   sub         ecx,edx            ; 計算地址值之差,並放入 ecx 中 (值爲4)
0040104B   sar         ecx,2              ; 將 ecx 中的數值右移兩位,也就是除以 4 (值爲1)
0040104E   push        ecx                ; 參數壓棧,供函數 printf() 使用

    第四個輸出是 4,因爲int類型的相減就是實際數字的相減,不需考慮其它因素,彙編代碼如下:
00401063   mov         edx,dword ptr [ebp-10h]
00401066   mov         eax,dword ptr [edx+4]
00401069   push        eax                ; 注意!壓棧之前沒有右移操作

    第五個輸出,講到這裏 我想大家應該都明白了爲什麼是4了,我只給出彙編代碼:

0040104F   mov         eax,dword ptr [ebp-10h]
00401052   add         eax,4
00401055   sub         eax,dword ptr [ebp-10h]
00401058   push        eax

    現在在回頭看一下第一個輸出結果的彙編代碼。在執行 p+1 的時候實際上是加了 4 (int類型的寬度),只是在減的時候將結果除了個 4,所以結果仍然是 1。
    ;(p+1)-p
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,4
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx
    

【後記】

    這些以前都有點印象,但是不是十分確定,昨天就試驗了一下,找出了一下規律。其實這些 C99 上應該都有的,我同學正在幫我找,找到了將鏈接貼到這裏。



圖 1 內存結構示意圖



【補充說明】

    看一下這條語句輸出是什麼呢?
        p = a;
        printf("%d  %08x  %d  %d  %d",
               (p+1)-p, *(p+1), &a[1]-&a[0],(int)(p+1)-(int)p, (char *)(p+1)-(char *)p );

    結果是: 1  05060708  1  4  4  

    第一個輸出大家可能理解爲理所當然了,我們先看後面的結果。

    第二個輸出值是數組 a 的 a[1] 元素的值,也就是說 (p+1) 指向的地址是 0x1238,而不是0x1235。爲什麼呢?因爲指針加 1 的時候,實際上是加上了他所指向數據類型的寬度,這樣指針就可以指向下一個元素而不是一個元素的一半了。彙編代碼如下:
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,                       ;注意這裏,實際加的是 4,而不是1
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx

    第三個輸出按理來說應該是 0x1238-0x1234 = 4,但是爲什麼是 1 呢?因爲返回的是這種元素的個數,而不是真正的地址差值,也就是把差值再除以元素寬度。下面是 VC 的彙編代碼,參數 &a[1]-&a[0] 的壓棧過程。
00401043   lea         ecx,[ebp-8]        ; 取 a[1] 的地址值
00401046   lea         edx,[ebp-0Ch]      ; 取 a[0] 的地址值
00401049   sub         ecx,edx            ; 計算地址值之差,並放入 ecx 中 (值爲4)
0040104B   sar         ecx,2              ; 將 ecx 中的數值右移兩位,也就是除以 4 (值爲1)
0040104E   push        ecx                ; 參數壓棧,供函數 printf() 使用

    第四個輸出是 4,因爲int類型的相減就是實際數字的相減,不需考慮其它因素,彙編代碼如下:
00401063   mov         edx,dword ptr [ebp-10h]
00401066   mov         eax,dword ptr [edx+4]
00401069   push        eax                ; 注意!壓棧之前沒有右移操作

    第五個輸出,講到這裏 我想大家應該都明白了爲什麼是4了,我只給出彙編代碼:

0040104F   mov         eax,dword ptr [ebp-10h]
00401052   add         eax,4
00401055   sub         eax,dword ptr [ebp-10h]
00401058   push        eax

    現在在回頭看一下第一個輸出結果的彙編代碼。在執行 p+1 的時候實際上是加了 4 (int類型的寬度),只是在減的時候將結果除了個 4,所以結果仍然是 1。
    ;(p+1)-p
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,4
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx
    

【後記】

    這些以前都有點印象,但是不是十分確定,昨天就試驗了一下,找出了一下規律。其實這些 C99 上應該都有的,我同學正在幫我找,找到了將鏈接貼到這裏。


      When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. (結果是兩個數組元素的下標之差)  

上一篇:編程經驗點滴(二)——《函數調用時參數壓棧順序的問題(2004-10-24)》
下一篇:編程經驗點滴(四)——《使用類的成員函數作爲線程的執行函數》

聲明:原創,版權所有,如需轉載請註明出處。
http://blog.csdn.net/blankman/archive/2004/11/23/programing_experience3.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章