GCC C ++(ARM)和const指针结构字段
-
18-09-2019 - |
题
假设有一个简单的测试代码
typedef struct
{
int first;
int second;
int third;
} type_t;
#define ADDRESS 0x12345678
#define REGISTER ((type_t*)ADDRESS)
const int data = (int)(®ISTER->second)*2;
int main(void)
{
volatile int data_copy;
data_copy = data;
while(1) {};
}
这是在CodeSourcery的G ++(GCC 4.3.2),用于裸金属ARM编译。它也有一个很标准的链接脚本。
当用C编译(如main.c中)对象“数据”进入闪存,如所预期。当用C ++编译(如main.cpp中)这个对象进入RAM,和额外的代码加入其中确实不外乎复制从Flash中的值到RAM(该值已计算,只需复制!)。因此,编译器可以计算地址,但不知何故不希望“刚 使用它的”问题的根源是地址的乘法 - 无。‘* 2’乘法两个版本按预期方式工作 - ‘数据 - 数据‘被声明为’被放置在Flash中同样,当’:
const int data = (int)(REGISTER)*2;
也一切都很好。
C和C ++编译所有的文件是相同的,唯一的区别是在调用编译器 - 克++为main.cpp中,GCC为main.c中(与警告的水平的差异,和C ++有RTTI和异常禁用)
有没有简单和优雅的方式来解决这个“C ++的问题”?我确实需要这样的操作中的Cortex-M3的位段区创建位地址常量数组。这是一个错误,或者也许是C ++编译器的有些异样限制?
我知道我可以在“C”文件创建数据对象,只是“外部”在C ++ -Include他们,但是这不是很优雅[;
感谢您的所有帮助!
解决方案 2
在合适的解决方案是使用 offsetof()宏从STDDEF.H报头。
基本上这样:
const int data = (int)(®ISTER->second)*2;
具有一个与替换
#include <stddef.h>
const int data = (int)(REGISTER + offsetof(type_t,second))*2;
和然后将对象放置在Flash既为C和C ++。
其他提示
您有几个问题。你为什么要服用一个地址,将其转换为整数,乘以2?你不应该乘以地址或地址存储为整数。你应该永远做指针唯一算术减去他们(以获得偏移),或与添加的指针偏移得到另一个指针。
在C ++中,对全局值初始化的规则比C.在C稍微更宽松,需要的值被初始化为编译时间常数;其结果是,编译器可以在只读存储器中放置const
全局值。在C ++中,值可以被初始化为它们不一定编译时间常数表达式,允许编译器在运行时生成的代码,以计算初始值。这个初始化代码进入main()
之前调用,类似于构造函数的全局对象。
既然你与常量地址的工作,你知道的int
有多大是你的平台上(我假设32位),你应该做这样的事情:
接下来,你的volatile
关键字的使用是完全错误的。 volatile
说,编译器不会保存变量在寄存器 - 它应该从内存在每次阅读时间被重新加载,应该每次写入的时间完全写入存储器。声明局部变量data_copy
作为volatile
几乎是没用的,除非你期待着另一个线程或信号处理程序开始修改它意外(高度怀疑)。此外,data_copy
仅仅是一个地址的副本,而不是寄存器的你想阅读的内容。
在什么应该做的是声明REGISTER
作为指针到一个易失性 - 这是volatile
的明确目的之一,用于存取存储器映射的寄存器。这里是你的代码应该是什么样子:
#define REGISTER (*(volatile type_t *)ADDRESS)
#define DATA (*(const volatile int *)((ADDRESS+4)*2))
这使得它如此,当你做这样的事情:
REGISTER.first = 1;
REGISTER.first = 2;
int x = REGISTER.second;
int y = DATA;
它总是这样合适的事:1写入到0×12345678,2〜0×12345678写入,从0x1234567c读取,并从0x2468acf8读取。该volatile
关键字确保这些读取和写入总是发生,他们没有得到优化:编译器将不删除第一次写入REGISTER.first
,如果它是一个普通变量,它是多余的。
修改强>
在回答您的评论,见安德鲁医学生的回应您的评论 - 你真的相乘的区别的两个指针之间通过2,这是确定的。只是要小心你的数据类型。我也从来没有提过关于内核任何东西。
您可以得到GCC把变量中的特定数据部分用的 section
属性:
const volatile int *data __attribute__((section("FLASH")) = /* whatever */;
使用适当的节名。如果你不知道那是什么,拿上它由C编译器(你说把它放在正确的部分)生成的目标文件,运行nm
,看看C编译器把哪一部分在它
您拍照时看看GCC 变量属性,或许“部分”以及帮助你在可变的位置。
- 您不应该乘以指针。
- 您不应该被存储指针作为 “INT”。 醇>
我敢肯定有一个合法的C ++的方式做你想要什么,但我不能完全理解这是你发布的代码是什么。
修改强>:
您说你要乘的指针,但是这是非常可能是错误的。请记住:(int)(REGISTER) * 2
将等于(int)(0x12345678 * 2)
这可能等于0x2468ACF0
的不可以你想要什么。你可能想:REGISTER + sizeof(int) * 2
这是2点的整数过去该结构的最后一个字节
<强>原始回答:强>
这看起来像一个失败的尝试做了“结构破解”(该示例使用C99风格,但它在C89工作得很好太,只需要具有1个作为最后元件的阵列)。也许你想要的是这样的:
typedef struct {
int first;
int second;
int third;
unsigned char data[1]; /* we ignore the length really */
} type_t;
type_t *p = (type_t *)0x12345678;
p->data[9]; /* legal if you **know** that there is at least 10 bytes under where data is */
这样做的常见的用法是这样的malloc分配的结构:
type_t *p = malloc((sizeof(int) * 3) + 20) /* allocate for the struct with 20 bytes for its data portion */
我想说对于外围读最安全的接入写你应该只使用volatile定义和偏移定义。铸造外围地址作为一个结构没有给出任何类型的对准或偏移量保证。
#define UART_BASE_REG ((volatile uint32_t*)0x12341234)
#define UART_STATUS_REG (UART_BASE_REG + 1)
...
如果我理解正确的话,你的总体目标是在总结这一段:
我确实需要这样的操作在Cortex-M3的的位段区域中产生的位的地址常量阵列。这是一个错误,或者也许是C ++编译器的有些异样限制?
我使用Keil编译为STM32,以及随之而来包含用于设置和清零位带状位宏示例之一:
#define RAM_BASE 0x20000000
#define RAM_BB_BASE 0x22000000
#define Var_ResetBit_BB(VarAddr, BitNumber) \
(*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 0)
#define Var_SetBit_BB(VarAddr, BitNumber) \
(*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 1)
#define Var_GetBit_BB(VarAddr, BitNumber) \
(*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)))
如果这些到底是不是你要找什么,我想他们可以进行修改,以满足您的需求。