0

I am understanding the working of pointers in C. Pointers basically stores the address of the memory (say addr1) and de-referencing the pointers gives us the value stored at that memory address addr1. Is there any possible way by which we can directly de-reference the memory address addr1 to get the value stored at that location?

I know how to use a pointer. I just want to know what actually happens behind. I tried to create the same scenario of how the pointer works in the below code using memory address. Although I am stuck in getting the value from a pointer. With this example, I want to know how the de-referencing of a pointer works.

For example:

#define LOCATION 0x68feac //Assuming that the memory location is available

int main()
{
    int a = 10;
    *(int *)LOCATION = (int)&a;
    printf("%x\n", (*(int*)(LOCATION)));    //It gives me the address of a
    printf("%x\n", *((*(int*)(LOCATION)))); /* I thought it would de-refer but
                                                it gives me compile time error 
                                                "invalid type argument of unary 
                                                '*'" */

}

I tried using

How to de-reference LOCATION so that we can get value 10? Any ideas or suggestions would help.

Navneet
  • 71
  • 8
  • 6
    `*(int *)LOCATION = (int)&a;`...where did you get the idea? – Sourav Ghosh Jan 31 '19 at 09:36
  • 5
    Please read a good C book....you are yet to have a clear idea about pointers. – Sourav Ghosh Jan 31 '19 at 09:36
  • Pointer Cliff's Notes - A pointer is simply a variable that holds the *address of* something else as its value. In other words, a pointer *points to* the address where something else can be found. For example, while `int a = 5;` stores the immediate value `5` as its value, `int *b;` creates a pointer to `int`, and `b = &a;` stores the address of `a` (the memory address where `5` is currectly stored) as its value. If you need the value stored at the memory address pointed to by a pointer, you *dereference* the pointer using the unary `'*'` operator, e.g. `int c = *b;` will initialize `c = 5`). – David C. Rankin Jan 31 '19 at 09:39
  • @DavidC.Rankin, good comment, but I would prefer to write "_a pointer points to the_ location _where something else can be found_" as the pointer holds the address. – Paul Ogilvie Jan 31 '19 at 09:42
  • Yep you are correct, but I ran out of characters so I was really pairing things down `:)` – David C. Rankin Jan 31 '19 at 09:45
  • 1
    I guess you meant `printf("%x\n", *(int*)*(int*)LOCATION)` – M.M Jan 31 '19 at 10:04
  • @M.M Yes I meant the same. I know how to use a pointer but I was just trying to imitate it using memory addresses. Thank you so much. – Navneet Jan 31 '19 at 10:13
  • @LourensDijkstra this is wrong of course as you define the pointer with identifier LOCATION. And if have defined LOCATION as the number it will not compile of course – 0___________ Jan 31 '19 at 10:39
  • `(int)&a;` - possible precision loss! You should prefer `uintptr_t` for this purpose. – Aconcagua Jan 31 '19 at 10:51

4 Answers4

2

What you are asking for is:

printf("%x\n", *((int *)(*(int*)(LOCATION))));

Because you need to convert the int value retrieved from *(int*)(LOCATION) to a pointer before de-referencing it.

But you really do not do that! It is an ugly syntax, and C compilers are great in optimizing out useless variables. So do yourself a favour and name the intermediary pointers:

#define LOCATION 0x68feac //Assuming that the memory location is available

int main()
{
    int a = 10;
    int **loc = (int **)LOCATION;  // BEWARE only make sense in very special use cases!
    *loc = &a;
    printf("%p\n", *loc);    //It gives me the address of a
    printf("%x\n", **loc);
    return 0;
}

Anyway, using an arbitrary memory location is seldom used in C, because the compiler (and linker) will use addresses that you can hardly guess. The only use case I know, is accessing well known physical addresses in embedded system or in kernel modules

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • @Serge Thank you so much for this line `printf("%x\n", *((int *)(*(int*)(LOCATION))));`. I actually know how to use a pointer but I was trying to immitate it using a memory address. The above line cleared a lot of things. Thank you once again. – Navneet Jan 31 '19 at 10:10
  • Why all the way around? Additional variables etc? No one uses it this way. – 0___________ Jan 31 '19 at 10:50
1

Very common in all embedded uC development (do not forget the volatile keyword).

*(volatile int *)LOCATION = a;

and how to print it

printf("a is : %d\n, *(volatile int *)LOCATION); 

or if you want to store the address of a

*(volatile int **)LOCATION = &a;

and how to print it

printf("Address if a is : %p, a is: %d\n, (void *)*(volatile int **)LOCATION, **(volatile int **)LOCATION); 

Real life example:

#define GPIOA_BASE 0x40000000

#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)

{
  __IO uint32_t MODER;        /*!< GPIO port mode register,                                  Address offset: 0x00 */
  __IO uint16_t OTYPER;       /*!< GPIO port output type register,                           Address offset: 0x04 */
  uint16_t RESERVED0;         /*!< Reserved,                                                                 0x06 */
  __IO uint32_t OSPEEDR;      /*!< GPIO port output speed register,                          Address offset: 0x08 */
  __IO uint32_t PUPDR;        /*!< GPIO port pull-up/pull-down register,                     Address offset: 0x0C */
  __IO uint16_t IDR;          /*!< GPIO port input data register,                            Address offset: 0x10 */
  uint16_t RESERVED1;         /*!< Reserved,                                                                 0x12 */
  __IO uint16_t ODR;          /*!< GPIO port output data register,                           Address offset: 0x14 */
  uint16_t RESERVED2;         /*!< Reserved,                                                                 0x16 */
  __IO uint32_t BSRR;         /*!< GPIO port bit set/reset registerBSRR,                     Address offset: 0x18 */
  __IO uint32_t LCKR;         /*!< GPIO port configuration lock register,                    Address offset: 0x1C */
  __IO uint32_t AFR[2];       /*!< GPIO alternate function low register,                Address offset: 0x20-0x24 */
  __IO uint16_t BRR;          /*!< GPIO bit reset register,                                  Address offset: 0x28 */
  uint16_t RESERVED3;         /*!< Reserved,                                                                 0x2A */
}GPIO_TypeDef;

and usage:

GPIOA -> MODER = value; 

__IO is defined as volatile

#define __IO volatile
0___________
  • 60,014
  • 4
  • 34
  • 74
  • Thank you. It helps a lot. And yes it is very common in embedded C development. :) – Navneet Jan 31 '19 at 10:42
  • Note that these kind of trashy register maps for various ARM are dangerous. Should someone access a single register out of those listed for the peripheral, without using the register map, you can get strict aliasing violations all over the place. Caused by their wild use of struct pointers. For example, if I do a direct 32 bit access to the ODR register + reserved in your list, then later access the same area using the struct and 16 bit access, there's a strict aliasing violation and gcc may go bonkers if you don't compile with `fno-strict-aliasing`. This is for Atmel SAM? – Lundin Jan 31 '19 at 11:41
  • @Lundin it does not break the strict aliasing rules. It is from common STM header. There is no danger at all. The compiler will generate 16bit wide instuctions for 16bits fields, 8 bit wide for 8 bit etc. It is the correct way. All known ARM compilers behave the same way (IAR, GCC, Keil, Green Hills) etc. Used by thousands of developers around the world. – 0___________ Jan 31 '19 at 12:50
  • @P__J__ Only gcc is problematic here and it does break strict aliasing if you do like I described, with optimizations on. As far as I know, none of the other mentioned compilers abuse strict aliasing for optimization purposes, since they are compilers for embedded systems. – Lundin Jan 31 '19 at 12:57
  • @Lundin as in this example. It is very important for the uC developers to keep the control over the width of the memory access. General C programmers do not need it and do not care about it https://godbolt.org/z/Hizfw2 – 0___________ Jan 31 '19 at 12:58
  • @Lundin uC gcc branch is different. ARM gcc developers know it and know much more about the uC programmers needs. – 0___________ Jan 31 '19 at 12:59
  • Regarding your example, relevant code invoking UB would be something like `*((volatile int16_t*)GPIOA_BASE) = 1; uint32_t undefined_behavior = GPIOA -> MODER;`. If you have one translation unit doing the former and another doing the latter, anything can happen. – Lundin Jan 31 '19 at 13:03
  • @Lundin In the embedded inplementations the first example is not the UB the second one is not as well. https://godbolt.org/z/hq4Y8h uC development & compilers are a bit different. More focus on the specific implementation - as the programming is very hardware related. I prefer the name "Embedded C" as this C is a bit different eve if the devoted C language lawyers think different (and they usually do not have any real low level embedded programming experience). – 0___________ Jan 31 '19 at 13:37
  • `printf("Address if a is : %p, a is: %d\n, *(volatile int **)LOCATION, **(volatile int **)LOCATION);` has undefined behavior. To print a pointer, use `%p` **and** cast the pointer as `(void *)`: `printf("Address if a is : %p, a is: %d\n, (void *)*(volatile int **)LOCATION, **(volatile int **)LOCATION);` – chqrlie Jan 31 '19 at 19:12
-1

To dereference an int from a given location:

int val = *((int*) LOCATION);

((int*) LOCATION) gives you the LOCATION casted to an int pointer, so the last step is to dereference it.

Example (where an exact LOCATION is specified as an existing pointer to integer, but it would work the same):

#include <stdio.h>

#define LOCATION ((void*) &a)

int main(void){
    int a = 123;
    printf("a=%d, &a=%p\n", a, &a);

    printf("*LOCATION=%d, LOCATION=%p\n", *((int*) LOCATION), LOCATION);
}
-2

You do things mostly correctly, but a pointer isn't just another integer, and you can't use arbitrary values, you have to use some that are allocated (i.e. given by the OS) to the program.

  1. To be able to dereference a pointer, we store the type of value stored in memory. That means that instead of a define you should declare int *LOCATION; to state that you want to point to a memory location where an integer is stored.

  2. In consequence, you shouldn't cast a pointer to int, because &a is already of the same type : a pointer to an int.

    Furthermore, you can't assign values to something you define. A define is not a type -- I'm sure you can find another question about what it is exactly.

  3. Getting the address of a before dereferencing it is correct, so do not assign arbitrary values to LOCATION ahead of time. At best, assign it NULL, to signify the pointer is not initialized.

Your code then becomes :

int* LOCATION = NULL;
int main()
{
    int a = 10;
    LOCATION = &a;
    printf("address of a: %lx\n", LOCATION);    // accessing the pointer accesses the memory address
    printf("value of a: %x\n", *LOCATION); // dereferencing the pointer with * gives the value stored at that address
} 

Now if you're using a memory-mapped peripheral, for example as @P__J__ suggests some hardware register with a fixed address, you need to cast the address to the right type.

#define LOCATION_ADDR 0x68feac
int volatile * const LOCATION = (int *) LOCATION_ADDR; 
  • volatile here means that the memory stored at that location may change due to factors external to the program. This changes compiler optimizations, for example a loop that reads this location until it changes would be removed without volatile.
  • const here means the pointer location won't change, though the data it contains might (different from const int !).
  • Once you're comfortable with this, you might use this in the define directly, i.e. #define LOCATION ((volatile int*)0x68feac)

If you want the hardware register to contain a pointer to an int, simply add another *.

#define LOCATION 0x68feac //Assuming that the memory location is available

int main()
{
    int a = 10;
    // dereferencing the address at LOCATION, storing into it the address of a
    *((volatile int **)LOCATION) = &a;
    printf("%p\n", *((volatile int **)LOCATION));
    printf("%x\n", **((volatile int **)LOCATION));
    return 0;
}
Community
  • 1
  • 1
Cimbali
  • 11,012
  • 1
  • 39
  • 68
  • 1
    In some environments you can use "arbitrary values" in this way (the value will be determined by the operating system or linker script etc. as some accessible location) – M.M Jan 31 '19 at 10:08
  • it is not something OP asks for. He want to dereference the particular address - for example the hardware register. – 0___________ Jan 31 '19 at 10:41
  • @P__J__ Well from "I just want to know what actually happens behind [pointers]" I inferred he was looking for much more basic knowledge. And his example is trying to dereference the value of `a`, not reading some embedded-C type location. – Cimbali Jan 31 '19 at 11:32