Accessing a member of a pointer (variable) struct

2019-08-20 18:23发布

问题:

Here's the deal: I am working on a C function that configures a struct that contains many members, as the *BASE (Pointer to a Struct),ID,MODE; but the BASE is a struct that might be defined as (say) "struct a", "struct b", "...", depending of the interface. This is the struct (and some examples) declaration:

typedef unsigned int u32_t;
typedef unsigned char u8_t;
typedef struct _interface_t{
    u32_t *BASE;
    u8_t ID;
    u32_t MODE;
} interface_t;

interface_t USART0_DEV  = {AT91C_BASE_US0, AT91C_ID_US0, 0}; // <-- Here we have a AT91C_BASE_US0 struct as *BASE
interface_t USART1_DEV  = {AT91C_BASE_US1, AT91C_ID_US1, 0};
interface_t TC0_DEV     = {AT91C_BASE_TC0, AT91C_ID_TC0, 0}; // <-- Here we have a AT91C_BASE_TC0 struct as *BASE
interface_t TC1_DEV     = {AT91C_BASE_TC1, AT91C_ID_TC1, 0};
interface_t TC2_DEV     = {AT91C_BASE_TC2, AT91C_ID_TC2, 0};
interface_t TWI_DEV     = {AT91C_BASE_TWI, AT91C_ID_TWI, 0}; // <-- Here we have a AT91C_BASE_TWI struct as *BASE

As you see I used u32_t instead of the struct because I'm declaring a pointer to that struct. Later I have my function defined as:

unsigned char ConfigureDevice(interface_t *Interface, u32_t config, u32_t Speed, u32_t IRQ_Trigger, void (*Interface_irq_handler)(void)){
    ...
    Interface->BASE->US_IER = IRQ_Trigger; //Note: US_IER Must receive a vector to a function
    ...
}

But I get error 'US_IER could not be resolved'. So I tried AT91S_USART InterfaceBASE = Interface->BASE; InterfaceBASE->US_IER = IRQ_Trigger; instead of Interface->BASE->US_IER = IRQ_Trigger; and got no error. But I can't use this, because I don't always know what kind of interface I'm handling (until I read ID member).

It gets even worst: When I try to compile I keep getting conflicting type qualifiers for 'u32_t' error in the u32_t typedef and initialization from incompatible pointer type and near initialization for USART0_DEV.BASE warnings in each of the interface_t definitions.

I searched in the forums but I couldn't figure my error(s) out. These ones kinda helped:

  • creating struct within struct
  • Defining struct within typedef struct

What's wrong with my code? What can I do to use something like Interface->BASE->US_IER

EDIT: Hi!, so now this is what I got:

typedef struct _Interface_t{
    struct PERIPH_BASE * BASE;
    u32_t ID;
    u32_t MODE;
Interface_t;

Interface_t USART0_DEV  = {(struct PERIPH_BASE *)AT91C_BASE_US0, AT91C_ID_US0, 0};
...
unsigned char ConfigureDevice(Interface_t *Interface, u32_t config, u32_t Speed, u32_t IRQ_Trigger,...
...
((AT91S_SYS)Interface->BASE)->AIC_IECR = IRQ_Trigger; //No problems marked by Eclipse Code Editor
....

}

Looks Fine, but when compiling I get cast to union type from type not present in union Error...

回答1:

BASE is a variable of type u32_t pointer and trying to access a member US_IER which is not really available in u32_t pointer. But your expectation of having this defined as a u32_t pointer instead structure pointer may not be possible as the compiler will expect a member should have been defined if it is accessed via a struct pointer.

But for your case(ie, having 128 different interface type), define a structure type with these 3 required members(common in all 128 interfaces) US_IER, US_IAM, US_IDM and define an union within that structure with required possible members and try accessing these items with a switch case or if else.



回答2:

If you want to stay "open", you should either define your BASE as a

void *BASE;

or, in order to signal/document that it is a special pointer, you can use an incomplete type:

struct hwbase * BASE;

and define it as

interface_t USART0_DEV  = {AT91C_BASE_US0, AT91C_ID_US0, 0};

if you used void * or rather

interface_t USART0_DEV  = {(struct hwbase *)AT91C_BASE_US0, AT91C_ID_US0, 0};

for the 2nd option.

Then, upon using it, after you have made sure that you operate on the right type of hardware (e. g. TWI, USART), you can cast.

Cast your pointer from above to the right type, such as

unsigned char ConfigureDevice(interface_t *Interface, u32_t config, u32_t Speed, u32_t IRQ_Trigger, void (*Interface_irq_handler)(void)){
    ...
    ((AT91S_USART *)Interface->BASE)->US_IER = IRQ_Trigger; //Note: US_IER Must receive a vector to a function
    ...
}

(here I am not sure if AT91S_USART is already a pointer type - in this case, you omit the *.)

Another option could be to add some function pointers in order to have a kind of "poor man's OOP":

typedef void config_func(struct _interface_t *, whateveryouneed...)

config_func config_usart; // forward declaration, does as well ensire you have no typo in the function
typedef struct _interface_t{
    void *BASE;
    u8_t ID;
    u32_t MODE;
    config_func * configure; // function pointer
} interface_t;

interface_t USART0_DEV  = {AT91C_BASE_US0, AT91C_ID_US0, 0, config_usart };

void config_usart(struct _interface_t * iface, whateveryouneed...)
{
    AT91S_USART * my_base = iface->BASE;
    ...
    my_base->US_IER = ...
    ...
}


回答3:

You have defined your base member as a pointer on a u32_t so the compiler think what is pointed by it is a u32_t and u32_t doesn't have a member named US_IER. What happen when you use the "working" version of you're code is a implicit cast of the pointer from uint_32* to AT91S_USART*, which contains the wanted member.