2010-10-07 80 views
1

我想從包結構中使用指針運算得到一些字段。但是下面的代碼有什麼問題?
在第一種情況我想如果我從包開始走4個字節(2短場)我得到TLOW。但它沒有給出預期value.Additionally第二種情況下,我想從包開始去12字節,獲得數據字段我的想法出了什麼問題?void *賦值問題

struct packet{ 
     short len; 
     short field; 
     int tLow; 
     int tHigh; 
     void *data; 
} 

int main() 
{ 
    struct packet pack; 
    struct packet *pck; 

    pack.len=3; 
    pack.field=34; 
    pack.tLow=712; 
    pack.tHigh = 12903; 
    pack.data = "message"; 

    pck = &pack; 
    int *timeLow = (int *)pck + 4; // i want to get tLow 
    printf("Time Low :%d\n",*time); 

    char *msg = (char *)pck + 12 ;// want data 
    printf("Message :%s\n",msg); 

    return 0; 
} 
+2

我希望這是一個學習練習,而不是生產代碼。這是一個很好的練習,但我從不想看到這樣的真實代碼。 – 2010-10-07 14:17:55

+0

你的意思是訪問字節計算或其他東西的結構成員的方法? – Qxtrml 2010-10-08 07:06:40

回答

3

你肯定會更好使用標準方法

int *timeLow = &(pck->tLow); 

編譯器允許在結構中的任何成員之間插入填充字節。這些填充字節的規則最好是定義的實現......所以你必須參考你的實現手冊來確定在你的特定情況下如何(或如果)以及多少字節被插入。另外要注意的填充字節的數量從編譯改變與不同的選項或編譯器編譯器編譯(計算機到計算機,...)

你可以嘗試使用Coffsetof,但它並不漂亮:

size_t offset = offsetof(struct packet, tLow); 
/* make sure offset is a multiple of sizeof (int*) */ 
int *timeLow = (int*)pck + offset/sizeof (int*); 

,或者少一點難看使用澆鑄到(char*)和從其他的答案複製代碼 :-)

size_t offset = offsetof(struct packet, tLow); 
int *timeLow = (int*)((char*)pck + offset); 

哦!並且在您的源文件中缺少分號

+0

正如你所提到的,你的'offsetof'示例代碼依賴於編譯器的填充,以便將'tLow'放在一個可以被'tLow'大小整除的偏移量上。如果編譯器沒有填充,那麼偏移將是不可分割的。 – rwong 2010-10-07 14:08:50

+0

好吧,我會使用標準的方法訪問結構members.And我意識到我的錯誤在指針算術通過將pck視爲integer.But有時字節操作需要和在這種情況下,我將使用轉換pck char,並進行字節操作或offsetof宏。 – Qxtrml 2010-10-08 06:58:12

1

當你寫

int *timeLow = (int *)pck + 4 
你是治療 'PCK' 作爲取決於你的系統可以是4或8個字節的INT指針

。 這將不能正確偏移量結構,因爲你再告訴它有4 INT

偏移,而不是你需要做這樣的

int *timeLow = (int*)((short *)pck + 2); 
+1

或(int *)((unsigned char *)pck + 4) – 2010-10-07 13:53:34

+0

謝謝,我通過將pck視爲整數來實現我的錯誤。首先轉換爲char,然後再進行字節計算是我認爲的安全方式。 – Qxtrml 2010-10-08 06:54:11

+0

np,容易做錯,自己做了。 – 2010-10-08 06:58:31

0

短褲不一定是2個字節長。所有指定的是它們小於或等於整數的大小。您應該使用sizeof()

0

這是錯誤的:pack.data = "message"; 您正在使用未分配的內存。

此外:int *timeLow = (int *)pck + 4; 不保證工作(編譯器和系統之間的結構對齊方式不同)。

+2

不是; 'pack.data'指向的內存是文字「消息」。沒關係。試圖改變它會是未定義的行爲,並且將一個字符串複製到'pack.data'中會很糟糕。 – 2010-10-07 14:16:53

+0

你是對的,大衛。 – vulkanino 2010-10-07 15:59:46

2

您在尋找offsetof

您的代碼看起來是這樣的:

int *timeLow = (int*) ((char*)pck + offsetof(struct packet, tLow); 

和PMG指出,

int *timeLow = &(pck->tLow); 

是獲得一個指向一個結構的成員的正規途徑。這是我今天學到感謝PMG -

這個答案還放在桌子上帶來pointer arithmetics

+0

@pmg,真的嗎?我看不出如何。 – 2010-10-07 14:05:34

+0

這個你喜歡的這種? http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/BitOp/pointer.html – 2010-10-07 14:06:42

+1

@pmg:'(char *)'強制轉換優先於指針加法。 http://www.difranco.net/cop2220/op-prec.htm但是,如果混淆,請使用括號。 – rwong 2010-10-07 14:11:35

2

基本上你是依靠未定義的行爲。結構調整,等等。但反正...

(INT *)PCK + 4.將推進指針4 *的sizeof(int)的。這是錯誤的,我們希望通過1推進它,如果我們假設結構包裝和sizeof(短)== 2,從而...

int *timeLow = (int *)pck + 1; // i want to get tLow 
printf("Time Low :%d\n",*timeLow); 

打印出正確的結果。

至於消息,你需要做一些骯髒的東西。由於我在x86_64編譯器選擇墊無效* 8個字節邊界,所以偏移是16,而不是預期的12

我們基本上取一個指向一個void *。因此,代碼如下所示:

char **msg = (char**)((char *)pck + 16) ;// want data 
printf("Message :%s\n",*msg); 

從來沒有像這樣寫代碼,這只是說明了一個點。

+1

+1。永不說永遠......除非必要:-) – pmg 2010-10-07 14:04:40

+0

@pmg:將'k'加到'(char *)'將地址移動'k * sizeof(char)',即元素類型和sizeof(char )'按定義給出1。 – rwong 2010-10-07 14:23:53

+0

哦!我已經看到星星了......需要休息。感謝rwong – pmg 2010-10-07 14:26:58