只读内存映射寄存器在C中使用' volatile const '定义,但在c++中仅使用' volatile '
Read-only memory-mapped registers defined with `volatile const` in C but only `volatile` in C++
在使用Atmel SAM3X8E的嵌入式系统项目中工作时,我注意到在一些CMSIS头文件中有以下代码位:
#ifndef __cplusplus
typedef volatile const uint32_t RoReg; /**< Read only 32-bit register (volatile const unsigned int) */
#else
typedef volatile uint32_t RoReg; /**< Read only 32-bit register (volatile const unsigned int) */
#endif
为什么c++的类型定义不包括const
?我在某个地方看到提到c++不存储运行时内存中的整数const变量,如果这是真的,这意味着const
将需要被删除,因为微控制器寄存器是如何内存映射的,但我似乎找不到任何其他说c++这样做的东西(尽管我的搜索无可否认相当简短)。没有太多的c++经验,我也认为这可能是c++不允许const
结构成员,因为这些类型主要用于寄存器集合的结构类型,但情况似乎也不是这样。
如果使用const声明,c++标准将要求您初始化变量的内容。在微控制器寄存器的情况下,您不希望这样做。
因为从来没有实例化过RoReg
对象,所以没有很好的理由在类型定义中省略const
限定符。
RoReg
的每次使用都是在一个宏中,该宏定义了指向类型的指针…
#define REG_WDT_SR (*(RoReg*)0x400E1A58U) /**< brief (WDT) Status Register */
…或者使用类似的宏访问struct
声明。
typedef struct {
WoReg WDT_CR; /**< brief (Wdt Offset: 0x00) Control Register */
RwReg WDT_MR; /**< brief (Wdt Offset: 0x04) Mode Register */
RoReg WDT_SR; /**< brief (Wdt Offset: 0x08) Status Register */
} Wdt;
#define WDT ((Wdt *)0x400E1A50U) /**< brief (WDT) Base Address */
即使使用const
限定符,代码在C和c++中也应该表现相同。
也许作者误解了标准。为了保证c++结构体具有与C中相同的布局,它要求类"对所有非静态数据成员具有相同的访问控制(第11条)"。作者可能把const
和volatile
误认为是访问控制说明符。如果它们是,那么您将希望所有结构体成员具有相同的cv-限定符,以确保C和c++(以及硬件)布局之间的兼容性。但是public
、protected
和private
定义了访问控制。
正如@fanl所提到的,const
确实改变了c++中全局变量的默认链接,并且防止在没有初始化的情况下定义变量。
但是有比删除const
更好的方法来获得外部链接。Chris链接的头文件中保留数组的使用也非常脆弱。我想说这段代码还有很大的改进空间——不要模仿它。
此外,这些变量不被定义(这会导致编译器和链接器选择一个地址),它们总是通过指针访问,地址根据内存映射固定。
对于纯粹用于c++的头文件,我是这样做的(内存映射匹配TI Stellaris芯片)。
看起来很复杂,但优化编译器将其简化为每次访问一条指令。地址偏移量是编码的,不依赖于结构内字段的顺序和填充,所以它不那么脆弱,更容易根据数据表进行验证。
template<uintptr_t extent>
struct memory_mapped_peripheral
{
char data[extent];
volatile uint32_t* offset( uintptr_t off ) { return reinterpret_cast<volatile uint32_t*>(data+off); }
volatile const uint32_t* offset( uintptr_t off ) const { return reinterpret_cast<volatile const uint32_t*>(data+off); }
};
struct LM3S_SYSTICK : private memory_mapped_peripheral<0x1000>
{
volatile uint32_t& CTRL (void) { return offset(0x010)[0]; }
volatile uint32_t& RELOAD (void) { return offset(0x014)[0]; }
volatile uint32_t& CURRENT(void) { return offset(0x018)[0]; }
}* const SYSTICK = reinterpret_cast<LM3S_SYSTICK*>(0xE000E000);
struct LM3S_NVIC : private memory_mapped_peripheral<0x1000>
{
volatile uint32_t& EN (uintptr_t i) { return offset(0x100)[i]; }
volatile uint32_t& DIS (uintptr_t i) { return offset(0x180)[i]; }
volatile uint32_t& PEND (uintptr_t i) { return offset(0x200)[i]; }
volatile uint32_t& UNPEND(uintptr_t i) { return offset(0x280)[i]; }
volatile const uint32_t& ACTIVE(uintptr_t i) const { return offset(0x300)[i]; }
volatile uint32_t& PRI (uintptr_t i) { return offset(0x400)[i]; }
volatile uint32_t& SWTRIG(void) { return offset(0xF00)[0]; }
}* const NVIC = reinterpret_cast<LM3S_NVIC*>(0xE000E000);
- 在提升multi_index容器中,是否定义了"default index"?
- #定义c-预处理器常量..我做错了什么
- 用C++中的一个变量定义一个常量
- 部分定义/别名模板模板参数
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- #为""定义宏;静态";针对不同的上下文
- 如何确保C++函数在定义之前声明(如override关键字)
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- 当类在C++中定义时,有什么方法可以"register"类吗?
- 在命名空间中定义函数还是限定函数
- 此代码是否违反一个定义规则
- 编译C++时未定义的引用
- 不同翻译单元中不可重载的非内联函数定义
- 为什么在定义函数之前先声明它
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 在类定义之后定义一个私有方法
- 使用用户定义函数的字符串反转
- 为什么ostream为定义为' volatile char[] '的字符串打印' 1 ' ?
- 只读内存映射寄存器在C中使用' volatile const '定义,但在c++中仅使用' volatile '
- "volatile"的定义是否如此不稳定,还是 GCC 存在一些标准合规性问题?