2016-01-22 85 views
8

考慮下面的示例方案:轉換一個指針到一個結構到其第一構件

$ gcc -std=c99 -O2 -Wall -Werror -pedantic -o main main.c 

預期的輸出是:

$ ./main 
a: 4, b: 2 
a: 4, b: 2 

的C99

#include <stdio.h> 

struct base { 
    int a, b; 
}; 

struct embedded { 
    struct base base; 
    int c, d; 
}; 

struct pointed { 
    struct base* base; 
    int c, d; 
}; 

static void base_print(struct base* x) { 
    printf("a: %d, b: %d\n", x->a, x->b); 
} 

static void tobase_embedded(void* object) { 
    base_print(object); // no cast needed, suitably converted into first member. 
} 

static void tobase_pointed(void* object) { 
    struct base* x = *(struct base**) object; // need this cast? 
    base_print(x); 
} 

int main(void) { 
    struct embedded em = {{4, 2}}; 
    struct pointed pt = {&em.base}; 
    tobase_embedded(&em); 
    tobase_pointed(&pt); 
    return 0; 
} 

編譯時標準說這是關於結構的第一個成員:

C99 6.7.2.1(13): 指向結構對象的指針,經適當轉換後,指向其初始成員...反之亦然。 在結構對象中可能有未命名的填充,但不在其開頭。

在示例程序中的指針struct embedded而不需要顯式的轉換被轉換爲指針struct base(通過void*)。

如果代替第一個成員是指向基地的指針,如struct pointed會怎樣?我不確定tobase_pointed內的演員。沒有投射垃圾被打印,但沒有編譯警告/錯誤。通過演員表輸出base.abase.b的正確值,但如果存在未定義的行爲,則這並不意味着太多。

是否將struct pointed轉換爲其第一個成員struct base*是否正確?

+0

'struct pointed'的第一個成員是一個指向'struct base'的指針。這將需要解引用'void * object'。但你不能提領'無效*'沒有告訴編譯器如何取消引用指針,因此需要投。 – alvits

回答

2

該代碼不只是強制轉換,它還將指向指向結構基址的指針取消引用。這是獲取指向基地的指針所必需的。

這是在代碼中發生了什麼,如果該函數tobase_pointed除去:

struct pointed pt = {&em.base}; 
void* object = &pt;     //pass to the function 
struct base** bs = object;   //the cast in the function 
assert(bs == (struct base**)&pt) ; //bs points to the struct pointed 
assert(bs == &(pt.base)) ;   //bs also points to the initial member struct base* base 
struct base* b = *bs ;    //the dereference in the function 
base_print(x); 

bs是被適當地轉換,以指向初始構件的指針。你的代碼是正確的。

+1

看到它一步一步使它清晰,謝謝。 – Adam

1

這種轉換是有道理的,你需要它,因爲你想將一個指針轉換成指針指針。如果你不投,解引用將是不正確的。

換句話說,你base*具有相同的地址pt對象。所以你可以通過指向pt的指針訪問它。但是你必須解除引用。

相關問題