I'm using a library which has a function with the following signature:
void LED_stop_blink_task ( void * callback_parameter );
The actual parameter the void pointer stands for is a pointer to uint32_t, which is the number of the led on the board.
Is there a way to call this function without using a variable to hold the data ?
In my imagination it will be like
LED_stop_blink_task(&35);
or the only way is like this:
uint32_t led_num = 35;
LED_stop_blink_task(&led_num);
If you're asking why I want to throw the variable away, well, I'm just curious if it's possible...
On most platforms it's possible to simply stuff the int in a void *:
LED_stop_blink_task((void *)32);
Then in the function you can cast to int.
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.
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.
In practice this will work on any POSIX-supported platform. For example TLPI says:
Strictly speaking, the C standards don’t define the results of casting
int to void * and vice versa. However, most C compilers permit these
operations, and they produce the desired result; that is, int j ==
(int) ((void *) j).
Cnicutar's answer is almost perfect; let me extend it with that it's not really portable - int is not guaranteed to be of the same size (or smaller) than a pointer, so you should use intptr_t or uintptr_t instead.
Related
Microsoft extensions to C and C++:
To perform the same cast and also maintain ANSI compatibility, you can cast the function pointer
to a uintptr_t before you cast it to a data pointer:
int ( * pfunc ) ();
int *pdata;
pdata = ( int * ) (uintptr_t) pfunc;
Rationale for C, Revision 5.10, April-2003:
Even with an explicit cast, it is invalid to convert a function pointer to an object pointer
or a pointer to void, or vice versa.
C11:
7.20.1.4 Integer types capable of holding object pointers
Does it mean that pdata = ( int * ) (uintptr_t) pfunc; in invalid?
As Steve Summit says:
The C standard is written to assume that pointers to different object types, and especially pointers to function as opposed to object types, might have different representations.
While pdata = ( int * ) pfunc; leads to UB, it seems that pdata = ( int * ) (uintptr_t) pfunc; leads to IB. This is because "Any pointer type may be converted to an integer type" and "An integer may be converted to any pointer type" and uintptr_t is integer type.
Given the definitions
int (*pfunc)();
int *pdata;
, the assignments
pdata = (int *)pfunc;
pdata = (int *)(uintptr_t)pfunc;
are, IMO, equivalent. On a platform where data pointers are of the same size as, or larger than, function pointers, both assignments will work as desired. But on a platform where data pointers are smaller than function pointers, both assignments will inevitably scrape off some of the bits of the function pointer, resulting in a data pointer which can not be converted back to the original function pointer later.
In particular, I believe that both assignments are equivalent despite the presence of the (uintptr_t) cast in the second one. I believe that cast accomplishes precisely nothing.
On a platform where data pointers are smaller than function pointers, and where type uintptr_t is of the same size as data pointers, in the assignment
pdata = (int *)(uintptr_t)pfunc;
, the cast to (uintptr_t) will scrape off some of the bits of pfunc's value.
On a platform where data pointers are smaller than function pointers, and where type uintptr_t is of the same size as function pointers, in the assignment
pdata = (int *)(uintptr_t)pfunc;
, the cast to (int *) will scrape off some of the bits of pfunc's value.
In both cases pdata will end up with only some fraction of pfunc's original value.
(Here I disregard the possibility of architectures with padding bits or the like. On some bizarre, hypothetical platform where function pointers are larger than data pointers, but the extra bits are always 0, both assignments would again work.)
(I've also disregarded the possibility that int * is a different size than void *. I'm not sure whether that would affect the answer, whether a "detour" via void * is more or less un- or necessary when attempting a conversion from int (*)() to int *.)
Casting to uintptr_t only works if this type is defined, which may not be the case on legacy systems using ancient compilers. Note however that uintptr_t must be large enough for any object pointer, especially char * or void *, but may be smaller than function pointers. Such architectures are rare today and Microsoft compilers probably no longer support them, but they were common place in the 16-bit world (MS/DOS, Windows 1, 2 and 3.x) where the medium model had 32-bit segmented code pointers and 16-bit data pointers.
Note also that the C Standard allows for int * and void * to have a different size and representation albeit no Microsoft compiler supports such exotic targets.
On current systems with modern compilers, all data pointers and code pointers have almost always the same size and representation. This is actually a requirement for POSIX compatibility, so the recommendation to use an intermediary cast to (uintptr_t) is valid and effective.
For complete portability, if the goal is to pass a function pointer via an opaque void *, you can always allocate an object of the proper function pointer type, initialize it with pfunc and pass its address:
// setting up the void *
int (*pfunc)();
void *pdata = malloc(sizeof pfunc);
memcpy(pdata, &pfunc, sizeof pfunc);
// using the void *
int (**ppfunc)() = pdata;
(*ppfunc)(); // equivalent to (**ppfunc)();
Is conversion of a function pointer to a uintptr_t / intptr_t invalid?
No. It may be valid. It may be undefined behavior.
Conversion of a function pointer to ìnt* is not defined. Nor to any object pointer. Nor to void *.
pdata = ( int * ) pfunc; is undefined behavior.
Conversion of a function pointer to an integer type is allowed, with restrictions:
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. C17dr 6.3.2.3 6
Also integer to a pointer type is allowed.
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. C17dr 6.3.2.3 6
void * to integer to void * is defined. Object pointer to/from void* is defined. Then the optional (u)intptr_t types are sufficient for round-trip success. Yet we are concerned about a function pointer. Often enough function pointers are wider than an int *.
Thus converting a function pointer to int * only makes sense through an integer type, wider the better.
VS may recommend through the optional type uintptr_t and is likely sufficient if information is lossless on other platforms. Yet uintmax_t may afford less loss of information, especially in the function pointer to integer step, so I pedantically suggest:
pdata = ( int * ) (uintmax_t) pfunc;
Regardless of the steps taken, code is likely to become implementation specific and deserves guards.
#ifdef this && that
pdata = ( int * ) (uintmax_t) pfunc;
#else
#error TBD code
#endif
Migrating the solution from the question to an answer:
Here is the answer from Microsoft:
Q: How exactly "cast the function pointer to a uintptr_t before you cast it to a data pointer" leads to maintaining "ANSI compatibility"?
A: Without the cast to uintptr_t it’s possible that the code will fail to compile with other compilers, even if they use the same pointer model. For example: https://gcc.godbolt.org/z/9EjTe1s4x - if you add the uintptr_t it compiles without warnings/errors.
void *vp = (void *)5;
uint8_t i = (uint8_t)vp;
Will i == 5 on all 8-bit and higher cpus? What are the risks doing this? Is there a better way to have a variable store either an 8-bit integer literal or a pointer in C99?
I have an array of function pointers to functions that take a void *. Some functions need to interpret the void * as a uint8_t.
void *vp = 5; should not compile; the C standard at least requires the compiler to issue a diagnostic message. You can request the conversion with void *vp = (void *) 5;, and you can request the reverse conversion with (uint8_t) vp. The C standard does not guarantee this will reproduce the original value. (Conversions involving pointers are specified in C 2018 6.3.2.3.) It is likely to work in most C implementations.
An alternative that would be defined by the C standard would be to use offsets into some sufficiently large object you already have. For example, if you have some array A, and you want to store some small number n in a void *, then you can do:
void *vp = (char *) A + n; // Point n bytes into the object A.
and you can recover the number with:
(char *) vp - (char *) A // Subtract base address to recover offset.
The C standard does allow for conversions between an integer and a pointer, however it doesn't explain exactly how it should happen. That is left up to each specific implementation.
Section 6.3.2.3 p5-6 of the C standard describes these conversions:
5 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.
6 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.
Under gcc in particular what you're doing will work, however it's not guaranteed to work on all compilers or architectures.
What is guaranteed to work however is to take the address of a compound literal:
void *vp = &(uint8_t){5};
uint8_t i = *(uint8_t *)vp;
This creates a temporary object of type uint8_t and takes its address. This address can then be converted to a void * and back which is fully standard compliant, as per paragraph 1 of 6.3.2.3:
A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
The lifetime of the compound literal is that of the block in which it is defined. So as long the pointer isn't used after that block ends it will work.
If however you intend to pass it to a function that starts a thread, you're better off dynamically allocating memory for the value and passing that. Otherwise, you run the risk of the function where the compound literal is defined returning while the thread function is running which could attempt to use that pointer.
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
In the GLib documentation, there is a chapter on type conversion macros.
In the discussion on converting an int to a void* pointer it says (emphasis mine):
Naively, you might try this, but it's incorrect:
gpointer p;
int i;
p = (void*) 42;
i = (int) p;
Again, that example was not correct, don't copy it. The problem is
that on some systems you need to do this:
gpointer p;
int i;
p = (void*) (long) 42;
i = (int) (long) p;
(source: GLib Reference Manual for GLib 2.39.92, chapter Type Conversion Macros ).
Why is that cast to long necessary?
Should any required widening of the int not happen automatically as part of the cast to a pointer?
The glib documentation is wrong, both for their (freely chosen) example, and in general.
gpointer p;
int i;
p = (void*) 42;
i = (int) p;
and
gpointer p;
int i;
p = (void*) (long) 42;
i = (int) (long) p;
will both lead to identical values of i and p on all conforming c implementations.
The example is poorly chosen, because 42 is guaranteed to be representable by int and long (C11 draft standard n157: 5.2.4.2.1 Sizes of integer types ).
A more illustrative (and testable) example would be
int f(int x)
{
void *p = (void*) x;
int r = (int)p;
return r;
}
This will round-trip the int-value iff void* can represent every value that int can, which practically means sizeof(int) <= sizeof(void*) (theoretically: padding bits, yadda, yadda, doesn't actually matter). For other integer types, same problem, same actual rule (sizeof(integer_type) <= sizeof(void*)).
Conversely, the real problem, properly illustrated:
void *p(void *x)
{
char c = (char)x;
void *r = (void*)c;
return r;
}
Wow, that can't possibly work, right? (actually, it might).
In order to round-trip a pointer (which software has done unnecessarily for a long time), you also have to ensure that the integer type you round-trip through can unambiguously represent every possible value of the pointer type.
Historically, much software was written by monkeys that assumed that pointers could round-trip through int, possibly because of K&R c's implicit int-"feature" and lots of people forgetting to #include <stdlib.h> and then casting the result of malloc() to a pointer type, thus accidentally roundtripping through int. On the machines the code was developed for sizeof(int) == sizeof(void*), so this worked. When the switch to 64-bit machines, with 64-bit addresses (pointers) happened, a lot of software expected two mutually exclusive things:
1) int is a 32-bit 2's complement integer (typically also expecting signed overflow to wrap around)
2) sizeof(int) == sizeof(void*)
Some systems (cough Windows cough) also assumed sizeof(long) == sizeof(int), most others had 64-bit long.
Consequently, on most systems, changing the round-tripping intermediate integer type to long fixed the (unnecessarily broken) code:
void *p(void *x)
{
long l = (long)x;
void *r = (void*)l;
return r;
}
except of course, on Windows. On the plus side, for most non-Windows (and non 16-bit) systems sizeof(long) == sizeof(void*) is true, so the round-trip works both ways.
So:
the example is wrong
the type chosen to guarantee round-trip doesn't guarantee round-trip
Of course, the c standard has a (naturally standard-conforming) solution in intptr_t/uintptr_t (C11 draft standard n1570: 7.20.1.4 Integer types capable of holding object pointers), which are specified to guarantee the
pointer -> integer type -> pointer
round-trip (though not the reverse).
As according to the C99: 6.3.2.3 quote:
5 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.56)
6 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.
According to the documentation at the link you mentioned:
Pointers are always at least 32 bits in size (on all platforms GLib
intends to support). Thus you can store at least 32-bit integer values
in a pointer value.
And further more long is guaranteed to be atleast 32-bits.
So,the code
gpointer p;
int i;
p = (void*) (long) 42;
i = (int) (long) p;
is safer,more portable and well defined for upto 32-bit integers only, as advertised by GLib.
As I understand it, the code (void*)(long)42 is "better" than (void*)42 because it gets rid of this warning for gcc:
cast to pointer from integer of different size [-Wint-to-pointer-cast]
on environments where void* and long have the same size, but different from int. According to C99, §6.4.4.1 ¶5:
The type of an integer constant is the first of the corresponding list in which its value can be represented.
Thus, 42 is interpreted as int, had this constant be assigned directly to a void* (when sizeof(void*)!=sizeof(int)), the above warning would pop up, but everyone wants clean compilations. This is the problem (issue?) the Glib doc is pointing to: it happens on some systems.
So, two issues:
Assign integer to pointer of same size
Assign integer to pointer of different size
Curiously enough for me is that, even though both cases have the same status on the C standard and in the gcc implementation notes (see gcc implementation notes), gcc only shows the warning for 2.
On the other hand, it is clear that casting to long is not always the solution (still, on modern ABIs sizeof(void*)==sizeof(long) most of the times), there are many possible combinations depending on the size of int,long,long long and void*, for 64bits architectures and in general. That is why glib developers try to find the matching integer type for pointers and assign glib_gpi_cast and glib_gpui_cast accordingly for the mason build system. Later, these mason variables are used in here to generate those conversion macros the right way (see also this for basic glib types). Eventually, those macros first cast an integer to another integer type of the same size as void* (such conversion conforms to the standard, no warnings) for the target architecture.
This solution to get rid of that warning is arguably a bad design that is nowadys solved by intptr_t and uintptr_t, but it is posible it is there for historical reasons: intptr_t and uintptr_t are available since C99 and Glib started its development earlier in 1998, so they found their own solution to the same problem. It seems that there were some tries to change it:
GLib depends on various parts of a valid C99 toolchain, so it's time to
use C99 integer types wherever possible, instead of doing configure-time
discovery like it's 1997.
no success however, it seems it never got in the main branch.
In short, as I see it, the original question has changed from why this code is better to why this warning is bad (and is it a good idea to silence it?). The later has been answered somewhere else, but this could also help:
Converting from pointer to integer or vice versa results in code that is not portable and may create unexpected pointers to invalid memory locations.
But, as I said above, this rule doesn't seem to qualify for a warning for issue number 1 above. Maybe someone else could shed some light on this topic.
My guess for the rationale behind this behaviour is that gcc decided to throw a warning whenever the original value is changed in some way, even if subtle. As gcc doc says (emphasis mine):
A cast from integer to pointer discards most-significant bits if the pointer representation is smaller than the integer type, extends according to the signedness of the integer type if the pointer representation is larger than the integer type, otherwise the bits are unchanged.
So, if sizes match there is no change on the bits (no extension, no truncation, no filling with zeros) and no warning is thrown.
Also, [u]intptr_t is just a typedef of the appropriate qualified integer: it is not justifiable to throw a warning when assigning [u]intptr_t to void* since it is indeed its purpose. If the rule applies to [u]intptr_t, it has to apply to typedefed integer types.
I think it is because this conversion is implementation-dependendent. It is better to use uintptr_t for this purpose, because it is of the size of pointer type in particular implementation.
As explained in Askmish's answer, the conversion from an integer type to a pointer is implementation defined (see e.g. N1570 6.3.2.3 Pointers §5 §6 and the footnote 67).
The conversion from a pointer to an integer is implementation defined too and if the result cannot be represented in the integer type, the behavior is undefined.
On most general purpose architectures, nowadays, sizeof(int) is less than sizeof(void *), so that even those lines
int n = 42;
void *p = (void *)n;
When compiled with clang or gcc would generate a warning (see e.g. here)
warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
Since C99, the header <stdint.h> introduces some optional fixed-sized types. A couple, in particular, should be used here n1570 7.20.1.4 Integer types capable of holding object pointers:
The following type designates a signed 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:
intptr_t
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:
uintptr_t
These types are optional.
So, while a long may be better than int, to avoid undefined behaviour the most portable (but still implementation defined) way is to use one of those types(1).
Gcc's documentation specifies how the conversion takes place.
4.7 Arrays and Pointers
The result of converting a pointer to an integer or vice versa (C90 6.3.4, C99 and C11 6.3.2.3).
A cast from pointer to integer discards most-significant bits if the pointer representation is larger than the integer type, sign-extends(2) if the pointer representation is smaller than the integer type, otherwise the bits are unchanged.
A cast from integer to pointer discards most-significant bits if the pointer representation is smaller than the integer type, extends according to the signedness of the integer type if the pointer representation is larger than the integer type, otherwise the bits are unchanged.
When casting from pointer to integer and back again, the resulting pointer must reference the same object as the original pointer, otherwise the behavior is undefined. That is, one may not use integer arithmetic to avoid the undefined behavior of pointer arithmetic as proscribed in C99 and C11 6.5.6/8.
[...]
(2) Future versions of GCC may zero-extend, or use a target-defined ptr_extend pattern. Do not rely on sign extension.
Others, well...
The conversions between different integer types (int and intptr_t in this case) are mentioned in n1570 6.3.1.3 Signed and unsigned integers
When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.
So, if we start from an int value and the implementation provides an intptr_t type and sizeof(int) <= sizeof(intptr_t) or INTPTR_MIN <= n && n <= INTPTR_MAX, we can safely convert it to an intptr_t and then convert it back.
That intptr_t can be converted to a void * and then converted back to the same (1)(2) intptr_t value.
The same doesn't hold in general for a direct conversion between an int and a void *, even if in the example provided, the value (42) is small enough not to cause undefined behaviour.
I personally find quite debatable the reasons given for those type conversion macros in the linked GLib documentation (emphasis mine)
Many times GLib, GTK+, and other libraries allow you to pass "user data" to a callback, in the form of a void pointer. From time to time you want to pass an integer instead of a pointer. You could allocate an integer [...] But this is inconvenient, and it's annoying to have to free the memory at some later time.
Pointers are always at least 32 bits in size (on all platforms GLib intends to support). Thus you can store at least 32-bit integer values in a pointer value.
I'll let the reader decide whether their approach makes more sense than a simple
#include <stdio.h>
void f(void *ptr)
{
int n = *(int *)ptr;
// ^ Yes, here you may "pay" the indirection
printf("%d\n", n);
}
int main(void)
{
int n = 42;
f((void *)&n);
}
(1) I'd like to quote a passage in this Steve Jessop's answer about those types
Take this to mean what it says. It doesn't say anything about size.
uintptr_t might be the same size as a void*. It might be larger. It could conceivably be smaller, although such a C++ implementation approaches perverse. For example on some hypothetical platform where void* is 32 bits, but only 24 bits of virtual address space are used, you could have a 24-bit uintptr_t which satisfies the requirement. I don't know why an implementation would do that, but the standard permits it.
(2) Actually, the standard explicitly mention the void* -> intptr_t/uintptr_t -> void* conversion, requiring those pointers to compare equal. It doesn't explicitly mandate that in the case intptr_t -> void* -> intptr_t the two integer values compare equal. It just mention in footnote 67 that "The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment.".
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.