addr is a parameter to the function and read_value is a local variable of the function. both are of type int.
Then what does:
read_value = (* (int *) (addr))
mean?
(int *) (addr) casts the numeric value of addr to an int * pointer. Unless special care is taken, this operation is unsafe because an arbitrary value of addr can violate the alignment requirements for int. In general, if the value of addr is not a multiple of the size of an int, it can lead to a misaligned read which can eventually result in a SIGBUS signal.
The asterisk finally fetches the int value located at that address (called dereferencing) and saves it into read_value. It is at this point where the misaligned read can happen, if the address is not sufficiently aligned. The dereference might as well cause a segmentation fault if the address happens to be restricted or protected.
I would actually declare addr to be of type uintptr_t, rather than int, since that gives more safety between the cast to int *. uintptr_t should correspond to the size and the representation of a pointer, while the int type is semantically unrelated to a pointer.
You cast addr to a pointer to int, dereference it, and put it in read_value .
If addr is really int, I think that it's undefined behavior.
Take the following example:
int read_value = 0;
int address = 0x1234;
read_value = *(int *) address;
This is equivalent to:
read_value = *(int *) 0x1234;
this reads an int at address 0x1234 and stores it in read_value object. It is done by first converting the int value 0x1234 to a pointer to int and then dereferencing the pointer to access the int value pointed at.
Note that the conversion (int *) 0x1234 is implementation defined.
(C99, 6.3.2.3p5) "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."
And the dereference of the pointer is undefined behavior if it is an invalid pointer or if it doesn't have the correct alignment. Any use of an invalid pointer is undefined behavior. An invalid pointer is a pointer that is not null but that doesn't point to a proper object or function.
Related
I have a code that I passed a certain place in the memory. This place in the memory is pointing to an array
uint32_t *ps2 = NULL;
uint32_t src_address = 0x1ffffc3;
How can I read the value of the array from this address?
I tried to cast it as follows
*ps2 = (void *)src_address;
but it gives me an error: invalid conversion from ‘void*’ to ‘uint32_t
Regards,
You have two problems:
First of all, the pointer ps2 is a null pointer, it doesn't point anywhere. That means you can't dereference it.
src_address is not a pointer, when it really should be.
All in all there's seems to be some mixup in your understanding of pointers and how they are used.
For it to work, first define ps2 as not a pointer:
uint32_t ps2;
then define src_address as a pointer:
uint32_t *src_address = (uint32_t *) 0x1ffffc3;
and finally dereference src_address like a normal pointer:
ps2 = *src_address;
There is a possible third problem: The address of src_address is not aligned for an uint32_t. On some systems unaligned access is invalid and will lead to hardware exceptions.
This is because you cannot guaranteely convert a pointer type to any integer type. 6.3.2.3(p5) (emp. mine):
Any pointer type may be converted to an integer type. Except as
previously specified, the result is implementation-defined. If the
result cannot be represented in the integer type, the behavior
is undefined. The result need not be in the range of values
of any integer type.
The undefined behavior may well be the compile error you see.
There are dedicated types intptr_t, uintptr_t. They are described at 7.20.1.4
The following type designates a signed integer type with the
property that any valid pointer tovoidcan be converted to this
type, then converted back to pointer tovoid,and the result will
compare equal to the original pointer
If your implementation implements the types you should use them. If no there is no any other conforming portable way to convert integers to pointers.
You want to give pointer p2 while (other than NULL).
But here you set this value not to pointer itself, but to memory it points to.
*ps2 = (void *)src_address;
And it points to....well nothing, it's NULL pointer (address 0 is not valid).
By using * you are accessing (or setting) value that pointer points to. So you need to remove * to change the pointer itself.
ps2 = (void *)src_address;
Or even better:
ps2 = (uint32_t*)src_address;
Then to read value from that address:
uint32_t value = *ps;
uint32_t *ps2 = NULL; // Assuming this is where you want your array to point.
uint32_t src_address = 0x1ffffc3; // source array
You can do :
ps2 = (uint32_t *)src_address; // Assuming 32 bit machine
Now to access every element of array, all you have to do is -
*ps2[0], *ps2[1]
ps2[0] will point to src_address + 0.
ps2[1] will point to src_address + 4(As pointer type is uint32_t)
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*) #
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.
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.
I've heard from many people that you cannot guarantee typecasting will be performed lossless. Is that only true if you don't know your processor, that is, you haven't verified the number of bytes used for your data types? Let me give an example:
If you execute the following:
typedef struct
{
int i;
char c;
float f;
double d;
} structure;
size_t voidPtrSz = sizeof(void *);
size_t charPtrSz = sizeof(char *);
size_t intPtrSz = sizeof(char *);
size_t floatPtrSz = sizeof(float *);
size_t doublePtrSz = sizeof(double *);
size_t structPtrSz = sizeof(structure *);
size_t funcPtrSz = sizeof(int (*)(float, char));
printf("%lu\n", voidPtrSz);
printf("%lu\n", charPtrSz);
printf("%lu\n", intPtrSz);
printf("%lu\n", floatPtrSz);
printf("%lu\n", doublePtrSz);
printf("%lu\n", structPtrSz);
printf("%lu\n", funcPtrSz);
…and the output is the following…
4
4
4
4
4
4
4
Can you assume that in all cases you can typecast a specific data type pointer to another data type pointer safely? For example, if you execute this:
int foo(float, char)
{
}
void *bar(void)
{
return (void *)foo;
}
int (*pFunc)(float, char) = bar();
Can you assume with certitude that pFunc has the address of foo?
Regarding your specific code example, let's refer to section 6.3.2.3 of the C99 language standard:
A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
Note that a pointer-to-function is not the same as pointer-to-object. The only mention of pointer-to-function conversions is:
A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.
So your code example invokes undefined behaviour.
If we avoid function-pointer conversions, the following paragraph explains everything:
A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer.
Note: Converting between pointer types is a separate issue from converting and then dereferencing (in general, that's only valid if you're converting to char * and then dereferencing.)
Can you assume that in all cases you can typecast a specific data type pointer to another data type pointer safely?
Any data pointer can be safely cast to char* or void*. Any char* or void* thus created can be cast back to its original type. Any other data pointer cast leads to undefined behavior when indirection is performed on the pointer.
Any function pointer type can be cast to any other function pointer type, although you should not call a function through the wrong type. Casting a function pointer to void* or any other data pointer type results in undefined behavior.
Is that only true if you don't know your processor, that is, you haven't verified the number of bytes used for your data types?
Even then, you're not safe. When the C standard says a construct has undefined behavior, compiler writers are free to handle the construct as they wish. The result is that even though you think you know a construct with UB will be handled because you know the target CPU, optimizing compilers may cut corners and generate very different code than you expect.
#Oli Charlesworth gives you a great answer.
I hope I can shed a little light on what pointer are so you can better understand pointer mechanics:
A pointer is an address. This address is the address of the first byte of your data. The type of the pointer specifies how many bytes starting from that first byte are part of the data and how those bytes encode the data.
For instance, on gcc x86, if you have a int * p, the value held by p tells the starting address of data, and the type of p (int *) tells that at that address he will interpret 4 bytes (in little endian byte-order) in two's complement signed number representation.
A void * pointer is a "generic pointer". The pointer still holds an address, but the pointer type doesn't specify what kind of data you find there, or even how many bytes form the data, so you can never access data through a void * pointer, but as answered before, you can safely convert between a pointer to void and a pointer to any incomplete or object type.
A pointer to function holds the address of a function, and the type of the pointer tells how to call that function (what parameters and of what kind) and what the function returns.
int num = 45,*ptr1,*ptr2;
ptr1=#
ptr2=&ptr1;
printf("%d\n",*ptr1);
I've been thinking about this question for a while, but couldn't find a way to understand it,why &ptr1 can not be assigned to ptr2 in line 3, &ptr1 is a pointer's address,this address is no different from other address like an address of an integer, say
int a=1;
ptr2=&a;
Which means that I can assign an integer's address to a pointer,but not a pointer's address to a pointer,what differences between these two "address" could possibly make them different? Address of common variables can be assigned to single pointer,but address of pointers can not be assigned to single pointer?
I know the right way to do it is use double pointer to declare ptr2,but why single pointer can't?
Simply put, pointers are not addresses, they are varibles representing an address with a type. So the types have be compatible for pointers to assign (with the exception of void * generic pointer).
ptr2 = &ptr1;
ptr1 has a type of int *, so &ptr1 has a type of int **, it's not the same with ptr2, which has a type of int *.
Reference: C99 6.5.16.1 Simple assignment
both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right.
Yes you can assign a pointer's address to a pointer, but it must be a pointer to a pointer variable.
int **ptr3;
ptr3 = &ptr1;
The reason you can't assign it the way you were trying is that a pointer to an int is not the same as an int. Pointers must be pointing to the same type to be compatible. If you really know what you're doing you can explicitly cast it, but that's a path to danger.
Your code is wrong. This expression:
ptr2 = &ptr1;
Attempts to make an int * out of an int ** without a cast. The C standard forbids such conversions without an explicit cast.
The reason it's not allowed is that pointer types aren't guaranteed by the standard to all be the same size - so the pointer to your pointer might not fit in the variable you declared to be a pointer to an int.
Since pointers to any type can be converted to and from void * implicitly, you could write (correct, but probably confusing) analogous code to that in your question:
int num = 45;
void *ptr1, *ptr2;
ptr1 = #
ptr2 = &ptr1;
But doing so will require you to carry around all of the type information in some other way:
printf("%d\n",*(int *)ptr1);
printf("%d\n",*(int **)ptr2);
The short answer is that type matters; a pointer to int is a different, incompatible type from pointer to pointer to int. As others have mentioned, different pointer types may have different sizes and representations.
A pointer value is not just an address; it has additional type semantics. For example, the expression ptr++ will advance the pointer to the address of the next object of the base type. If the base type is char, then the pointer is advanced 1 byte. If the base type is int, the pointer is advanced sizeof (int) bytes.
Simply put because it will confuse the compiler. The compiler can work only according to the language standard. It doesn't have a brain of its own.
The language standard tells the compiler that if there is a int *
go to the address stored in that variable and use it.
In case there is a int ** then it tells it
go to the address in that variable. You aren't done yet as that is also an address. Go there and use what is present there.
This goes on and on for int *** and so on.
Hope this helps you to get over this basic confusion.
If you could assign any address to any pointer regardless of type, on the grounds that one address is just like any other address, consider the trouble you could get yourself into if the following became legal:
int n = 40;
int * p = &n;
int ** pp = &n; /* Typeless address assignment as you would like */
printf("%d\n", **pp); /* Bad Things happen here */
or the other way round:
int n = 40;
int * p = &n;
int * p2 = &p; /* More typeless address assignment */
printf("%d\n", *p2); /* Definitely not what you want */
Even if one address was the same as any other, sensibly dereferencing a pointer would become somewhat troublesome if things worked the way you suggest.
The reason you can't do what you suggest is that the type information you'd lose under your proposal is needed for dereferencing to work. If all you wanted pointers to do was to store and retrieve addresses, you'd have a point, but they're not just used for this. If they were, we could just have void pointers and be done with it.
I completely agree to your statement that when pointer variable always would store a value that is integer, as the address to any variable/array would be an integer.
But still the data-type that is used to declare the pointer is the one of whose address it would be storing.
There are 3 points:
1. The bits that are used while storing integer values differ from machine to machine.
i.e. 32-bit, 64-bit and further more complications may add-up.
2. Memory occupied i.e. bytes of data stored in it. Reason is : somewhere even the pointer variable is stored in memory. Right?
3. There are certain operations associated with pointers like ++ or --.
Remember, pointer type is dependent on the type of variable it points to.
This is the reason/need for the pointer to pointer.