Casting a pointer to an int - c

I am writing my own functions for malloc and free in C for an assignment. I need to take advantage of the C sbrk() wrapper function. From what I understand sbrk() increments the program's data space by the number of bytes passed as an argument and points to the location of the program break.
If I have the following code snippet:
#define BLOCK_SIZE 20
int x;
x = (int)sbrk(BLOCK_SIZE + 4);
I get the compiler error warning: cast from pointer to integer of different size. Why is this and is there anyway I can cast the address pointed to by sbrk() to an int?

I get the compiler error warning: cast from pointer to integer of different size.
Why is this
Because pointer and int may have different length, for example, on 64-bit system, sizeof(void *) (i.e. length of pointer) usually is 8, but sizeof(int) usually is 4. In this case, if you cast a pointer to an int and cast it back, you will get a invalid pointer instead of the original pointer.
and is there anyway I can cast the address pointed to by sbrk() to an int?
If you really need to cast a pointer to an integer, you should cast it to an intptr_t or uintptr_t, from <stdint.h>.
From <stdint.h>(P):
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 a 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 a pointer to void, and the result will compare equal to the original pointer: uintptr_t
On XSI-conformant systems, the intptr_t and uintptr_t types are required; otherwise, they are optional.

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.

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..

Can you ever assume typecasting pointers is safe?

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.

What is intptr_t,is it a type for integer or pointer?

It's defined in /usr/include/stdint.h:
typedef long int intptr_t;
is it supposed to be a type for integer or pointer?
It is a signed integer type that is big enough to hold a pointer.
It is a signed integer type that guaranteed to can hold a void* type.
And why there is also [u]intptr_t? Because:
Any valid pointer to void can be converted to intptr_t or uintptr_t
and back with no change in value. The C Standard
guarantees that a pointer to void may be converted to or from a
pointer to any object type and back again and that the result must
compare equal to the original pointer. Consequently, converting
directly from a char * pointer to a uintptr_t is allowed on implementations that support the uintptr_t.

Resources