Equivalent of uintptr_t/intptr_t for pointers to functions? - c

Afaik uintptr_t and intptr_t can be used to hold any pointer to void. Hence these types can be used to store pointers to data.
In C99 or later, are there similar signed and unsigned integer types capable of holding pointers to functions?

No, there are no such types.
Function pointers may only be reliably cast to other function pointer types (and then, only dereferenced while pointing to the correct function type).
The conversion of function pointers to integers in C is covered by 6.3.2.3/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.
Note that even if the integer type is large enough, casting to integer and back to function pointer is not guaranteed to retrieve the original function pointer.
In C++, the text is in [expr.reinterpret.cast] points 4 and 6. The behaviour is similar, but it explicitly guarantees that if an integer of sufficient size exists, then converting function pointer to integer and back again does retrieve the original function pointer.

Related

What is the appropriate arithmetic type for a pointer?

In my code I have two char pointer, one to a string and the other as an error indicator for strtoumax() and strtod(). I am currently using the type size_t (aka unsigned long) to calculate the difference between them. Is there any type designed to specifically match pointer type size on every machine? Or do I have to check it myself with macros?
For pointer difference use ptrdiff_t. If you're just trying to store a pointer as an integer, use uintptr_t (or intptr_t).
In my code I have two char pointer, one to a string and the other as
an error indicator for strtoumax() and strtod(). I am currently using
the type size_t (aka unsigned long) to calculate the difference
between them.
Don't do that. If you want a pointer difference then compute a pointer difference:
#include <stdint.h>
// ...
ptrdiff_t difference = p2 - p1;
And note ptrdiff_t, which is the type of the result of a pointer difference.
If you want a difference in bytes instead of in units the size of the pointed-to type (including if the pointed-to type is incomplete, such as void) then first convert to pointers to char:
ptrdiff_t difference_in_bytes = (char *) p2 - (char *) p1;
(char is the smallest addressible unit of storage, but technically, it might be larger than 8 bits on some C implementations. CHAR_BIT will help you figure that out if you're concerned about such cases.)
Do not compute a pointer difference by converting to integer and performing integer arithmetic, because although the behavior of that is defined (+/- signed integer overflow), the meaning of the result is not.
Is there any type designed to specifically match pointer
type size on every machine? Or do I have to check it myself with
macros?
Yes. In stdint.h there are definitions of uintptr_t and intptr_t, which can support round-trip pointer to integer to pointer conversions without data loss. But C does not define the meaning of the value resulting from converting a pointer to an integer, so these are best used as opaque types.
To store pointers as integers you can use intptr_t and uintptr_t declared in the header <stdint.h>.
From the C Standard (7.20.1.4 Integer types capable of holding object pointers)
1 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.
To store difference between two pointers you can use ptrdiff_t declared in the header <stddef.h>.
Pay attention to that you may calculate difference between two pointers if they both point to elements of the same array or one past the last element. Otherwise you will get undefined behavior.

Integer type for holding function pointers?

What standard-defined integer type should I use for holding pointer to functions? Is there a (void*)-like type for functions that can hold any functions?
It's very certain that it's not [u]intptr_t because the standard said explicitly it's for pointers to objects and the standard makes a clear distinction between pointer to objects and pointer to functions.
There is no specified type for an integer type that is sufficient to encode a function pointer.
Alternatives:
Change code to negate the need for that integer type. Rarely is such an integer type truly needed.
Use an array: unsigned char[sizeof( int (*)(int)) )] and int (*f)(int)) within union to allow some examination of the integer-ness of the pointer. Still there may be padding issues. Comes down to what code want to do with such an integer.
Use uintmax_t and hope it is sufficient. A _Static_assert(sizeof (uintmax_t) >= sizeof (int (*)(int)) ); is a reasonable precaution though not a guarantee of success.
The limiting spec
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
Note even [u]intptr_t for object pointer types are a guarantee either as they are optional types.
It's very certain that it's not [u]intptr_t
It's true. But the distinction is only made to guarantee the correctness of some essential conversions. If you look at the 2nd edition of "The C Programming Language", then the distinction is more subtle to notice than the current wording in the standard.
If you target platforms with operating systems, then [u]intptr_t is probably exactly what you want, as:
POSIX specifies dlsym function that returns void * with the requirement that result can be casted to function pointers and be usable.
Win32 GetProcAddress is defined in such way that, if the return values' not NULL, it'll be a valid memory address that's valid for functions and/or objects.

Is there any difference between uintptr_t and unsigned int when unsigned int can hold any address?

Description of uintptr_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
And since any pointer can be converted to void pointer and vice versa:
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.
Any pointer can be converted to uintptr_t and vice versa, OK.
Now, description of integers and pointers:
[Integer -> Pointer]
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
[Pointer -> Integer]
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.
OK. Now, since in my system's ABI (Procedure call standard for ARM architecture) both unsigned int and pointers have same size and alignment, and my system uses plain 32bit continuous values starting from 0x0 for memory addresses, it seems that the implementation-defined gap in the conversion of
Integer -> Pointer and Pointer -> Integer
has been filled in my system, and I can safely convert pointers to unsigned integers, and there is no difference between converting a pointer into uintptr_t and converting a pointer to unsigned int in my system (both will yield same value). Am I right with my assumption? or there is something I'm missing?
Even given that unsigned int has enough bits to represent all addresses in the C implementation, the C standard does not guarantee this means that, given a void * pointer p, the expression (void *) (unsigned) p == p evaluates to true. Because the conversion from void * to an integer is implementation-defined, it might do more than simply reproduce the address as an unsigned value. It might include some bits describing the provenance of the address or a checksum, and unsigned might be insufficient to contain the necessary information to restore the original value.
Most implementations are likely to simply convert the address in the obvious way, reinterpreting the bits of the virtual memory adddress as an unsigned value, and no problems will arise. However, this is a feature of the implementation; it is not a requirement of the C standard.

Casting malloc to a primitive type

How is this allowed in C?
int p= (int) malloc (sizeof(int));
I only get a warning when I compile in gcc.
warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
A void pointer cannot be casted to a primitive type right?
*((int*)(x))//is allowed - assume x is of type void*
But how is direct cast to primitive also allowed?
This is not forbidden, but implementation defined behaviour. Quoting C11, chapter §6.3.2.3
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.
That is why compiler emits the warning.
That said, quoting chapter 7.20.1.4, Integer types capable of holding object pointers, we have intptr_t and uintptr_t.
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
Having said that, just for sake of completeness's sake, let me add that, there is fair amount of reasons on why not to cast the return value of malloc() and family in C..

Difference between address of a variable and integer

Whats the difference between integer and the address returned by & ?
Therefore, why should I type-cast integer to an integer pointer when assigning it to an integer pointer?
Integer and pointer to integer are different types.
Integer variable holds integer value.
Pointer variable holds address value.
Given your example:
int value = 5;
int address = &value;
value is variable of type int.
&value returns address of type int*
address = &value tries to assign int* to int.
While integers can be converted to pointers and back, it doesn't mean that addresses are necessarily integer values.
Just like you can assign double to int, you can assign int * to int. But it's not necessarily what you want, and that's why compiler warns about it.
In fact, unless you are working very close to hardware (writing driver for example), it's very unlikely you need to do conversions between integers and pointers.
Correct way to do this, without needing any casts:
int * pointer = &value;
Whats the difference between integer and the address returned by & ?
I think this question has already been answered in the comments. The difference is the type of those variables. Basically int = Integer type and *int = Pointer type.
Furthermore: Conversion from pointer to int can result in undefined behavior.
Therefore, why should I type-cast integer to an integer pointer when
assigning it to an integer pointer?
Because the standard says:
C99 Section 6.5.4 and 6.5.16.1:
Conversions that involve pointers, other than where permitted by the
constraints of 6.5.16.1, shall be specified by means of an explicit
cast.
And C++ Section 5.4:
Any type conversion not mentioned below and not explicitly defined by
the user (12.3) is ill-formed.

Resources