What's the shortest code to write directly to a memory address in C/C++?

爱⌒轻易说出口 提交于 2019-12-21 03:21:08

问题


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...


回答1:


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.




回答2:


#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.




回答3:


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



回答4:


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).




回答5:


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...)




回答6:


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.



来源:https://stackoverflow.com/questions/2417195/whats-the-shortest-code-to-write-directly-to-a-memory-address-in-c-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!