c-是否可以使一个结构指针指向另一个不同类型的结构指针



以下结构是类型为timer_t的寄存器结构

typedef struct {                                    /*!< TIMER0 Structure                                                      */
uint32_t  CFG;                               /*!< GPTM Configuration                                                    */
uint32_t  TAMR;                              /*!< GPTM Timer A Mode                                                     */
uint32_t  TBMR;                              /*!< GPTM Timer B Mode                                                     */
uint32_t  CTL;                               /*!< GPTM Control                                                          */
uint32_t  SYNC;                              /*!< GPTM Synchronize                                                      */
uint32_t  RESERVED;
uint32_t  IMR;                               /*!< GPTM Interrupt Mask                                                   */
uint32_t  RIS;                               /*!< GPTM Raw Interrupt Status                                             */
uint32_t  MIS;                               /*!< GPTM Masked Interrupt Status                                          */
uint32_t  ICR;                               /*!< GPTM Interrupt Clear                                                  */
uint32_t  TAILR;                             /*!< GPTM Timer A Interval Load                                            */
uint32_t  TBILR;                             /*!< GPTM Timer B Interval Load                                            */
uint32_t  TBMATCHR;                          /*!< GPTM Timer B Match                                                    */
uint32_t  TAPR;                              /*!< GPTM Timer A Prescale                                                 */
uint32_t  TBPR;                              /*!< GPTM Timer B Prescale                                                 */
uint32_t  TAPMR;                             /*!< GPTM TimerA Prescale Match                                            */
uint32_t  TBPMR;                             /*!< GPTM TimerB Prescale Match                                            */
uint32_t  TAR;                               /*!< GPTM Timer A                                                          */
uint32_t  TBR;                               /*!< GPTM Timer B                                                          */
uint32_t  TAV;                               /*!< GPTM Timer A Value                                                    */
uint32_t  TBV;                               /*!< GPTM Timer B Value                                                    */
uint32_t  RTCPD;                             /*!< GPTM RTC Predivide                                                    */
uint32_t  TAPS;                              /*!< GPTM Timer A Prescale Snapshot                                        */
uint32_t  TBPS;                              /*!< GPTM Timer B Prescale Snapshot                                        */
uint32_t  TAPV;                              /*!< GPTM Timer A Prescale Value                                           */
uint32_t  TBPV;                              /*!< GPTM Timer B Prescale Value                                           */
uint32_t  RESERVED1[981];
uint32_t  PP;                                /*!< GPTM Peripheral Properties                                            */
}timer_t;

并且这个CCD_ 2用于解引用它们,所以我可以在它们上写,因为CCD_ 3是所用模块的基地址。现在,我想为每个寄存器声明另一个类型为timerAModeBitField_t;的位字段,类似于这个

typedef struct timerAModeBitField{
uint32_t timerAMode         : 2;
uint32_t timerACaptMode     : 1;
uint32_t timeraPWMMode      : 1;
uint32_t timerACountDir     : 1;
uint32_t timerAMatchIntEn   : 1;
uint32_t timerAWaitOnTrig   : 1;
uint32_t timerASnapShotMode : 1;
uint32_t timerAIntervalLoad : 1;
}timerAModeBitField_t;

所以我可以直接写入它们的特定位,有没有可能实现这样的东西,这样我可以先去引用寄存器,然后去引用位字段TIMER0->TAMR->timerAmode = 0x04

在可移植C中,位字段的布局是未指定的。因此,您不能使用位字段来描述便携式C.中的特定布局

然而,如果您正在为特定的体系结构编写代码,那么您的C实现可能会有额外的保证。特别是在Arm平台上,适用Arm体系结构过程调用标准(AAPCS(。它有一个针对位字段的规则,特别是针对C和C++中的位字段。如果位字段描述是由您的供应商提供的,并且外围设备打算在Arm处理器上使用,则AAPCS保证您对数据布局做出相同的假设,因此您的代码将使用预期的数据布局。

但是,使用相同的数据布局是不够的!当一个字段以一种方式写入(比如通过uint32_t(,然后以另一种方式读回(比如通过位字段(时,您还必须确保编译器会做正确的事情。C编译器通常可以假设,当通过不同类型访问内存时,它是不同的内存。这使编译器能够更好地优化代码。

有几个例外我不会在这里讨论,但总的来说,你不能假设用一种方式写记忆,用另一种方式读回来会得到一致的结果。以不同方式访问同一内存称为混叠。什么是严格的混叠规则?对这一点如何适用于C有一个很好的解释。小心:违反混叠规则通常会导致代码仅在某些优化级别和特定上下文中失败(例如,取决于编译器可用于特定代码块的寄存器数量(,因此这可能很难调试。特别要注意的是,强制转换通常可以绕过编译器警告,但不能避免别名错误:强制转换只是告诉编译器不要抱怨代码损坏,它们并不能减少代码的损坏。

让编译器知道您通过不同类型访问同一内存的一种方法是将这些类型放在并集中。(这在C11中得到了保证,没有官方保证,但在C99中得到了广泛实施。(这就是:

typedef union {
uint32_t value;
timerAModeBitField bits;
} timerAModeUnion;
typedef struct {
…
timerAModeUnion TAMR;
…
} timer_t;

然后,您可以可靠地执行诸如大容量写入TIMER0->TAMR.value和通过TIMER0->TAMR.bits.timerAmode读取特定位字段之类的操作。

您可以使用(内部(结构作为另一个(外部(结构的元素的数据类型,如下所示:

typedef struct timerAModeBitField {
uint32_t timerAMode         : 2;
uint32_t timerACaptMode     : 1;
uint32_t timeraPWMMode      : 1;
uint32_t timerACountDir     : 1;
uint32_t timerAMatchIntEn   : 1;
uint32_t timerAWaitOnTrig   : 1;
uint32_t timerASnapShotMode : 1;
uint32_t timerAIntervalLoad : 1;
uint32_t padding            : 23;
} timerAModeBitField_t;
typedef struct {                                    /*!< TIMER0 Structure                                                      */
uint32_t  CFG;                               /*!< GPTM Configuration                                                    */
timerAModeBitField_t TAMR;                   /*!< GPTM Timer A Mode                                                     */
uint32_t  TBMR;                              /*!< GPTM Timer B Mode                                                     */
uint32_t  CTL;                               /*!< GPTM Control                                                          */
uint32_t  SYNC;                              /*!< GPTM Synchronize                                                      */
uint32_t  RESERVED;
uint32_t  IMR;                               /*!< GPTM Interrupt Mask                                                   */
uint32_t  RIS;                               /*!< GPTM Raw Interrupt Status                                             */
uint32_t  MIS;                               /*!< GPTM Masked Interrupt Status                                          */
uint32_t  ICR;                               /*!< GPTM Interrupt Clear                                                  */
uint32_t  TAILR;                             /*!< GPTM Timer A Interval Load                                            */
uint32_t  TBILR;                             /*!< GPTM Timer B Interval Load                                            */
uint32_t  TBMATCHR;                          /*!< GPTM Timer B Match                                                    */
uint32_t  TAPR;                              /*!< GPTM Timer A Prescale                                                 */
uint32_t  TBPR;                              /*!< GPTM Timer B Prescale                                                 */
uint32_t  TAPMR;                             /*!< GPTM TimerA Prescale Match                                            */
uint32_t  TBPMR;                             /*!< GPTM TimerB Prescale Match                                            */
uint32_t  TAR;                               /*!< GPTM Timer A                                                          */
uint32_t  TBR;                               /*!< GPTM Timer B                                                          */
uint32_t  TAV;                               /*!< GPTM Timer A Value                                                    */
uint32_t  TBV;                               /*!< GPTM Timer B Value                                                    */
uint32_t  RTCPD;                             /*!< GPTM RTC Predivide                                                    */
uint32_t  TAPS;                              /*!< GPTM Timer A Prescale Snapshot                                        */
uint32_t  TBPS;                              /*!< GPTM Timer B Prescale Snapshot                                        */
uint32_t  TAPV;                              /*!< GPTM Timer A Prescale Value                                           */
uint32_t  TBPV;                              /*!< GPTM Timer B Prescale Value                                           */
uint32_t  RESERVED1[981];
uint32_t  PP;                                /*!< GPTM Peripheral Properties                                            */
} timer_t;

然后你可以用这种方式访问模式位:

TIMER0->TAMR.timerAmode = 2;

注意:不应将0x04分配给宽度为2的位字段,因为该值的宽度太大。可能的值范围从0到3。

编辑:

让我们缩小示例:

typedef union {
struct {
unsigned int mode: 2;
unsigned int padding: 30;
};
uint32_t value;
} control_t;
typedef struct {
control_t control;
uint32_t  stuff;
} module_t;

您总是可以获得结构元素的地址:

module_t module;
control_t* mode_p = &module.control;
mode_p->mode = 3; // write only to 2 bits
uint32_t v = mode_p->value; // read all 32 bits

通过并集,您可以使用完整的控制寄存器,也可以方便地访问一些位:

module.control.value = 0x12345678;
module.control.mode = 2;

不要陷入从机器代码角度想太多的陷阱。使用更高级语言的抽象来获得编译器更好的源代码控制。尽可能避免强制转换,因为它们告诉编译器要思考";程序员总是对的,我凭什么怀疑她"即使在代码有错误的情况下也是如此。

注意:事实上,C标准并没有定义比特字段的分配方式。然而,编译器以一种可复制的方式来做这件事,它很可能会被记录下来。确保通过检查验证使用情况的编译器来保护代码。

您可以使用未命名的成员来尝试这种方法:

#include <stdint.h>
typedef struct {
...
union {
struct {
uint32_t timerAMode         : 2;
uint32_t timerACaptMode     : 1;
uint32_t timeraPWMMode      : 1;
uint32_t timerACountDir     : 1;
uint32_t timerAMatchIntEn   : 1;
uint32_t timerAWaitOnTrig   : 1;
uint32_t timerASnapShotMode : 1;
uint32_t timerAIntervalLoad : 1;
};
uint32_t TAMR;
};
...
} timer_t;
#define TIMER0_BASE   (0x40001000)
volatile timer_t *TIMER0 = (timer_t*) TIMER0_BASE;
int main(void)
{
TIMER0->TAMR = 0x1234;
TIMER0->timerAMode = 2;
}

通过使用unmodify成员,您可以直接访问联合/结构的成员,而无需添加该联合的字段名。这允许一次访问TAMR以及它的单独字段

最新更新