Failing to understand what the expression *(uint32_t*) does - c

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?

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.

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.

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

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.

Related

Why typecast a pointer at its declaration?

Not sure if I'm phrasing the question properly nor how to best explain what I'm trying to have answered so bear with me.
When defining a microcontroller register, you would write something like this:
#define io_register (*(volatile unsigned char *)0x25)
I also found this line of code here: Pass a hex address to a Pointer Variable
int *pointer = (int *) 0x00010010;
I understand that a pointer that points at an int is being declared left of the = sign, but why is there a typecast *(int ) right of it?
Same with the #define, I understand that the value at 0x25 is being dereferenced but why am I not able to write it like this:
#define io_register *(0x25)
or
int *pointer = 0x25;
What am I missing here? Please feel free to rephrase or correct any mistakes I've made, still trying to wrap my head around pointers and registers.
An important prerequisite of any assignment operation is data type compatibility. In other words, the data type of the equation's (int *pointer = 0x25;) LHS (int *pointer) must be compatible with whatever data type results from the evaluation of the equation's RHS (0x25).
For example, if both LHS and RHS are int types, then there will be no problem assigning the integer value of RHS to LHS.
But, in this expression:
int *pointer = 0x25;
you have int * data type in LHS and int data type in RHS. So, there's an incompatibility between LHS & RHS. That's why you need to cast RHS to (int *).
The cast suppresses the warning. That is its role in those statements. Otherwise it will be casted implicit way, and warning omitted. No difference in the code or program execution.
#define io_register *(0x25)
*(0x25) will not be a pointer in any circumstanes and io_register when used means : multiply by 0x25.
int x = 10 io_register;
which means: int x = 10 *(0x25);
With
int *pointer = (int *) 0x00010010;
You are telling the pointer to point to an absolute address in memory, the value at that address can be dereferenced with *pointer.
With
int *pointer = 0x25;
The compiler does not like that because 0x25 is an integer but pointer is not an integer, it is an integer pointer.
The cast is needed because 0x00010010 is not a pointer but an integer (it is the first type among int, unsigned int or long int that can represent the value 65552) .
Only integer constant expressions having value of 0 can be converted to a pointer implicitly (i.e. without a cast); the resulting pointer would be a null pointer.
You can naturally write
#define io_register *(0x25)
but you cannot use that io_register to access memory at address 0x25, because that 0x25 is not a pointer - it is an integer.
In C one does not really access addresses; instead one dereferences pointers to get lvalues that designate an object, and then get the value of that object, or a set a new value to it.

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:
Can we use pointers without declaring any variables?
Why pointer to a pointer is used? Is it because, we cannot do ptr = 0x04000000?
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;
Ad. 1: C allows to convert an integral value to a pointer and vice versa. Weather you assign the (intermediate) conversion to a variable or not does not matter. Code part (volatile uint32*)(0x04000000) actually converts integral literal 0x0400000 to a pointer of type uint32*; Note the volatile, which turns off any compiler optimizations and lets the code actually access the respective memory whenever dereferened.
Ad 2: there is no pointer to pointer; *((volatile uint32*)(0x04000000)) just dereferences the pointer (which has been explained in (1).
I guess this is about GameBoy Advance development.
You can write to a memory address without declaring any variable, a pointer is a type of value which represents a memory address, you can write and read from it without the need to store it anywhere.
It's not a pointer to a pointer, it's a hardcoded address which is cast to a (volatile uint32*), and the macro adds the * operator in front just to save you from writing it, which is just confusing.
I'm working on a framework for GBA development right in these days, maybe you can pick some informations or code from there, mind that code is C++14 though.

How to Cast Integer Value to Pointer Address Without Triggering Warnings

I have the following variable
uint32_t Value = 0x80
0x80 represents an address in the memory e.g.
// Write 2 at address 0x80
*(uint32_t*)((uint32_t)0x80) = 2;
How can i cast Value to a Pointer, so it points to 0x80?
uint32_t *Pointer = ?? Value;
This:
(uint32_t*)(uint32_t)Value;
returns:
warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
To handle integer to object pointer conversion, use the optional integer uintptr_t or intptr_t types. Function pointers are a separate matter.
The following type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer C11dr 7.20.1.4 1
uintptr_t
Then convert the void * to the desired type.
#include <stdint.h>
uintptr_t Value = 0x80;
uint32_t *Pointer = (void *) Value;
If 0x80 was not derived from a valid uint32_t *, the result in undefined behavior (UB). Yet it sounds like OP is on a platform with memory mapped data locations.
I will spell it out for you: given
uint32_t Value = 0x80;
you want
*((uint32_t *)(uintptr_t)Value) = 2;
The type uintptr_t, if it exists, is guaranteed to be castable to and from arbitrary pointer types without loss of information. It is not guaranteed to exist, but on platforms where it doesn't exist, what you're trying to do cannot safely be done without a great deal more information.
No other type is officially guaranteed to have this property; however, "cpp plus 1" is correct that size_t usually also does. I would go so far as to say that any ABI for a flat-memory architecture, that doesn't make size_t have this property, was specified incorrectly, but they do exist and sometimes you have to work with them.
It would be better to declare Value with a pointer type in the first place:
uint32_t *const Value = (uint32_t *)(uintptr_t)0x80;
because then you only have to write the casts when you initialize it, not when you use it,
*Value = 2;
and you probably have a bunch of places where you use it. This also avoids a potential problem if it happens that sizeof(uintptr_t) < sizeof(uint32_t), as uint32_t is never involved in the address arithmetic; the compiler may complain if the cast from 0x80 to uintptr_t actually truncates the constant, but that shouldn't ever happen with the real memory addresses you are accessing this way.

How to dereference and autoincrement a cast pointer in C?

I have a void* in plain C that I'm using while walking through some unstructured data, and I'd like to cast with dereference and autoincrement as I go. I would like to write the following:
void* ptr = /*something*/;
uint16_t i = *((uint16_t*) ptr)++;
The compiler objects and tells me "lvalue required as increment operand", but I guess I thought that a pointer cast as a pointer would still qualify as an lvalue.
Clearly my intent is for ptr to now point two bytes beyond where it pointed before. I can't remove the parentheses around the cast of ptr because ++ has higher precedence than the cast, so this won't work as I want it to:
int i = *(uint16_t*) ptr++;
I could of course do my own incrementing, like the following, but I was hoping for something elegant and concise:
int i = *(uint16_t) ptr;
ptr += sizeof(uint16_t);
What's a good way to do this?
It would be simplest to write:
uint16_t *uptr = ptr;
uint16_t i = *uptr++;
ptr = uptr; // if required
You might be able to restructure the larger code to have a uint16_t * as much as possible, removing the need for so many conversions.
Incrementing a pointer cast to a different type has been disallowed for a long time, probably since C89. The reason is that the conversion from one type of pointer to another type can change the representation, and thus might not refer to the same object.
You must split the expression into 2 statements:
void *ptr = /*something*/;
uint16_t i = *(uint16_t*)ptr;
ptr = (uint16_t*)ptr + 1;
Or if context commands for a single expression, you could use this hack:
uint16_t i = ((uint16_t*)(ptr = (uint16_t*)ptr + 1))[-1];
Or this one if you want to obfuscate even more:
uint16_t i = (-1)[(uint16_t*)(ptr = (uint16_t*)ptr + 1)];
void *ptr;
*(*(uint16_t **)&ptr)++;
Taking the address to the pointer (a void ** type) casting it to uint16_t **, dereference it, now you have a valid lvalue you can increment and dereference normally.

Why is it better to use cast when you assign an integer to a pointer?

Let's assume I have this code:
char *pointer;
unsigned int a;
pointer = a;
For me this doesn't generate any problems but I will receive the following warning:
assignment makes pointer from integer without a cast
Why is it better to use cast?
I'm using a 32 bit machine.
Edit:
By "this doesn't generate any problems", I was saying that it compiles and pointer stores the value of variable a.
Edit:
And variable a will actually store an address.
It's almost always better not to try to assign an integer value to a pointer at all. In the rare cases where such an assignment makes sense, a cast is not just better, it's required.
Pointers and integers are distinct types. In some cases it can make sense to mix them; in such cases you need a cast, because there is no implicit conversion between integers and pointers (other than the special case of 0, treated as a null pointer constant).
char *pointer;
int a; /* or unsigned int a, it's the same either way */
pointer = a;
This assignment is illegal (more precisely, a constraint violation). A conforming C compiler must at least warn about it. Many compilers, after printing a warning, will generate code that performs an implicit conversion, but that's not required by the language, and you shouldn't depend on it. My personal opinion is that compilers that do this aren't doing you any favors.
pointer = (char*)a;
This is legal; the cast tells the compiler to generate an conversion from int to char*. But the result of that conversion is implementation-defined, and very likely doesn't yield a meaningful result -- even if char* and int happen to be the same size.
(In the code in your question, a hasn't been initialized. I presume that's not the case in the real code you haven't shown us.)
What are you actually trying to accomplish?
Because in C:
pointer = a; // not valid in C: a is an int object
// pointer an object of pointer type
the statement is not valid, you need an explicit conversion (a cast) to convert a value to a pointer type.
pointer = (char *) a; // valid assignmnent
Some compilers are nice with you and will implicitly convert the int value to a pointer type (and add a warning!) but they are not required to do so and a compiler can refuse to compile your program.
% cat int-cast.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("sizeof char *: %zu\n", sizeof (char *));
printf("sizeof int: %zu\n", sizeof (int));
}
Then if compiled 32 or 64 bit the size of the types differ
% cc -m32 int-cast.c && ./a.out
sizeof char *: 4
sizeof int: 4
% cc -m64 int-cast.c && ./a.out
sizeof char *: 8
sizeof int: 4
And as Keith Thompson explicitly points out, even if they were guaranteed to be the same size, a cast would still be required, and the result would be at best implementation-defined.
In C a pointer is not only an address in memory, it is also a type:
pointer = memory size + type
The best way to understand this is to think about pointer arithmetic that prevent to do some arithmetics operations. For example, you can do:
int i;
int * ptr, ptr2;
ptr = &i; // Assign the address of an int to an int pointer
ptr2 = ptr + 2; // Shift the pointer of two int size further
i = ptr - ptr2; // Compute the number of slots between ptr and ptr2
But, you cannot do:
int i;
char c;
int * ptr;
char * ptr2;
ptr = &c; // The types are not matching, the compiler will complain
ptr = 2 * ptr; // This is not allowed by pointer arithmetic
ptr = ptr + ptr; // This is not allowed by pointer arithmetic
And so on...
But, what you have always to remember is that a pointer is not only a memory address. It is the combination of a memory address and a type.

Resources