2010-01-24 62 views
3
#include <stdio.h> 

int main(void){ 
    unsigned a[3][4] = { 
    {2,23,6,7}, 
    {8,5,1,4}, 
    {12,15,3,9} 
}; 
printf("%u",*((int*)(((char*)a)+4))); 
return 0; 
} 

在我的機器的輸出爲a[0][1]即 .Could有人值解釋這是怎麼工作?這個指針算法是如何工作的?

編輯:回滾到舊yucky代碼,什麼是專門給我:P

+0

是的,這是一個錯字:| – 2010-01-24 08:29:16

+0

我在閱讀GMan的文章後編輯了這段代碼:) – 2010-01-24 08:35:54

+0

謝謝,但我不會這麼做。 :)它混淆了人們,並使我們所給的答案無效。保持這個問題是如此的一切,所以一切都繼續有意義,所以路人可以獲得完整的價值。編輯:好得多。 :) – GManNickG 2010-01-24 08:37:42

回答

13

所以,你有你的存儲器陣列像這樣:

2, 23, 6, 7, 8... 

這樣做是數組轉換爲一個char*,它可以讓你訪問單個字節,它指向的位置:

2, 23, 6, 7, 8... 
^ 

然後,它增加了四個字節,把它移到下一個值(更多內容更高版本)。

2, 23, 6, 7, 8... 
^

然後把它變成一個int*和間接引用它,所獲得的價值23


有技術上的三件事情不對的代碼。

第一個是假定unsigned的大小是4個字節。 (因此,+ 4)。但這不一定是真的!更好的是+ sizeof(unsigned),確保無論unsigned碰巧是什麼尺寸都是正確的。

第二個問題是轉換爲int:原始數組爲unsigned,但值正在轉換爲intint範圍內存在範圍內的值(因爲int範圍的一半爲負值)。因此,如果數組中的某個值不能表示爲int(表示該值大於INT_MAX) ,你會得到錯誤的價值。最好是轉換爲unsigned*,以保持正確的類型。

最後一件事是格式說明符。整數說明符是%d,但代碼使用%u,它是無符號整數。實際上,即使投回到int*是錯誤的,printf也會將轉換成值,回到unsigned*,恢復它的完整性。通過解決問題二,問題三解決了問題。

還有一個隱藏第四個問題:代碼糟透了。這可能是爲了學習的目的,但yuck

+0

明白了!謝謝:) – 2010-01-24 08:27:43

+0

@nthgreek:指針算術:http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html – 2010-01-24 08:30:54

+0

@GMan:'有一個隱藏的第四個問題:代碼很爛。這可能是爲了學習的目的,但是糟糕。' - 同意:P – 2010-01-24 08:37:27

2

它首先隱式地將數組a轉換爲指向其開始的指針。然後它將指針轉換爲char *並將值遞增4.值4恰好與系統上的sizeof(無符號)相同,所以實際上它已將一個元素從頭開始向前移動。然後它將地址轉換爲int *並讀取它指向的值(運算符*)。這個結果值被打印爲無符號整數,這是因爲int和unsigned是相同的大小。

靜態二維數組在存儲器中的佈局使得所有元素實際上按照一維數組順序存儲。

1

無符號int是尺寸4.即的sizeof(無符號)== 4

它可以容納4個字符,其中的每個是一個字節[用C未用Java/C#等]。

數組在內存中連續分配。當你將無符號數組視爲char *時,你需要將指針移動4步以達到數組中的下一個無符號值。

+0

'sizeof(unsigned)== 4' *在這種情況下*,不一定在任何地方。這可能是你想說的。 – GManNickG 2010-01-24 08:34:45

1

首先,創建一個尺寸爲3x4的2-dim數組。

((char*)a)之後你可以使用它作爲char數組。我們將它指定爲b。

((char*)a)+4b[4]相同,它指向char數組中的第012個元素(您記得,C中的aarays是從0開始的)。或者只是第5個字節。

當您將數組轉換回int時,i-th int數組的元素從i*4字節開始,如果sizeof(int) = 4。因此,在第5個字節中,int數組的第二個元素開始位於指針指向的位置。編譯器從第4個位置開始獲取4個字節並表示它是int。這完全是[0] [1]。

9

陣列:

unsigned a[3][4] = { 
    {2,23,6,7}, 
    {8,5,1,4}, 
    {12,15,3,9} 
}; 

在存儲器被佈置爲(假設a本身是在存儲器位置0x8000,特定字節序和一個四字節int):

0x8000 0 0 0 2 
0x8004 0 0 0 23 
0x8008 0 0 0 6 
0x800C 0 0 0 7 
0x8010 0 0 0 8 
0x8014 0 0 0 5 
0x8018 0 0 0 14 
0x801C 0 0 0 12 
0x8020 0 0 0 15 
0x8024 0 0 0 3 
0x8028 0 0 0 9 

分解表達式:

*((int*)(((char*)a)+4)) 
  • ((char*)a)給你一個char指針。
  • +4前進4個字節(4 * sizeof(char)),該指針
  • (int*)匝的所述結果返回到int指針。
  • *解除引用指針提取int

這是非常愚蠢的做法,因爲它本身不可移植(例如,對於其中int是兩個或八個字節的環境)。

+1

+1僅僅是因爲你的陣列內存佈局看起來非常棒,與我的相比。 – GManNickG 2010-01-24 08:41:17

+0

好的解釋+1! :) – 2010-01-24 08:42:16

+0

@ GMAN:是的,確實paxdiablo的'數組內存佈局看起來很棒',但是我在閱讀完這一行之後纔得到解決方案'這個功能是將數組轉換爲char *,它允許您訪問各個字節':) – 2010-01-24 08:46:36