0

注:これはC ++の質問ではなく、C ++コンパイラは使用できず、C99のみを使用できます。

これは有効な(そして受け入れられる、美しい)コードですか?

typedef struct sA{
   int a;
} A;

typedef struct aB{
   struct sA a;
   int b;
} B;

A aaa;
B bbb;

void init(){
   bbb.b=10;
   bbb.a.a=20;
   set((A*)&bbb);
}

void set(A* a){
   aaa=*a;
}

void useLikeB(){
   printf("B.b = %d", ((B*)&aaa)->b);
}

つまり、「サブクラス」を「スーパークラス」にキャストし、「スーパークラス」を「サブクラス」に再キャストした後、特定の動作が必要な場合は有効ですか?

ありがとう

4

4 に答える 4

5

まず第一に、C99 標準では、任意の構造体ポインターをその最初のメンバーへのポインターにキャストすることが許可されています ( 6.7.2.1 構造体および共用体指定子)。

13 構造体オブジェクト内で、ビットフィールド以外のメンバーとビットフィールドが存在するユニットには、宣言された順序で増加するアドレスがあります。適切に変換された構造体オブジェクトへのポインターは、その最初のメンバー (または、そのメンバーがビットフィールドの場合は、それが存在するユニット) を指し、その逆も同様です。構造体オブジェクト内に名前のないパディングがある場合がありますが、先頭にはありません。

他の方法では、コード内で自由に次のことができます。

  1. に変換B*するA*と、常に正しく機能します。
  2. に変換A*しますが、B*実際には を指していない場合、Bさらにメンバーにアクセスするとランダムに失敗します。
  3. が指す構造体を割り当てます — ただし、ポインターが から変換された場合A*、共通メンバーのみが割り当てられ、残りのメンバーは無視されます。AB*B
  4. B*指す構造体を割り当てAます — ただし、最初にポインターを変換する必要があり、(3) に注意してください。

したがって、あなたの例はほぼ正しいです。ただし、ポイント(4)で述べたように割り当てたタイプの構造体であるuseLikeB()ため、正しく機能しません。これには 2 つの結果があります。aaaA

  1. 非共通Bメンバーは実際にはコピーされませんaaa((3) で述べたように)。
  2. Aプログラムは、そうでないものにアクセスしようとしてランダムに失敗しますB((2) で述べたように、存在しないメンバーにアクセスしています)。

より実用的な方法で説明すると、Aコンパイラを宣言すると、 のすべてのメンバーを保持するために必要な量のメモリが予約されますABにはより多くのメンバーがあるため、より多くのメモリが必要になります。通常の変数と同様Aに、実行時にサイズを変更できないため、 の残りのメンバーを保持できませんB


注意として、(1) により、ポインターを変換する代わりに、実質的にメンバーへのポインターを取得できます。これにより、最初のメンバーだけでなく、任意のメンバーにアクセスできるようになります。ただし、この場合、その逆は機能しないことに注意してください。

于 2012-08-18T14:48:43.087 に答える
2

これはかなり汚れていて、比較的危険だと思います。これで何を達成しようとしていますか?また、aaaがBであるという保証はなく、Aである可能性もあります。したがって、誰かが「uselikeB」を呼び出すと、失敗する可能性があります。また、アーキテクチャによっては、「inta」と「structaへのポインタ」が正しくオーバーラップするかどうかが異なり、「inta」に割り当てて「structa」にアクセスすると面白いことが起こる可能性があります。

于 2012-08-18T13:30:19.593 に答える
0

まず第一に、この割り当ての安全性に関する以前の投稿者からのほとんどの懸念に同意します。

そうは言っても、そのルートに進む必要がある場合は、1 レベルの間接化といくつかのタイプ セーフ チェッカーを追加します。

static const int struct_a_id = 1;
static const int struct_b_id = 2;
struct MyStructPtr {
   int type;
   union {
     A* ptra;
     B* ptrb;
     //continue if you have more types.
   }
};

アイデアは、「型」情報を含む構造体を介してポインターを渡すことで、ポインターを管理することです。クラス ツリーを記述する側にクラスのツリーを作成し (安全にキャストするための制限があるため、これはツリーを使用して表すことができることに注意してください)、質問に答えて、構造が正しく上下にキャストされていることを確認できます。したがって、「useLikeB」関数は次のように記述できます。

MyStructPtr the_ptr;
void init_ptr(A* pa)
{
     the_ptr.type = struct_a_id 
     the_ptr.ptra = pa;
}
void useLikeB(){
    //This function should FAIL IF aaa CANT BE SAFELY CASTED TO B
    //by checking in your type tree that the a type is below the 
    //a type (not necesarily a direct children).
    assert( is_castable_to(the_ptr.type,struct_b_id ) ); 
    printf("B.b = %d", the_ptr.ptrb->b);
}

私の2セント。

于 2012-08-18T15:15:30.547 に答える
0

なぜこれを行うのですか?持つ

set((A*)&bbb);

正しいよりも書くのは簡単ではありません

set(&bbb.a);

ここに投稿する際に避けるべきその他の事柄:

  • set宣言される前に使用します
  • aaa=aする必要がありますaaa = *a
于 2012-08-18T14:37:45.213 に答える