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

发布于 2024-08-24 09:25:00 字数 424 浏览 8 评论 0原文

我正在为没有内存保护的嵌入式系统(在 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 抱怨它无法将某些内容分配给左值而告终。 ..

I'm writing system-level code for an embedded system without memory protection (on an ARM Cortex-M1, compiling with gcc 4.3) and need to read/write directly to a memory-mapped register. So far, my code looks like this:

#define UART0     0x4000C000
#define UART0CTL  (UART0 + 0x30)

volatile unsigned int *p;
p = UART0CTL;
*p &= ~1;

Is there any shorter way (shorter in code, I mean) that does not use a pointer? I looking for a way to write the actual assignment code as short as this (it would be okay if I had to use more #defines):

*(UART0CTL) &= ~1;

Anything I tried so far ended up with gcc complaining that it could not assign something to the lvalue...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

挥剑断情 2024-08-31 09:25:00
#define UART0CTL ((volatile unsigned int *) (UART0 + 0x30))

:-P

编辑添加:哦,为了回应有关如何标记问题 C++ 以及 C 的所有评论,这里有一个 C++ 解决方案。 :-P

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

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

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

:-P

Edited to add: Oh, in response to all the comments about how the question is tagged C++ as well as C, here's a C++ solution. :-P

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

This can be stuck straight in a header file, just like the C-style macro, but you have to use function call syntax to invoke it.

千仐 2024-08-31 09:25:00

我想吹毛求疵:我们是在谈论 C 还是 C++?

如果是 C,我愿意听从 Chris 的回答(并且我希望删除 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

但是使用不能为 null 的 const 指针有什么意义呢?从语义上讲,这就是创建引用的原因。

I'd like to be a nitpick: are we talking C or C++ ?

If C, I defer to Chris' answer willingly (and I'd like the C++ tag to be removed).

If C++, I advise against the use of those nasty C-Casts and #define altogether.

The idiomatic C++ way is to use a global variable:

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

I declare a typed global variable, which will obey scope rules (unlike macros).

It can be used easily (no need to use *()) and is thus even shorter!

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

If you want it to be pointer, then it would be:

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

But what is the point of using a const pointer that cannot be null ? This is semantically why references were created for.

云淡月浅 2024-08-31 09:25:00

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

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

UART0CTL &= ~1;

这是一个可能更可取的品味问题。我曾经遇到过团队希望寄存器看起来像变量的情况,并且我曾经编写过添加的取消引用被认为“隐藏太多”的代码,因此寄存器的宏将被保留为必须明确取消引用(如克里斯的回答)。

You can go one further than Chris's answer if you want to make the hardware registers look like plain old variables:

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

UART0CTL &= ~1;

It's a matter of taste which might be preferable. I've worked in situations where the team wanted the registers to look like variables, and I've worked on code where the added dereference was considered 'hiding too much' so the macro for a register would be left as a pointer that had to be dereferenced explicitly (as in Chris' answer).

如何视而不见 2024-08-31 09:25:00
#define UART0  ((volatile unsigned int*)0x4000C000)
#define UART0CTL (UART0 + 0x0C)
#define UART0  ((volatile unsigned int*)0x4000C000)
#define UART0CTL (UART0 + 0x0C)
她说她爱他 2024-08-31 09:25:00

我喜欢指定结构中的实际控制位,然后将其分配给控制地址。像这样的东西:(

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

如果语法不太正确,我很抱歉,我实际上已经有一段时间没有用 C 编写代码了......)

I like to specify the actual control bits in a struct, then assign that to the control address. Something like:

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

(Apologies if the syntax isn't quite right, I haven't actually coded in C for quite awhile...)

浅暮の光 2024-08-31 09:25:00

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

通常,编译器将其生成的对象放置在节中
像数据和 bss。然而,有时您需要额外的部分,
或者你需要某些特定的变量出现在特殊的
部分,例如映射到特殊硬件。该部分
属性指定变量(或函数)位于
特定部分。例如这个小程序使用了几个
具体部分名称:

 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")));

      主要的()
      {
        /* 初始化堆栈指针 */
        init_sp(栈+sizeof(栈));

        /* 初始化初始化数据 */
        memcpy (&init_data, &data, &edata - &data);

        /* 打开串口 */
        init_duart(&a);
        init_duart(&b);
      }

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

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

Another option which I kinda like for embedded applications is to use the linker to define sections for your hardward devices and map your variable to those sections. This has the advantage that if you are targeting multiple devices, even from the same vendor such as TI, you will typically have to alter the linker files on a device by device basis. i.e. Different devices in the same family have different amounts of internal direct mapped memory, and board to board you might have different amounts of ram as well and hardware at different locations. Here's an example from the GCC documentation:

Normally, the compiler places the objects it generates in sections
like data and bss. Sometimes, however, you need additional sections,
or you need certain particular variables to appear in special
sections, for example to map to special hardware. The section
attribute specifies that a variable (or function) lives in a
particular section. For example, this small program uses several
specific section names:

      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);
      }

Use the section attribute with global variables and not local variables, as shown in the example.

You may use the section attribute with initialized or uninitialized
global variables but the linker requires each object be defined once,
with the exception that uninitialized variables tentatively go in the
common (or bss) section and can be multiply “defined”. Using the
section attribute will change what section the variable goes into and
may cause the linker to issue an error if an uninitialized variable
has multiple definitions. You can force a variable to be initialized
with the -fno-common flag or the nocommon attribute.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文