Search code examples
cpointersdereference

How is this dereferenced?


I'm following a tutorial, where they want to write a particular value (0x0403) to register (at address 0x04000000)

As per my knowledge, this could be done like this,

unsigned int 32 *ptr;
ptr = 0x04000000
*ptr = 0x403

However, they are doing following:

#define REG_DISP      *((volatile uint32*)(0x04000000))
#define VIDEOMODE_3                            0x0003
#define BGMODE_2                               0x0400

int main()
{
    REG_DISP = VIDEOMODE_3 | BGMODE_2;
    while(1){}
}

Now, I have following questions:

  1. Can we use pointers without declaring any variables?

  2. Why pointer to a pointer is used? Is it because, we cannot do ptr = 0x04000000?


Solution

  • Just a remark. All this can only be implementation defined, because the language itself has no concept of registers living at well known addresses.

    The standard just says at 6.3.2.3 Pointers §5 (emphasize mine):

    An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

    That means that this is valid C, provided the implementation allows it to make sense:

    unsigned int *ptr;
    ptr = 0x04000000;
    *ptr = 0x403;
    

    It just uses a named pointer to access the specific address. It can be done without naming the pointer that way:

    * ((unsigned int *) 0x04000000) = 0x403;
    

    Let us see how it works:

    • (unsigned int *) 0x04000000 converts an unsigned int to a pointer to unsigned int
    • * ((unsigned int *) 0x04000000) dereferences that pointer
    • * ((unsigned int *) 0x04000000) = 0x403; assigns a value to the pointed variable

    As you want to access a physical register, you need to ask the compiler to immediately write the value instead of keeping it in an internal register which could be allowed per the as if rule. That is the meaning of the volatile qualifier. As it is dedicated to a specific implementation, it is perfectly legal, provided you can be sure that unsigned int has 32 bits in that implementation, to write

    volatile unsigned int *ptr;
    ptr = 0x04000000;
    *ptr = 0x403;
    

    or

    * ((volatile unsigned int *) 0x04000000) = 0x403;