How to access members through a void pointer

前端 未结 4 1455
我寻月下人不归
我寻月下人不归 2021-01-15 20:29

Started by trying to write a small program to translate basic arithmetic into English, I end up building a binary tree(which is inevitably very unbalanced) to represent the

相关标签:
4条回答
  • 2021-01-15 20:51

    Just convert p to the relevant pointer type:

    s *a = p;
    
    a->i1 = 42;
    a->i2 = 31;
    

    or

    ((s *) p)->i1 = 42;
    ((s *) p)->i2 = 31; 
    
    0 讨论(0)
  • 2021-01-15 21:05

    If you know the offset at where your struct member is you can do pointer arithmetic and then cast to the appropriate type according to the value of entity_flag.

    I would strongly suggest to align both structures in bytes and use the same number of bytes for oprt and digit.

    Also, if you only have oprt and digit "types" in your tree you could sacrifice the first bit of precision to flag for digit or oprt and save the space needed for unsigned char entity_flag. If you use a single 4 bytes int var for both oprt and digit and use the first bit to encode the type you can extract a digit by (using the union solution pattern: proposed in the thread)

    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
    

    Using union does not necessarily uses more memory for digits. Basically a digit only takes 4 bytes. So every time you need to alloc a digit type expr you can simply call malloc(4), cast the results to *expr, and set the MSB to 1 accordingly. If you encode and decode expr pointers without bugs, you'll never try to reach beyond the 4th bytes of a "digit" type expr ... hopefully. I don't recommend this solution if you need safety ^_^

    To check for expr types easily, you can use bitfield inside the union I believe:

    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;
    

    } expr;

    0 讨论(0)
  • 2021-01-15 21:15

    You can't access members through void * pointers. There are ways you could cast it (indeed, you don't even need to state the case explicitly with void *), but even that is the wrong answer.

    The correct answer is to use 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;
    

    You then access an expression like this (given a variable expr *e):

    e->expr->entity_flag;
    

    And a digit like this:

    e->digit->entity_flag;
    

    Any other solution is a nasty hack, IMO, and most of the casting solutions will risk breaking the "strict aliasing" rules that say that the compiler is allowed to assume that two pointers of different types can't reference the same memory.


    Edit ...

    If you need to be able to inspect the data itself in order to figure out which member of the union is in use, you can.

    Basically, If the top-most fields in two structs are declared the same then they will have the same binary representation. This isn't just limited to unions, this is true in general across all binaries compiled for that architecture (if you think about it, this is essential for libraries to work).

    In unions it is common to pull those out into a separate struct so that it's obvious what you're doing, although it's not required:

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

    In this scheme, p->base.ID, p->A.ID, p->B.ID are guaranteed to read the same.

    0 讨论(0)
  • 2021-01-15 21:15

    you could cast it:

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

    I don't see any entity_flag in struct s but if you mean expr the same applies:

    unsigned char flag=((expr*)vp)->entity_flag;
    
    0 讨论(0)
提交回复
热议问题