4

言い換えれば、void*ポインターを何らかの構造体型へのポインターとして再解釈 (変換ではなく!) してもよいでしょうか (void*ポインターが適切に変換された有効な構造体アドレスを実際に保持していると仮定します)。

実際、私は次のシナリオで興味深いです:

typedef struct void_struct void_struct_t;

typedef somestruct
{ 
    int member;
    // ... other members ...
}somestruct_t;

union 
{
    void*          pv; 
    void_struct_t* pvs; 
    somestruct_t*  ps; 
}u;

somestruct_t s={};

u.pv= &s;

u.ps->member=1; // (Case 1)  Ok? unspecified? UB? 

u.pvs=(void_struct_t*)&s;

u.ps->member=1;  // (Case 2) )Ok?

私が C11 標準で見つけたのは、ケース 1 ではかなり残念なことです。

§6.2.5

28 void へのポインターは、文字型へのポインターと同じ表現およびアライメント要件を持たなければなりません。 ] 同様に、互換性のある型の修飾または非修飾のバージョンへのポインターは、同じ表現およびアラインメント要件を持たなければなりません。構造体型へのすべてのポインターは、相互に同じ表現とアラインメントの要件を持つ必要があります。共用体型へのすべてのポインターは、相互に同じ表現とアライメントの要件を持つ必要があります。他の型へのポインターは、同じ表現またはアライメント要件を持つ必要はありません。

ただし、ケース 2 は有効であるように思われますが、100% 確実ではありません...

質問はほとんどが C 指向ですが、私は C++ にも興味があります (C++ コンパイラでコンパイルしている間、コードが有効であることを望みます)。正直なところ、C++11 標準ではさらに少ないことがわかったので、ケース 2 でさえ疑わしいように思えます...しかし、何かが欠けている可能性があります。

[編集] この質問の背後にある本当の問題は何ですか?

構造体として定義された (潜在的に大きな) 型のセットがあります。タイプごとに、コンパニオン タイプを定義する必要があります。

typedef struct companion_for_sometype
{
  sometype* p_object;
  // there are also other members
}companion_for_sometype;

明らかに、コンパニオン型は C++ のテンプレートになりますが、C 用のソリューションが必要です (より正確には、「クリーン C」、つまり、コードを有効な C++ コードにしたいため、C89 と C++ の交差用)。

幸い、C でもマクロを定義できるので問題ありません。

DECLARE_COMPANION(type_name) typedef struct companion_for_##type_name
{
  type_name* p_object;
  // there are also other members
}companion_for_##type_name;

コンパニオンが必要なすべてのタイプに対して呼び出すだけです。

コンパニオン型に対する一連の汎用操作もあります。これらの操作もマクロによって定義されます (純粋な C にはオーバーロードがないため)。

この操作の1つとして、

#define op(companion_type_object) blablabla

void*コンパニオン オブジェクトのフィールドへのポインタを割り当てる必要がありp_objectます。つまり、次のようにする必要があります。

(companion_type_object).p_object= (type_name*) some_function_returning_pvoid(..)

しかし、マクロは type_name を認識しない (コンパニオン型のオブジェクトのみがマクロに渡される) ため、マクロは適切なポインター キャストを実行できません。

質問は実際にはこの問題に触発されています。

それを解決するために、代入内のターゲット ポインターを void* として再解釈してから代入することにしました。コンパニオン宣言のポインターをポインターの共用体に置き換えることによって行うことができます (問題はこのケースに関するものです)。または、ターゲット ポインターを直接再解釈することもできます。

*(void**) &(companion_type_object).p_object= some_function_returning_pvoid(..)

しかし、ポインターを再解釈しないと解決策を見つけることができません (ただし、いくつかの可能性が欠けている可能性があります)。

4

2 に答える 2

6

void *構造体型へのすべてのポインターを含む、任意のオブジェクト ポインター型を保持できるポインターです。したがって、構造体型への任意のポインターをvoid *.

ただしvoid *、構造体型へのポインターは同じ表現を持つことが保証されていないため、ケース 1 は未定義の動作です。

(C11, 6.2.5p28) 「[...] 他の型へのポインターは、同じ表現またはアライメント要件を持つ必要はありません。」

于 2012-07-25T10:02:58.670 に答える
2

C では、void *任意のオブジェクト型に自動的にキャストされるため、次のように動作します。

(companion_type_object).p_object = some_function_returning_pvoid(..)

C++ では を使用する必要がありますが、 を使用static_castして必要な型を見つけることができますdecltype

(companion_type_object).p_object = 
    static_cast<decltype(*(companion_type_object).p_object) *>(
        some_function_returning_pvoid(..))

C++03 では、 に相当するコンパイラ拡張機能を使用できるはずですdecltype。または、マクロで生成されたメソッドを提供して、 aを適切な型companion_type_objectにキャストすることもできます。void *

static type_name *void_p_to_object_p(void *p) { return static_cast<type_name *>(p); }
...
(companion_type_object).p_object = companion_type_object.void_p_to_object_p(
    some_function_returning_pvoid(..))
于 2012-07-26T08:56:54.750 に答える