12

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]
Cpp plus 1
  • 990
  • 8
  • 25
Progga
  • 343
  • 1
  • 3
  • 11

3 Answers3

7

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.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • The compiler shows no warnings and the debugger tells me, that Pointer contains the correct address – Progga Jul 20 '17 at 16:44
  • Note: OP is doing half a "round-trip here. In terms of pointer _sizes_, C allows object pointers to `char`, `int`, `struct foo`, etc and `void` to have different sizes with certain restrictions. Different sizes are uncommon. Function pointers may have a different size than object pointers - sometimes wider/narrower. All theses object pointers can certainly "round-trip" though `void *`. Functions pointers might not "round trip" - its UB. Any `void *` pointer can "round trip" though `uintptr_t`. – chux - Reinstate Monica Jul 20 '17 at 17:04
4

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.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Still will break if `sizeof(Value) > sizeof(uintptr_t)` in general case – Eugene Sh. Jul 20 '17 at 16:38
  • 1
    I wonder if I should insist that *guaranteed to be castable to and from arbitrary pointer types* is incorrect, and it is true for `void` pointers only.. :) – Eugene Sh. Jul 20 '17 at 16:44
  • @EugeneSh. What you say is true, but I am not going to change my answer because (a) I think that will just confuse OP further, and (b) an ABI where `void *` is not the same size and/or representation as other pointers _shouldn't_ provide `uintptr_t`. – zwol Jul 20 '17 at 16:47
  • As I said, I am not going to insist. But I would like to know there is the (b) assertion came from? Any pointer is castable to `void*`. `void*` is castable to `uintptr_t`. And vice-versa. I don't see a prevention to have different pointer sizes and still to conform it. – Eugene Sh. Jul 20 '17 at 16:49
  • @EugeneSh. The C standard _allows_ an awful lot of ABIs that contain bad design decisions. Providing `uintptr_t` on an architecture that needs multiple pointer sizes or representations is one such bad design decision. – zwol Jul 20 '17 at 17:03
  • Why do you think it is bad design for `uintptr_t` to be provided on a platform with multiple pointer sizes and representations? To me it totally makes sense to still provide `uintptr_t`, for the same reasons that it makes sense to provide `void *`, on systems with pointers of different size. Both have to big enough to encode all the bits in the biggest pointer type, and both enable doing something in a standard-conforming way that otherwise cannot be done (certain manipulations and generic referencing, respectively) . – mtraceur Dec 03 '21 at 03:03
  • @mtraceur The short version is that if `uintptr_t` exists, people don't just expect `(T *)(uintptr_t)val` to be lossless for arbitrary T and valid pointer-to-T `val`, they also expect the reverse, `(uintptr_t)(T *)val` to be lossless for arbitrary T and arbitrary _`uintptr_t`_ value `val`. This second property cannot be guaranteed unless all pointers are the same size. – zwol Dec 03 '21 at 13:45
  • Yeah but... why is that a good expectation for them to have? My reflexive is to judge this expectation as wrong and misguided and no one should expect this. It seems obvious to me (after having gotten used to thinking in C's type system flexibility and under-definedness) that I should not expect such a thing, unless I am making the conscious decision to be unportable to systems where that property doesn't hold, but that it is useful to be able to write code which manipulates pointers as integers without relying on that less universal property. – mtraceur Dec 03 '21 at 20:07
  • But I guess more importantly, asserting the opinionated position that this expectation in wrong or bad is not my main point - I would be very happy with a C standard that provided *two* pointer-as-integer types: a mandatory one where only the first expectation holds, and an optional one which is only defined when the second expectation also holds. – mtraceur Dec 03 '21 at 20:11
  • (Although what I would prefer even more is for the C standard to define some macro in some library that lets source code ask questions about all these useful implementation-defined variations, like asking "are pointers interchangeable?" with something like `#if _STDC_IDENTICAL_POINTER_REPRESENTATIONS`.) – mtraceur Dec 03 '21 at 20:18
  • @mtraceur I take a very pragmatic position on this sort of thing: I know there's lots of code out there that uses `(uintptr_t)(T *)val` -- typically without having ever considered the possibility that all pointers might not have the same number of value bits -- and so I say, if we're gonna design an ABI that breaks this, let us at least ensure it breaks at compile time, by withdrawing `uintptr_t`. – zwol Dec 03 '21 at 21:49
  • I mostly agree, but we should be careful when designing things to be safe for people who don't turn on their brain enough to consider certain possibilities. I think there's a balance beyond which that makes things worse, and in particular I don't think code relying on unportable assumptions should only in very exceptional cases be permitted to worsen things for carefully portable code. – mtraceur Dec 04 '21 at 16:54
  • Like I don't think it's less pragmatic to think that people coding for such ABIs would benefit from something like `uintptr_t` being available. In fact I think it might be pragmatic to assume that code which "uses `(uintptr_t)(T *)val` -- typically without having ever considered the possibility that all pointers might not have the same number of value bits" might in the general case work fine, or work correctly if used correctly by the caller of that code, even if the pointers do not have the same number of value bits. – mtraceur Dec 04 '21 at 17:02
0

Well, far more important than insane ABIs where sizeof(uintptr_t) is not at least as large as the largest pointer type, is to keep in mind pointer-provenance enforcements.

You should be able to cast to and from (void *) and uintptr_t, and through uintptr_t, you can use a (void *)arg "blind cookie" to pass around, e.g, int or unsigned int (just do a static_assert(sizeof(int) <= sizeof (uintptr_t) at most, to weed out insanely broken-by-design platforms).

But do not create a pointer through uintptr_t, or otherwise manipulate it: that's not going to have the correct provenance, and architectures such as CHERRY would fault and abort the program if you try to dereference that pointer. I.e. think of it as an one-way street for valid pointers: it can become an integer type, but it cannot go back to being a pointer.

That's why, if you need a blind cookie in an API, you use (void *) instead of the largest integer type or uintptr_t itself. That allows valid pointers, as well as whatever-fits-maskerading-as-a-pointer-that-will-not-be-dereferenced as the cookie contents.

anonymous
  • 81
  • 1
  • 3