2010-06-20 100 views
8

這是我在某個論壇上看到的一個採訪問題。我一直在試圖弄清楚它是如何工作的,但我不太明白。有人可以解釋它是如何工作的嗎?問:給定一個指向結構內成員a的指針,寫一個返回結構指針的例程。給定一個指向結構內成員a的指針,編寫一個返回結構指針的例程

struct s 
{ 
    ... 
    int a; 
    … 
}; 

struct s *get_s_ptr(int *a_ptr) 
{ 
    // implement this. 
} 

答案是:

struct s* get_s_ptr(int *a_ptr) 
{ 
    return (struct s*)((char*)a_ptr - (int)&((struct s*)0)->a); 
} 
+0

什麼**你問**嗎? – 2010-06-20 02:28:32

+0

對不起,我在標題中有問題,但不是帖子本身。現在已經修復了。 – Steve 2010-06-20 02:31:24

回答

12

它是如何工作的?

這裏的基本公式(以字節爲單位所有算術)是

address of struct member s->a == s + byte offset of a 

鑑於s類型,單一的編譯器,和一個目標機器,他們決定字節的a —抵消它的對於s型的每個結構都是一樣的。

您被給予左手邊,面試官要求您恢復s。你可以通過獲得一個新的等式來做到這一點;減去的字節從兩側偏移:

address of struct member s->a - byte offset of a == s 

中存在的問題,你給出s->a的地址,但你必須要弄清楚的字節偏移。要做到這一點,你有s設置爲零再次使用原始方程

address of struct member s->a where s is zero == zero + byte offset of a 
               == byte offset of a 

C語言中的左側是建立如下

struct pointer s where s is zero       (struct s *)0 
struct member s->a where s is zero       ((struct s*)0)->a 
address of s->a where s is zero        &((struct s*)0)->a 

最後的步驟:

  1. 爲了使算術合法C這個字節偏移量被轉換爲一個整數。
  2. 爲了確保減法是以字節爲單位完成的,a_ptr轉換爲char *
  3. 爲了給出正確類型的結果,其差異轉換爲struct s *

附錄:作爲禮Bendersky指出,應儘量避免出現這種代碼將是必要的情況下。幾乎總是有一個更好的方法。

+0

真好,解釋!謝謝 – 2010-06-21 05:18:11

+3

你給出了一個完全錯誤的,非標準的和通常*不好的*代碼段的很好的詳細解釋。 – 2010-06-22 03:05:49

+1

@Eli:好點。我編輯了我的答案。 – 2010-06-22 14:17:24

4

可以使用offsetof宏。

struct s* get_s_ptr(int *a_ptr) 
{ 
    return (struct s*)((char*)a_ptr - offsetof(struct s,a)); 
} 

我遲到了。我的互聯網連接速度很慢。

5

答案是:它沒有。它不起作用,即使它看起來似乎「一見鍾情」。 「答案」試圖解引用空指針,導致未定義的行爲。所以,除非你的「工作」的想法包含未定義的行爲,否則這個答案是行不通的。

該解決方案還有更多的問題,除了嘗試去除空指針(儘管這完全足以將「答案」扔到垃圾箱)。另一個問題是(struct s*) 0的結果是struct s *類型的空指針。該語言不保證空指針的實際物理值。如果可能很容易就像0xBAADFOOD,這會立即搞砸「答案」的預期功能。

隱含技術的正確實施將涉及標準offsetof宏(已在吳年的答覆建議,但我會重複一次)

struct s* get_s_ptr(int *a_ptr) 
{ 
    return (struct s*) ((char *) a_ptr - offsetof(struct s, a)); 
} 
+0

同意'NULL'指針。理論上它可能是非零的,這是螺絲釘的事情。 但是,這個指針沒有取消引用。如果你看看'offsetof'的定義 - 你會看到完全一樣的東西。 也就是說,編寫&(pObj-> a)不會解引用任何東西。因爲表達式的結果是地址。這只是一個算術 – valdo 2010-06-20 07:26:58

+1

&(pObj-> a)與&((* pObj).a)相同。因此,如果pObj爲NULL,它將解引用NULL指針。 – Nyan 2010-06-20 07:40:36

+0

我在這裏試過了:http://codepad.org/PtLv8XN7。 ((struct s *)0) - > a導致seg故障,而&((struct s *)0) - > a導致正確的偏移。有什麼想法嗎?無論如何,offsetof()可能是最好的方法。 – Steve 2010-06-20 08:23:14

1

認爲這將是有益的,

/* offsetof example */ 
#include <stdio.h> 
#include <stddef.h> 

struct mystruct { 
    char singlechar; 
    char arraymember[10]; 
    char anotherchar; 
}; 

int main() 
{ 
    printf ("offsetof(mystruct,singlechar) is %d\n",offsetof(mystruct,singlechar)); 
    printf ("offsetof(mystruct,arraymember) is %d\n",offsetof(mystruct,arraymember)); 
    printf ("offsetof(mystruct,anotherchar) is %d\n",offsetof(mystruct,anotherchar)); 

    return 0; 
} 

輸出:

offsetof(mystruct,singlechar) is 0 
offsetof(mystruct,arraymember) is 1 
offsetof(mystruct,anotherchar) is 11 

所以你的情況,

return (struct s*) ((char *) a_ptr - offsetof(struct s, a)); 
  • aptr爲char *
  • 減去a WRT的偏移struct s
  • 投地struct s*
  • 回報resultant ptr
相關問題