44

I am failing to understand what the expression *(uint32_t*) does.

I have broken the statement down to an example that declares the parts so I can try and interpret what each one does.

  uint32_t* ptr;
  uint32_t num
  *(uint32_t*)(ptr + num); // <-- what does this do?

I don't understand the last bit in the example, what happens when the expression *(uint32_t*)(ptr + num); executes during runtime?

JΛYDΞV
  • 8,532
  • 3
  • 51
  • 77
Hoa.N
  • 453
  • 1
  • 4
  • 8

4 Answers4

82

uint32_t is a numeric type that guarantees 32 bits. The value is unsigned, meaning that the range of values goes from 0 to 232 - 1.

This

uint32_t* ptr;

declares a pointer of type uint32_t*, but the pointer is uninitialized, that is, the pointer does not point to anywhere in particular. Trying to access memory through that pointer will cause undefined behaviour and your program might crash.

This

uint32_t num;

is just a variable of type uint32_t.

This

*(uint32_t*)(ptr + num);

ptr + num returns you a new pointer. It is called pointer arithmetic. It's like regular arithmetic, only that compiler takes the size of types into consideration. Think of ptr + num as the memory address based on the original ptr pointer plus the number of bytes for num uint32_t objects.

The (uint32_t*) x is a cast. This tells the compiler that it should treat the expression x as if it were a uint32_t*. In this case, it's not even needed, because ptr + num is already a uint32_t*.

The * at the beginning is the dereferencing operator which is used to access the memory through a pointer. The whole expression is equivalent to

ptr[num];

Now, because none of these variables is initialized, the result will be garbage.

However, if you initialize them like this:

uint32_t arr[] = { 1, 3, 5, 7, 9 };
uint32_t *ptr = arr;
uint32_t num = 2;

printf("%u\n", *(ptr + num));

this would print 5, because ptr[2] is 5.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Pablo
  • 13,271
  • 4
  • 39
  • 59
  • 3
    OK except for the printf specifier. `"%" PRIu32` is better. `"%u"` matches `unsigned`. – chux - Reinstate Monica Feb 16 '18 at 20:09
  • @chux huy the keyboard gremlin attacked me, so many "typos". I fixed the `printf` specifier, didn't think about that. – Pablo Feb 16 '18 at 20:11
  • then what is the difference between ```uint32_t``` and ```int32_t``` – Travis Su Feb 02 '20 at 00:30
  • @TravisSu read the first sentence of my answer. `uint32_t` is for unsigned 32 bit integers, meaning it stores values between 0 and 4294967295 and `int32_t` is for signed 32 bit integers, meaning it stores values between -2147483648 and 2147483647. – Pablo Feb 02 '20 at 03:13
  • self-learning Python and this is very clear thanks – n00dles Nov 16 '21 at 04:41
15

uint32_t is defined in stdint.h, so one may need to include it

#include <stdint.h>

this header shall define uint32_t to be an unsigned integer type taking exactly 32 bits.

user7610
  • 25,267
  • 15
  • 124
  • 150
3

This doesn't really do anything. Let me give you a different example:

uint32_t data;
void *pointer = &data;
*(uint32_t *)pointer = 5;

First of all, void* means "generic" pointer. It can point to objects of any type.

Now, (uint32_t *) means "interpret pointer as a pointer to an object with type uint32_t.

The rest of the expression simply means "store 5 at the location stored by this pointer".

If you want to know what uint32_t is, that's an unsigned integer with exactly 32 bits. And pointer + num is the same as the adress of pointer[5].

giusti
  • 3,156
  • 3
  • 29
  • 44
  • 5
    `void *pointer = &data;` is valid C++. Is the implicit conversion from `void *` to other pointer types that is invalid in C++. – HolyBlackCat Feb 16 '18 at 20:14
0

This type of expression is usually used in type punning. If you're not familiar with type punning, the main idea is to bypass the type system so that you can treat something as a different type than it really is (ie treat an int a as double)

The main idea behind type punning is you take a pointer to a current variable and then pun it into a different type by casting it into a pointer of that type and then dereferencing it, hence the commonly used cast and dereference you are referring to ( *(uint32_t *) = cast to unsigned 32bit int pointer and then dereference).

As others have pointed out, your code "does nothing" because you are punning an int to an int, which has no effect. If you wanted to pun an int into a double however...

uint32_t num=5;
double& myvar=*(double*) &num;

Now you can manipulate nums memory as a double via myvar even though num is still an Int. This is a terrible idea and is just meant as a toy example of the use of punning.

Cole
  • 600
  • 6
  • 12
  • 1
    Your type punning example breaks C strict aliasing rules. In practice it means that it will not work reliably on compilers which do type based alias analysis. – user694733 Feb 22 '21 at 12:38
  • Ah, sorry. I missed the fact this was tagged as C, this example is from C++, not sure how relevant it would be for C. – Cole Feb 22 '21 at 20:52