Доступ к члену структуры указателя (переменной) внутри структуры

Вот в чем дело: я работаю над функцией C, которая настраивает структуру, содержащую множество элементов, например *BASE (указатель на структуру), ID, MODE; но BASE - это структура, которая может быть определена как (скажем) "структура a", "структура b", "...", в зависимости от интерфейса. Это объявление структуры (и некоторых примеров):

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

Как видите, я использовал u32_t вместо структуры, потому что объявляю указатель на эту структуру. Позже у меня есть моя функция, определенная как:

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
    ...
}

Но я получаю сообщение об ошибке «US_IER не может быть разрешен». Поэтому я попробовал AT91S_USART InterfaceBASE = Interface->BASE; InterfaceBASE->US_IER = IRQ_Trigger; вместо Interface->BASE->US_IER = IRQ_Trigger; и не получил никакой ошибки. Но я не могу использовать это, потому что я не всегда знаю, с каким интерфейсом я работаю (пока я не прочитал член ID).

Это становится еще хуже: когда я пытаюсь скомпилировать, я продолжаю получать conflicting type qualifiers for 'u32_t' ошибку в u32_t typedef и initialization from incompatible pointer type и near initialization for USART0_DEV.BASE предупреждения в каждом из interface_t определений.

Я искал на форумах, но я не мог понять свои ошибки. Эти вроде помогли:

Что не так с моим кодом? Что я могу сделать, чтобы использовать что-то вроде Interface->BASE->US_IER

РЕДАКТИРОВАТЬ: Привет!, и вот что я получил:

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
....

}

Выглядит нормально, но при компиляции получаю cast to union type from type not present in union Error...


person Roosembert Palacios    schedule 07.08.2013    source источник
comment
Всегда ли US_IER является первым членом структуры, на которую указывает BASE?   -  person jxh    schedule 07.08.2013
comment
не всегда первый, но он является членом каждого возможного типа интерфейса   -  person Roosembert Palacios    schedule 07.08.2013
comment
Тогда я не уверен, что вы сможете сделать эту работу, если вы не можете выяснить, какой тип интерфейса должен быть.   -  person jxh    schedule 07.08.2013
comment
Вы пытаетесь солгать компилятору, а ему не нравится, когда ему лгут, поэтому он получает ответ. Это нормально; нечестность не окупается, особенно в коде C. Вы пытаетесь использовать BASE для представления универсального указателя. В C общий указатель пишется как void *. Вопрос о том, достаточно ли изменить BASE на void * (и последующие изменения с использованием приведения при доступе к данным через BASE), остается открытым. Вам может быть лучше с профсоюзом, а может и нет. Это может зависеть от того, есть ли у вас открытый список типов интерфейсов. (Кроме того, будьте осторожны при использовании имен, начинающихся с _.)   -  person Jonathan Leffler    schedule 07.08.2013
comment
Список не является открытым, но имеет ~ 128 различных типов структуры; Я пытался объявить 128 типов с помощью case/switch, но все равно получаю ошибку type qualifiers for 'u32_t' в typedef u32_t и предупреждения initialization from incompatible pointer type и near initialization for USART0_DEV.BASE в каждом из определений interface_t, даже после попытки привести указатель к u32_t*. Я не понимаю другого способа определения типа   -  person Roosembert Palacios    schedule 07.08.2013


Ответы (3)


BASE — это переменная типа u32_t pointer, пытающаяся получить доступ к элементу US_IER, который на самом деле недоступен в u32_t pointer. Но ваше ожидание определения this как u32_t pointer вместо structure pointer может оказаться невозможным, поскольку компилятор будет ожидать, что член должен быть определен, если к нему обращаются через указатель структуры.

Но для вашего случая (т. е. при наличии 128 различных типов интерфейса) определите тип структуры с этими 3 обязательными элементами (общими для всех 128 интерфейсов) US_IER, US_IAM, US_IDM и определите объединение внутри этой структуры с требуемыми возможными элементами и попробуйте получить доступ к этим элементам с помощью переключателя. случае или если еще.

person Opener    schedule 07.08.2013

Если вы хотите оставаться «открытым», вы должны либо определить свой BASE как

void *BASE;

или, чтобы сигнализировать/документировать, что это специальный указатель, вы можете использовать неполный тип:

struct hwbase * BASE;

и определить его как

interface_t USART0_DEV  = {AT91C_BASE_US0, AT91C_ID_US0, 0};

если вы использовали void * или скорее

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

для 2-го варианта.

Затем, после его использования, после того, как вы убедились, что работаете на правильном типе оборудования (например, TWI, USART), вы можете транслировать.

Наведите указатель сверху на правильный тип, например

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
    ...
}

(здесь я не уверен, что AT91S_USART уже является типом указателя - в этом случае вы опускаете *.)

Другим вариантом может быть добавление некоторых указателей на функции, чтобы иметь своего рода «ООП для бедняков»:

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 = ...
    ...
}
person glglgl    schedule 07.08.2013
comment
Спасибо! Это вроде как сработало; Я не получил предупреждения в редакторе кода Eclipse, но компилятор по-прежнему жалуется... cast to union type from type not present in union - person Roosembert Palacios; 08.08.2013
comment
Почти, действительно это было (AT91S_USART *)Interface->USART_IER - person Roosembert Palacios; 09.08.2013

Вы определили свой базовый элемент как указатель на u32_t, поэтому компилятор считает, что он указывает на u32_t, а u32_t не имеет члена с именем US_IER. Что происходит, когда вы используете «рабочую» версию своего кода, так это неявное приведение указателя из uint_32* в AT91S_USART*, который содержит требуемый член.

person Martin Lavoie    schedule 07.08.2013
comment
как объявить, что указатель имеет US_IER, US_IAM, US_IDM, ... членов? без объявления типа интерфейса. Должен ли я использовать if/else if/else if... для 128 типов интерфейсов? - person Roosembert Palacios; 07.08.2013
comment
Все ли они содержат разные члены? - person Martin Lavoie; 07.08.2013
comment
Да, все они отличаются друг от друга, но всегда содержат как минимум эти 3 члена (US_IER, US_IAM, US_IDM) - person Roosembert Palacios; 07.08.2013
comment
Один из способов, которым я мог бы это сделать, - это геттер и сеттер для членов, которые являются общими для всех интерфейсов. Таким образом, вы могли бы как бы инкапсулировать переключатель/кейс или if/else if. - person Martin Lavoie; 07.08.2013
comment
Можете ли вы объяснить немного больше? (геттер и сеттер) - person Roosembert Palacios; 07.08.2013
comment
вы могли бы иметь что-то вроде void setBase_USIER(Interface* interface, US_IER_T us_ier) { switch(interface->ID){ case AT91C_ID_US0: AT91S_USART interfacebase = interface->Base; interfacebase->US_IER = us_ier; break; //same thing for all other interface } } Это похоже на использование объектно-ориентированной парадигмы. - person Martin Lavoie; 07.08.2013
comment
Интересно... Спасибо! Как мне преодолеть ошибку type qualifiers for 'u32_t' в typedef u32_t и initialization from incompatible pointer type и near initialization for USART0_DEV.BASE warnings в каждом из определений interface_t. - person Roosembert Palacios; 07.08.2013
comment
Мне кажется, что это еще одна ошибка типа, вы можете попробовать привести свой указатель к u32_t *. Тем не менее, я хотел бы упомянуть, чтобы попытаться сделать это по-другому, потому что мне это кажется плохим. - person Martin Lavoie; 07.08.2013
comment
Я пытался, но компилятор все еще жалуется!; Кстати, я работаю в корпусе/переключателе, я думаю, что даже если это большой блок, он решит мои проблемы... - person Roosembert Palacios; 07.08.2013