cstructoffsetof

What is correct way to access a struct using pointer and offsetof()


I have the following code so as to be able to access numerous fields in array of structs (I've reduced it to two for simplicity). What is the correct incantation for the final pointer calculation *(ptr + offset) = data; because I always get :

error: incompatible types when assigning to type ‘struct osc_in_data’ from type ‘int32_t’ {aka ‘int’}

#define NumHarmonics   10

int32_t data1;
int32_t data2;



struct osc_in_data                                                           
{
    int32_t     LevelAttackRate;                                                
    int64_t     LevelPeakLevel;                                                 
    int32_t     LevelDecayRate;                                                 
} OscControl[NumHarmonics];



void SetADSRvalues(int32_t offset, int32_t data)
{
    int32_t harmonic;
    struct osc_in_data *ptr;
    for (harmonic = 0; harmonic < NumHarmonics; harmonic++)
    {
       ptr = &OscControl[harmonic];
       *(ptr + offset) = data;
    }
} 


SetADSRvalues(offsetof(struct osc_in_data, LevelAttackRate), data1)
SetADSRvalues(offsetof(struct osc_in_data, LevelDecayRate), data2)

Solution

  • The members have int32_t type so pointers to them are int32_t*.

    The offsetof(...) is just the offset in bytes. So you just take the pointer to the structure which member you want to modify. Then add to the pointer the offset using plain addition and remembering to use char* pointers to add one byte at a time. Then just cast the pointer to proper type and dereference and access it.

    void SetADSRvalues(size_t offset, int32_t data)
    {
        for (size_t harmonic = 0; harmonic < NumHarmonics; harmonic++) {
              // take the pointer to the structure we want to modify
              void *base = &OscControl[harmonic];
              // arithmetic using void* pointers is invalid
              // so convert to `char*` pointer before arithmetic
              char *basechar = base;
              // then add the offset - just plain addition
              // results in the address of the member inside the struct
              void *memberpnt = basechar + offset;
              // the member is `int32_t`, so the pointer has to be `int32_t*`
              int32_t *memberpnt_int32 = memberpnt;
              // finally set the value
              *memberpnt_int32 = data;
    
              // or a oneliner version:
              *(int32_t*)((char*)&OscControl[harmonic] + offset) = data;
        }
    }