在C / C ++中直接写入内存地址的最短代码是什么?

我正在为没有内存保护的嵌入式系统编写系统级代码(在ARM Cortex-M1上,使用gcc 4.3编译),需要直接读/写内存映射寄存器。 到目前为止,我的代码看起来像这样:

#define UART0 0x4000C000 #define UART0CTL (UART0 + 0x30) volatile unsigned int *p; p = UART0CTL; *p &= ~1; 

有没有使用指针的更短路(代码更短,我的意思)? 我正在寻找一种方法来编写实际的赋值代码(如果我不得不使用更多的#defines,那就没关系):

 *(UART0CTL) &= ~1; 

到目前为止我尝试过的任何东西最终都是因为gcc抱怨它无法为左值分配东西……

我想成为一个挑剔者:我们在谈论C或C ++吗?

如果是C,我愿意顺从克里斯的回答(我希望删除C ++标签)。

如果是C ++,我建议不要使用那些讨厌的C-Cast和#define

惯用的C ++方式是使用全局变量:

 volatile unsigned int& UART0 = *((volatile unsigned int*)0x4000C000); volatile unsigned int& UART0CTL = *(&UART0 + 0x0C); 

我声明了一个类型化的全局变量,它将遵循范围规则(与宏不同)。

它可以很容易地使用(不需要使用*() ),因此更短!

 UART0CTL &= ~1; // no need to dereference, it's already a reference 

如果你想让它成为指针,那么它将是:

 volatile unsigned int* const UART0 = 0x4000C000; // Note the const to prevent rebinding 

但是使用不能为空的const指针有什么意义呢? 这在语义上是为什么创建引用的原因。

 #define UART0CTL ((volatile unsigned int *) (UART0 + 0x30)) 

😛

编辑补充:哦,响应所有关于如何标记C ++和C的问题的评论,这里是一个C ++解决方案。 😛

 inline unsigned volatile& uart0ctl() { return *reinterpret_cast(UART0 + 0x30); } 

这可以直接插入头文件中,就像C风格的宏一样,但是你必须使用函数调用语法来调用它。

 #define UART0 ((volatile unsigned int*)0x4000C000) #define UART0CTL (UART0 + 0x0C) 

如果你想让硬件寄存器看起来像普通的旧变量,你可以比Chris的答案更进一步:

 #define UART0 0x4000C000 #define UART0CTL (*((volatile unsigned int *) (UART0 + 0x30))) UART0CTL &= ~1; 

这是一个可能更适合的品味问题。 我曾经在团队希望寄存器看起来像变量的情况下工作,并且我已经研究了添加的解引用被认为是“隐藏得太多”的代码,因此寄存器的宏将留作必须的指针被明确地解除引用(如Chris的回答)。

我想在结构中指定实际控制位,然后将其分配给控制地址。 就像是:

 typedef struct uart_ctl_t { unsigned other_bits : 31; unsigned disable : 1; }; uart_ctl_t *uart_ctl = 0x4000C030; uart_ctl->disable = 1; 

(抱歉,如果语法不太正确,我实际上已经用C语言编写了很长时间……)

我对嵌入式应用程序的另一个选择是使用链接器为硬件设备定义部分,并将变量映射到这些部分。 这样做的好处是,如果您要定位多个设备,即使是来自TI等同一供应商,您通常也必须逐个设备地更改链接器文件。 即,同一系列中的不同设备具有不同数量的内部直接映射内存,并且板对板可能具有不同数量的内存以及不同位置的硬件。 以下是GCC文档中的示例:

通常,编译器将它生成的对象放在数据和bss等部分中。 但是,有时您需要其他部分,或者需要某些特定变量出现在特殊部分中,例如映射到特殊硬件。 section属性指定变量(或函数)存在于特定部分中。 例如,这个小程序使用几个特定的​​节名称:

  struct duart a __attribute__ ((section ("DUART_A"))) = { 0 }; struct duart b __attribute__ ((section ("DUART_B"))) = { 0 }; char stack[10000] __attribute__ ((section ("STACK"))) = { 0 }; int init_data __attribute__ ((section ("INITDATA"))); main() { /* Initialize stack pointer */ init_sp (stack + sizeof (stack)); /* Initialize initialized data */ memcpy (&init_data, &data, &edata - &data); /* Turn on the serial ports */ init_duart (&a); init_duart (&b); } 

将section属性与全局变量一起使用,而不是局部变量,如示例所示。

您可以将section属性与初始化或未初始化的全局变量一起使用,但链接器要求每个对象定义一次,除了未初始化的变量暂时进入公共(或bss)部分并且可以多次“定义”。 使用section属性将更改变量进入的部分,如果未初始化的变量具有多个定义,则可能导致链接器发出错误。 您可以使用-fno-common标志或nocommon属性强制初始化变量。