I'm porting a product to a CMSIS-based RTOS, and the product needs to obtain the thread ID as a 32-bit integer. However, the CMSIS thread id type (osThreadId) is completely opaque, typedef'd to struct os_thread_cb * with a comment that it can safely be changed to something else as.
So is there a safe device-independent way to get a thread-unique integer id? Can I assume, for example, that the pointer address itself will be unique and constant for each thread?
osThreadId is a pointer - pointers on Cortex-M are 32 bit, so casting to an 32 bit integer type is safe. As it is a pointer it the thread's control block, it will be entirely unique to that thread.
Since you are using CMSIS, it rather implies that target architecture, but in the general case to ensure portability to systems with perhaps different sized pointers, you could use uintptr_t declared in stdint.h. A typedef may be useful:
typedef uintptr_t tid_t
Then you can safely cast an osThreadId to a tid_t - either implicitly or explicitly.
Strictly uintptr_t is an integer type capable of holding a void* such that casting back to a void* it will compare as equal to the original pointer. Since any pointer type may be cast to and from void*, it is generally the case that a uintptr_t can hold any pointer, however if such things worry you, you could strictly cast to a void* before assigning to a uintptr_t - but in practice this is probably unnecessary and certainly so on Cortex-M.
Related
I was wondering how void pointers are implemented. I tried to find it out on Godbolt with x86-64 (you can see it here), but it didn't reveal anything. How are void pointers implemented?
Edit:
This is the code I used:
int main() {
int volatile y = 123;
void* volatile x = &y;
}
All I was trying to see here is what x would look like. I put volatile so that gcc wouldn't eliminate it as dead code.
Generally speaking, all pointers on an x86-64 processor are simply 8 byte values containing some memory address. Only the compiler cares about what they point it.
From my perspective, the asm clearly reveals that void* uses the same object-representation as other pointers, such as int*, so casting to and from void* is a no-op that just keeps the compiler's type system happy.
Everything in asm is just bytes that you can do integer operations on if you want. Pointers are just integers that you can dereference. e.g. in x86-64 asm, +1 to a uintptr_t is no different than +1 to a char*, because C defines sizeof(char) as 1, and x86-64 is byte addressable so every integer increment to a pointer is a new byte. (So C implementations on x86-64 use CHAR_BIT=8).
void* is just a type you that can hold any pointer value, but that doesn't let you do math on it with +1 or whatever. OTOH in C you don't need to cast to assign other pointer types to/from it.
All of this follows from x86-64 having a flat memory model, not seg:off or something. And that function pointers have the same representation as data pointers. On hypothetical machines (or C implementations) you might see something special for void*.
e.g. a machine that emulated CHAR_BIT=8 on top of word-addressable memory might have sizeof(char*) > sizeof(int*), but void* would have to be wide enough to hold any possible pointer and might even have a different format than either. (Having a narrow char that you can't actually store in a thread-safe way (without a non-atomic RMW of the containing word) wouldn't be viable for C11.)
Semi-related: Does C have an equivalent of std::less from C++? talks about how C pointers might work in other hypothetical non-simple implementations. A seg:off pointer model wouldn't make void* different from int* though.
Adding to the answer above, memory has no type, whatsoever. Any generally speaking, every pointer type is basically an unsigned long integer value, pointing to some address in the memory.
Originally C language didn't have any void*, char* was the generic pointer type. And to quote from C: A Reference Manual:
the problem with this use of char* is that the compiler cannot check that
programmers always convert the pointer type properly
Is a uintmax_t guarantee to be large enought to hold a function pointer?
i know this:
The following type designates an unsigned integer type capable of
representing any value of any unsigned integer type:
uintmax_t
and
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 a pointer to void, and the result will compare
equal to the original pointer: uintptr_t
and that a void-Pointer is may not large enought to hold a function pointer, so a uintptr_t is may also not large enough to hold a function pointer.
There is no such guarantee in the C standard.
First, there is no guarantee that any pointer can be losslessly converted to an integer type (except a NULL pointer). It is true that uintptr_t must be able to losslessly represent a void pointer (and thus any onject pointer). However, there is no guarantee that an implementation has uintptr_t, since it and intptr_t are optional (last sentence of ยง 7.20.1.4).
Second, a function pointer is not an object pointer, and it is not necessarily possible to convert one to a void pointer and back. So even if uintptr_t does exist, it might not be big enough to hold a function pointer.
On an X/Open System Interface (XSI) compatible implementation (most Posix systems), you must be able to convert between void pointers and function pointers, and uintptr_t must exist. So in that case, you do have the guarantee. (The convertibility between void and function pointers is required by the dlsym system interface, which was moved from XSI to base Posix in Issue 7 (2008). However, the existence of uintptr_t continues to be an XSI extension.)
No, not in general. void* and in consequence [u]intptr_t are only guaranteed to be wide enough to hold object pointers, that is pointers to object types. Function pointers may, on some platforms, be wider and comprise more information than just an entry point to the function. So on such platforms a void* or uintptr_t has not enough bits to represent all information that would be needed.
On many platforms, function pointers have the same width as object pointers, though, and they may even allow to convert from one to another. But this is an extension of the C standard and you'd have to check with your platform documentation.
Is a uintmax_t guarantee to be large enough to hold a function pointer?
No guaranteed way as well answered by others.
Yet the size of a function pointer does exist and it does have a bit pattern. If sufficiently large, uintmax_t could hold the pointer's bit pattern (and maybe more). This size test could be assessed at compile time.
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
int foo(int x) {
return x+x;
}
int main(void) {
union {
uintmax_t um;
int (*fp)(int);
} u = {0};
assert(sizeof u.um >= sizeof u.fp); // This assertion may fail
u.fp = foo;
uintmax_t save = u.um;
printf("%ju\n", save);
u.um = save;
printf("%d\n", (*u.fp)(42));
return 0;
}
Output
4198816
84
I want to create a type to store pointers. The type should be compatible with C99 and have a fixed-width of 64 bits. I came up with several alternatives but they all seem flawed:
Using uint64_t is incorrect since conversions between pointers and integers are implementation-defined [C99 standard, 6.3.2.3].
uinptr_t also appears to be out of the picture, since the width of this type is not fixed and the type is optional anyway [7.18.1.4].
Using a struct such as
struct {
#ifdef __LP64__
void* ptr;
#else
// if big endian the following two fields need to be flipped
void* ptr;
uint32_t padding;
#endif
} fixed_ptr_type;
does not work either because the size of a pointer is not fixed even within the same implementation
Is there any C99-compatible definition of the type I'm looking for?
Object pointers
The best type to store object pointers is void *. Any object pointer can be converted to void * and back again.
Function pointers
A void * cannot necessarily store a function pointer. However, any function pointer can be converted to another type of function pointer, so you could store them in some arbitrary type (such as void (*)(void)).
Padding
I have no idea why you would need your pointer type to have a predetermined size, but you could pad them by using a union and hope that the result is not too large:
union fixed_ptr_type {
void *p;
char c[64/CHAR_BIT];
};
assert (CHAR_BIT * sizeof (union fixed_ptr_type) == 64);
I don't understand your objection to using the void * with padding. All objects of the same type have the same size. If a different object pointer type has a different size, that doesn't matter, because you convert it to void * to store it in your super-pointer.
Regarding uintptr_t: If it is not supported , then chances are that it's because there is actually no way of doing this on the particular platform.
So you could use uintptr_t. To add in the fixed-width requirement, you could cast to uintptr_t then to uint64_t (if you're happy with knowing you'll have to change your code when someone puts out a system that has pointers greater than 64bits!)
You cannot portably store pointer values in a 64-bit type. It's perfectly legal for an implementation to use 128-bit pointers.
If you don't mind losing portability to systems with pointers bigger than 64 bits, you can probably get away with using uint64_t. Conversions from pointer types to uint64_t are not guaranteed to work correctly without losing information, but they will almost certainly do so on any reasonable systems where pointers are no wider than 64 bits.
If an implementation has no 64-bit unsigned integer type without padding bits, then it will not define uint64_t at all (for example, a system with 9-bit bytes would not be able to implement uint64_t). There's a type uint_least64_t that's guaranteed, as the name implies, to be at least 64 bits wide; it will be exactly 64 bits on most systems, and wider than 64 bits only on systems where uint64_t doesn't exist.
uintptr_t is guaranteed to hold a converted void* value without loss of information, but it's not guaranteed to exist -- and if it doesn't exist, then no integer type can hold a converted void* value without loss of information. A conforming implementation needn't necessarily have any integer type that can hold a pointer value without loss of information.
Function pointers are another matter. Conversion from a function pointer to void*, or to any integer type, has undefined behavior (because the standard doesn't say what the behavior should be).
There simply is no 100% portable way to do what you're trying to do. You'll just have to settle for 99.9% portability. If you're not concerned with function pointers, I'd suggest using uint64_t (perhaps defining your own typedef to make it clear what you're doing) and add a compile-time or run-time check to confirm that sizeof (void*) <= sizeof (uint64_t). That should cover every existing implementation that I've ever heard of.
It might be helpful to know what your actual goal is. Why do you want to store pointers in no more or less than 64 bits? What problem does this solve that storing them in void* objects doesn't solve?
Incidentally, the __LP64__ macro that you mention in your question is non-standard.
Is it a good idea to use intptr_t as a general-purpose storage (to hold pointers and integer values) instead of void*? (As seen here: http://www.crystalspace3d.org/docs/online/manual/Api1_005f0-64_002dBit-Portability-Changes.html)
For what I've already read:
int -> void* -> int roundtrip is not guaranteed to hold original value; I guess int -> intptr_t -> int will do
pointer arithmetics on both void* and intptr_t require casts, so none gets advantage here
void* means less explicit casts when storing pointers, intptr_t means less casts when storing integer values
intptr_t requires C99
What else should I take into consideration?
Is it a good idea to use intptr_t as a general-purpose storage (to hold pointers and integer values) instead of void*?
No.
intptr_t is not guaranteed to exist. First, as you note, it was introduced in C99. Second, implementations are not required to have an integer type big enough to hold converted pointer values without loss of information.
Converting an int to intptr_t and back is unlikely to lose information but there's no actual guarantee that intptr_t is wider than int.
If you want to store pointer values, store them in pointer objects. That's what pointer objects are for.
Any pointer to an object or incomplete type can be converted to void* and back again without loss of information. There is no such guarantee for pointers to functions -- but any pointer-to-function type can be converted to any other pointer-to-function-type and back without loss of information. (I'm referring to the C standard; I think POSIX provides some additional guarantees.)
If you want to store either an integer or a pointer value in the same object, the first thing you should do is re-think your design. If you've already done so, and concluded that you really do want to do this, consider using a union (and keeping careful track of what kind of value you've stored most recently).
There are APIs that use a void* argument to allow arbitrary data to be passed; see, for example, the POSIX pthread_create() function. This can be abused by casting an integer value to void* but it's safer to pass the address of an integer object.
No, you can't be guaranteed that any particular type is a reasonable way of storing both pointers and integers, and besides, it makes your code confusing. There is a better way.
If you want to store an integer and a pointer in the same object, the clean and portable method is to use a union:
union foo {
int integer_foo;
void *pointer_foo;
};
This is portable and allows you to store both sorts of things in the size of storage needed for the larger of the two. It is guaranteed to always work.
What does the following expression mean?
unsigned char *res = malloc(5);
Now I cast res:
(long)res
What does this casting mean?
Using that value will interpret the address to which res points (which is just a number anyway) as a long.
It will work most of the time but it's not completely okay (depends a lot on how you're using it). For example if you simply want to print it, you can get away with
printf("%p", res);
As a rule of thumb: treat any cast with suspicion.
The allocated memory is not read, you're just casting the pointer to the memory to a long.
This doesn't directly answer your question but is a useful bit of information that is more-or-less relevant to your siutation.
A cast from a pointer type to an integer type is implementation defined (that means the implementation decides what happens when you cast a pointer to an integer). C99 implementations that do support some type of reversible conversion should also provide two types found in <stdint.h> specifically for converting pointers to integers, namely uintptr_t and intptr_t. If your implementation provides these two types, then you can safely convert a pointer to these types and back to the original pointer type.
Since these types are implementation defined, you will need to check your implementations documentation for what the underlying types are.