1

基本的な算術を英語に翻訳する小さなプログラムを書くことから始めて、私は評価の順序を表すために二分木(必然的に非常に不均衡です)を構築することになります。まず、私は書いた

struct expr;

    typedef struct{
    unsigned char entity_flag;  /*positive when the oprd
    struct represents an entity 
     ---a single digit or a parenthesized block*/                      
    char oprt;

    expr * l_oprd;// these two point to the children nodes 
    expr * r_oprd;
    } expr;

ただし、1桁を効率的に表すために、私は

typedef struct{
 unsigned char entity_flag;
 int ival;
} digit;

各「expr」構造体の「oprd」フィールドは上記の構造体のいずれかである可能性があるため、これらのタイプを次のように変更します。

void * l_oprd;
void * r_oprd;

次に、「中心的な質問」があります。voidポインタを介してメンバーにアクセスするにはどうすればよいですか。次のコードを参照してください

#include<stdio.h>
#include<stdlib.h>


typedef struct {
int i1;
int i2;} s;
main(){
void* p=malloc(sizeof(s));

//p->i1=1;
//p->i2=2;

*(int*)p=1;
*((int *)p+1)=2;
printf("s{i1:%d, i2: %d}\n",*(int*)p,*((int *)p+1));
}

コンパイラはコメント付きバージョンを受け入れません!上記の雑然としたアプローチでそれを行う必要がありますか?

助けてください。

PS:お気づきのとおり、上記の各構造体は「entity_flag」という名前のフィールドを持っているため、

void * vp;
...(giving some value to vp)
unsigned char flag=vp->entity_flag;

ボイドが何を指しているかに関係なくフラグを抽出できますが、これはCで許可されていますか?またはCで「安全」ですか?

4

4 に答える 4

1

p 関連するポインター型に変換するだけです。

s *a = p;

a->i1 = 42;
a->i2 = 31;

また

((s *) p)->i1 = 42;
((s *) p)->i2 = 31; 
于 2012-11-19T13:07:30.637 に答える
1

あなたはそれをキャストすることができます:

((s*)p)->i1=1;
((s*)p)->i2=2;

entity_flag構造体には何も表示されませんが、同じことが当てはまるs場合:expr

unsigned char flag=((expr*)vp)->entity_flag;
于 2012-11-19T13:07:59.120 に答える
1

void *ポインターを介してメンバーにアクセスすることはできません。キャストする方法はいくつかあります (実際、でケースを明示的に述べる必要さえありませんvoid *) が、それでも間違った答えです。

正しい答えは、次を使用することunionです。

typedef union {
  struct{
    unsigned char entity_flag;  /*positive when the oprd
    struct represents an entity 
     ---a single digit or a parenthesized block*/                      
    char oprt;

    expr * l_oprd;// these two point to the children nodes 
    expr * r_oprd;
  } expr;
  struct{
    unsigned char entity_flag;
    int ival;
  } digit;
} expr;

次に、次のような式にアクセスします ( variable を指定expr *e):

e->expr->entity_flag;

そして、このような数字:

e->digit->entity_flag;

他の解決策は厄介なハック、IMO であり、キャスト ソリューションのほとんどは、異なる型の 2 つのポインターが同じメモリを参照できないとコンパイラが想定できるという「厳密なエイリアシング」ルールを破るリスクがあります。


編集 ...

ユニオンのどのメンバーが使用されているかを把握するためにデータ自体を検査できる必要がある場合は、それが可能です。

基本的に、2 つの構造体の最上位フィールドが同じように宣言されている場合、それらは同じバイナリ表現になります。これは共用体に限ったことではなく、そのアーキテクチャ用にコンパイルされたすべてのバイナリに一般的に当てはまります (考えてみれば、これはライブラリが機能するために不可欠です)。

ユニオンでは、それらを別の構造体に引き出すのが一般的です。これにより、何をしているのかが明確になりますが、必須ではありません。

union {
  struct {
    int ID;
  } base;
  struct {
    int ID;
    char *data
  } A;
  struct {
    int ID;
    int *numeric_data;
  } B;
}

このスキームではp->base.ID、 、p->A.IDp->B.IDは同じものを読み取ることが保証されています。

于 2012-11-19T13:14:04.097 に答える
1

構造体メンバーのオフセットがわかっている場合は、ポインター演算を実行してから、entity_flag の値に従って適切な型にキャストできます。

両方の構造をバイト単位で整列し、oprt と digit に同じバイト数を使用することを強くお勧めします。

また、ツリーに oprt と digit の「タイプ」しかない場合は、精度の最初のビットを犠牲にして digit または oprt のフラグを立て、unsigned char entity_flag に必要なスペースを節約できます。oprt と digit の両方に単一の 4 バイトの int var を使用し、最初のビットを使用して型をエンコードする場合は、数字を抽出できます (union ソリューション パターンを使用: スレッドで提案されています)。

typedef union {
    struct {
        int code;
        expr * l_expr;
        expr * r_expr;
    } oprt;
    struct {
       int val;
    } digit;
} expr;

expr *x;
int raw_digit = x->digit.val;

int digit = raw_digit | ((0x4000000 & raw_digit) << 1 ) // preserves sign in 2's complement 

x->digit.val = digit | 0x8000000                       // assuming MSB==1 means digit

ユニオンを使用しても、必ずしも桁により多くのメモリが使用されるわけではありません。基本的に、1 桁は 4 バイトしかありません。したがって、数字型の expr を割り当てる必要があるたびに、単純に malloc(4) を呼び出し、結果を *expr にキャストし、それに応じて MSB を 1 に設定できます。バグなしで expr ポインターをエンコードおよびデコードする場合、「数字」型の expr の 4 番目のバイトを超えて到達しようとすることは決してありません ... うまくいけば。安全が必要な場合は、このソリューションはお勧めしません ^_^

expr 型を簡単に確認するには、ユニオン内でビットフィールドを使用できます。

typedef union {
   struct {
       int code;
       expr * l_expr;
       expr * r_expr;
   } oprt;
   struct {
       int val;
   } digit;
   struct {
       unsigned int is_digit : 1;
       int : 31; //unused
   } type;

} 式;

于 2012-11-19T13:55:36.323 に答える