2016-01-22 8 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へのポインタは、明示的なキャストを必要とせず(void*を通じて)struct baseへのポインタに変換されるプログラム例で

代わりに、最初のメンバーがstruct pointedのようなベースへのポインタの場合はどうなりますか?私はtobase_pointedのキャストについては不明です。キャストなしではガベージが出力されますが、コンパイルの警告/エラーは出力されません。キャストでは、base.abase.bの正しい値が表示されますが、未定義の振る舞いがあるかどうかは実際にはあまり意味がありません。

struct pointedを最初のメンバーに変換するキャストはstruct base*ですか?

+0

'struct pointed'の最初のメンバーは' struct base'へのポインタです。これは、 'void * object'の逆参照を必要とします。しかし、ポインタを間接参照する方法をコンパイラに知らせずに、 'void *'を逆参照することはできません。したがって、キャストする必要があります。 – alvits

答えて

2

コードは単にキャストするだけでなく、struct baseへのポインタへのポインタを逆参照します。これは、まず最初にベースへのポインタを得るために必要です。

これは機能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へのポインタを通してそれにアクセスすることができます。しかし、逆参照する必要があります。

関連する問題