C - Why cast to uintptr_t vs char* when doing pointer arithmetic - c

I am working on a programm where I have to modify the target process memory/ read it.
So far I am using void* for storing adresses and cast those to char* if I need to change them (add offset or modify in general)
I have heard of that type defined in stdint.h but I don't see the difference in using it for pointer arithmetic over the char* conversion (which seems more C89 friendly atleast to me)
So my question: What of those both methods should I use for pointer arithmetic? Should I even consider using uintptr_t over char* in any case?
EDIT 1
Basically I just need to know if this yields
0x00F00BAA hard coded memory adress in target
process
void* x = (void*)0x00F00BAA;
char* y = (void*)0x00F00BAA;
x = (uintptr_t)x + 0x123;
y = (char*)y + 0x123;
x == y?
x == (void*)0x00F00CCD?
y == (void*)0x00F00CCD?

In comments user R.. points out that the following is likely incorrect if the addresses the code is dealing with are not valid within the current process. I've asked the OP for clarification.
Do not use uintptr_t for pointer arithmetic if you care about the portability of your code. uintptr_t is an integer type. Any arithmetic operations on it are integer arithmetic, not pointer arithmetic.
If you have a void* value and you want to add a byte offset to it, casting to char* is the correct approach.
It's likely that arithmetic on uintptr_t values will work the same way as char* arithmetic, but it absolutely is not guaranteed. The only guarantee that the C standard provides is that you can convert a void* value to uintptr_t and back again, and the result will compare equal to the original pointer value.
And the standard doesn't guarantee that uintptr_t exists. If there is no integer type wide enough to hold a converted pointer value without loss of information, the implementation just won't define uintptr_t.
I've actually worked on systems (Cray vector machines) where arithmetic on uintptr_t wouldn't necessarily work. The hardware had 64-bit words, with a machine address containing the address of a word. The Unix-like OS needed to support 8-bit bytes, so byte pointers (void*, char*) contained a word address with a 3-bit offset stored in the otherwise unused high-order 3 bits of the 64-bit word. Pointer/integer conversions simply copied the representation. The result was that adding 1 to a char* pointer would cause it to point to the next byte (with the offset handled in software), but converting to uintptr_t and adding 1 would cause it to point to the next word.
Bottom line: If you need pointer arithmetic, use pointer arithmetic. That's what it's for.
(Incidentally, gcc has an extension that permits pointer arithmetic on void*. Don't use it in portable code. It also causes some odd side effects, like sizeof (void) == 1.)

Related

Subtraction between pointers of different type [duplicate]

This question already has answers here:
Pointer/Address difference [duplicate]
(3 answers)
Closed 6 years ago.
I'm trying to find the distance in memory between two variables. Specifically I need to find the distance between a char[] array and an int.
char data[5];
int a = 0;
printf("%p\n%p\n", &data[5], &a);
long int distance = &a - &data[5];
printf("%ld\n", distance);
When I run my my program without the last two lines I get the proper memory address of the two variables, something like this:
0x7fff5661aac7
0x7fff5661aacc
Now I understand, if I'm not wrong, that there are 5 bytes of distance between the two (0x7fff5661aac8, 0x7fff5661aac9, 0x7fff5661aaca, 0x7fff5661aacb, 0x7fff5661aacc).
Why I can't subtract a pointer of type (int *) and one of type (char *). Both refer to memory address.. What should I do in order to calculate the distance, in bytes, between the two?? I tried casting one of the two pointers but it's not working.
I get: "error: 'char *' and 'int *' are not pointers to compatible types". Thanks to everyone will help me
Nopes, this is not possible.
First, you can only subtract pointers of (to) "compatible" types, an int and a char are not compatible types here. Hence the subtraction is not possible.
That said, even if both are pointers to compatible type, then also, the following comes into picture.
So, secondly You cannot just subtract two arbitrary pointers, they need to be essentially part of (address for elements of) the same array. Othweise, it invokes undefined behavior.
Quoting C11, chapter §6.5.6, Additive operators
When two pointers are subtracted, both shall point to elements of the same array object,
or one past the last element of the array object; the result is the difference of the
subscripts of the two array elements. [....]
Thirdly, another important point, the result of subtraction of two pointers is of type ptrdiff_t, a signed integer type.
[...] The size of the result is implementation-defined,
and its type (a signed integer type) is ptrdiff_t defined in the <stddef.h> header. [...]
so, to print the result, you need to use %td format specifier.
Pointer subtraction is only defined for pointers within the same array (or just past the last element of an array). Any other use is undefined behavior. Let's ignore that for your experimentation.
When two pointers of the same type to elements of the same array object are subtracted, the result is the difference of the array indices. You could add that signed integer result (of type ptrdiff_t) to the first pointer and get the value of the second pointer, or subtract the result from the second pointer and get the value of the first pointer. So in effect, the result is the difference in the byte address of the two pointers divided by the size of the object being pointed to. This is why it makes no sense to allow subtraction of pointers of incompatible type, particularly when the referenced object types are of different size. How could you divide the difference in byte address by the size of the object being pointed to when the pointers being subtractedare referring to differently sized objects?
Still, for experimentation purposes, you can cast both pointers (pointing to different objects) to char * and subtract them, and many compilers will just give you the difference in their byte address as a number. However, the result could overflow an integer of ptrdiff_t. Alternatively, you can convert both pointers to an integer of type intptr_t and subtract the integers to get the difference in byte address. Again, it's theoretically possible that the result of the subtraction could overflow an integer of type intptr_t.
On a standard PC nothing keeps you from casting both pointers to an integer type which can hold the pointer value, and subtracting the two integers.
Such an integer type is not guaranteed to exist on all architectures (but on many common systems it does) — imagine segmented memory with more information than just a single number. If the integer type doesn't fit, the behavior of the cast is undefined.
From the standard draft n1570, 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.
Often the difference between addresses will be what one expects (variables declared in succession are next to each other in memory) and can be used to tell the direction the stack grows etc.
It may be interesting to explore what else you can do with integers and pointers.
Olaf commented that if you "cast [an arithmetic computation result] back to a pointer, you invoke UB." That is not necessarily so; it depends on the integer value. The standard draft says the following in 6.3.2.3/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
(Emphasis by me.) If we compute the address of a struct member by adding an offset to the struct's address we have obviously taken care of the mentioned issues, so it's up to the implementation. It is certainly not UB; a good many embedded systems would fail if we couldn't use an integer -> pointer conversion, and access that memory through the resulting pointer. We must make sure that the system allows it, and that the addresses are sound.
The paragraph has a footnote:
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.
That is, they are meant to not surprise the user. While in theory the addresses of unrelated objects adjacent in memory could be projected to wildly different integer values, they are not supposed to. A user can for example reasonably expect that linear memory is projected into a linear integer number space, keeping ordering and distances.
I should also emphasize (as I did in one comment) that the standard is not the world. It must accommodate and give guarantees for a wide range of machines. Therefore the standard can only be the smallest common denominator. If we can narrow the range of architectures we consider, we can make much better guarantees.
One common example is the possible presence of trap values in integer registers, or flags indicating a read from an uninitialized register, which also traps; these are responsible for a wide range of UB cases in the standard which simply do not apply, for example, to your PC.
uint8_t * ptr = ...;
uint8_t * ptr2 = ptr + 5;
Now if ptr was 100, what will ptr2 be? Correct, it will be 105. But now look at that code:
uint32_t * ptr = ...;
uint32_t * ptr2 = ptr + 5;
Again, if ptr was 100, what will ptr2 be? Wrong! It won't be 105, it will be 120.
Why? Pointer arithmetic is not integer arithmetic!
ptr2 = ptr + 5;
Actually means:
ptr2 = int_to_ptr(ptr_to_int(ptr) + (sizeof(ptr) * 5));
Functions int_to_ptr and ptr_to_int don't really exist, I'm just using them for demonstration purpose, so you better understand what is going on between the scenes.
So if you subtract two pointers, the result is not the difference of their addresses, it's the number of elements between them:
uint32_t test[50];
ptrdiff_t diff = &test[20] - &test[10];
diff will be 10, as there are 10 elements in between them (one element is one uint32_t value) but that doesn't mean there are 10 bytes between test[10] and test[20], there are 40 bytes between them as every uint32_t value takes up 4 bytes of memory.
Now you may understand why subtracting pointers of different types makes no sense, as different types have different element sizes and what shall such a subtraction then return?
If you want how many bytes are in between two pointers, you need to cast them both to a data type that has one-byte elements (e.g. uint8_t * or char * would work) or cast them to void * (GNU extension but many compilers support that as well), which means the data type is unknown and thus the element size is unknown as well and in that case the compiler will byte-sized elements. So this may work:
ptrdiff_t diff = (void *)ptr2 - (void *)ptr1;
yet this
ptrdiff_t diff = (char *)ptr2 - (char *)ptr1;
is more portable.
It will compile, it will deliver an result. If that result is meaningful, that's a different topic. Unless both pointers point to the same memory "object" (same struct, same array, same allocated memory region), it is not, as the standard says that in that case, the result is undefined. That means diff could (legally) have any value, so a compiler may as well always set diff to 0 in that case, that would be allowed by the standards.
If you want defined behavior, try this instead:
ptrdiff_t diff = (ptrdiff_t)ptr2 - (ptrdiff_t)ptr1;
That is legal and defined. Every pointer can be casted to an int value and ptrdiff_t is an int value, one that is guaranteed to big enough so that every pointer can fit into it (don't ever use int or long for that purpose, they do not make any such guarantee!). This code converts both pointers to integers and then subtract them. I still don't see anything useful you can do with diff now, but that code at least will deliver a defined result, yet maybe not the result you might be expecting.
Try typecasting each address to void *
long int distance = (void *)&a - (void *)&data[5];
As others will point out, this is dangerous and undefined, but if you're just exploring how memory works, it should be fine.
The size of int and size of char pointers are different.In a system where int size is 4 bytes if you will do int_pointer++ it will increase address by 4 bytes while it will increment address by 1 byte in case of char_ptr. Hence you might be getting error.
This is because pointer arithmetic is about offsets.
For example, if you have an array and a pointer to that array like:
int array[3] = { 1, 2, 3};
int *ptr = array;
and then you increment ptr, you expect next value from the array, e.g. array[0] after array[1], no matter what type is stored in that. So when you substract pointers you don't get e.g. bytes, but offset.
Don't substract pointers that are not the part of the same array.

De-referencing pointer to a volatile int after increment

unsigned int addr = 0x1000;
int temp = *((volatile int *) addr + 3);
Does it treat the incremented pointer (ie addr + 3 * sizeof(int)), as a pointer to volatile int (while dereferencing). In other words can I expect the hardware updated contents of say (0x1012) in temp ?
Yes.
Pointer arithmetic does not affect the type of the pointer, including any type qualifiers. Given an expression of the form A + B, if A has the type qualified pointer to T and B is an integral type, the expression A + B will also be a qualified pointer to T -- same type, same qualifiers.
From 6.5.6.8 of the C spec (draft n1570):
When an expression that has integer type is added to or subtracted from a pointer, the
result has the type of the pointer operand.
Presuming addr is either an integer (variable or constant) with a value your implementation can safely convert to an int * (see below).
Consider
volatile int a[4] = [ 1,2,3,4};
int i = a[3];
This is exactly the same, except for the explicit conversion integer to volatile int * (a pointer to ...). For the index operator, the name of the array decays to a pointer to the first element of a. This is volatile int * (type qualifiers in C apply to the elements of an array, never the array itself).
This is the same as the cast. Leaves 2 differences:
The conversion integer to "pointer". This is implementation defined, thus if your compiler supports it correctly (it should document this), and the value is correct, it is fine.
Finally the access. The underlying object is not volatile, but the pointer/resp. access. This actually is a defect in the standard (see DR476 which requires the object to be volatile, not the access. This is in contrast to the documented intention (read the link) and C++ semantics (which should be identical). Luckily all(most all) implementations generate code as one would expect and perform the access as intended. Note this is a common ideom on embedded systems.
So if the prerequisites are fulfilled, the code is correct. But please see below for better(in terms of maintainability and safety) options.
Notes: A better approach would be to
use uintptr_t to guarantee the integer can hold a pointer, or - better -
#define ARRAY_ADDR ((volatile int *)0x1000)
The latter avoids accidental modification to the integer and states the implications clear. It also can be used easier. It is a typical construct in low-level peripheral register definitions.
Re. your incrementing: addr is not a pointer! Thus you increment an integer, not a pointer. Left apart this is more to type than using a true pointer, it also is error-prone and obfuscates your code. If you need a pointer, use a pointer:
int *p = ARRAY_ADDR + 3;
As a personal note: Everybody passing such code (the one with the integer addr) in a company with at least some quality standards would have a very serious talk with her team leader.
First note that conversions from integers to pointers are not necessarily safe. It is implementation-defined what will happen. In some cases such conversions can even invoke undefined behavior, in case the integer value cannot be represented as a pointer, or in case the pointer ends up with a misaligned address.
It is safer to use the integer type uintptr_t to store pointers and addresses, as it is guaranteed to be able to store a pointer for the given system.
Given that your compiler implements a safe conversion for this code (for example, most embedded systems compilers do), then the code will indeed behave as you expect.
Pointer arithmetic will be done on a type that is volatile int, and therefore + 3 means increase the address by sizeof(volatile int) * 3 bytes. If an int is 4 bytes on your system, you will end up reading the contents of address 0x100C. Not sure where you got 0x1012 from, mixing up decimal and hex notation?

Casting pointers?

What will happen If we cast 32 bit pointer to 8 bit?
I assume that for example we have 0x8000 0000 and if we cast to 8 bit, the value of the new pointer will be 0x00. Am I right?
A pointer is a pointer, depend on platform. On a 32 bits CPU a pointer is always 32 bits.
void *temp = 0x8000000;
uint8_t *temp = 0x8000000;
uint16_t *temp = 0x8000000;
If you cast the pointer you change the pointed value size.
void *temp = 0x80000000;
uint8_t temp2 = *((char *)(temp)); // return a single char (8 bits) at 0x80000000
uint16_t temp3 = *((short *)(temp)); // return a single short (16 bits) at 0x80000000
On a x86 arch CPU a pointer is just a integer pointing to a memory area.
What you are really asking, i think, and hope, is if is possible to have a pointer to an integer, and then cast it to pointer to a char.
You can look inside the bits of an integer by creating a pointer to it and cast it to a narrower type, such as char and then increment it.
Basically, if you would increment a char pointer, you will move up only one byte inside the integer.
Well, there are actually architectures which have different pointers of different size. A classical x86 would be to have near and far pointers. The former had 16 bits and was restricted to a certain memory area. Any usage outside this area would have been undefined. The far pointer consisted of two 16 bit values (segment:offset) in a singel 32 bit variable. The segment part spcified the memory region the pointer was valid for, while the offset was actually identical to a near pointer.
However, that was (and is) not part of the C standard, but architecture-specific extensions. As these might have been accepted as crucial for the platform, it was supported by all toolchains available, but still nothing standard.
For your pointers, you should - in general - never fiddle with a pointer, expecially never cast it to a type which cannot hold all bits required for the pointer. If you have to store a pointer in an integer variable for some reason, use uintptr_t or intptr_t which are defined in stdint.h as of C99 standard. However, they are optional, so your toolchain might not support them (gcc for instances does; not idea if Microsoft already had the time to add this header to VS).
Remember that the value stored in uintptr_t (for example) might not be the same as a direct cast to another integer type (but for ARM this is the case as I know myself).
If you are asking, however, how to compress a pointer value, you might first cast it to unitptr_t and then compress the integer value using one of the well-known algorithms (Huffman, etc.).

Cast int to pointer - why cast to long first? (as in p = (void*) 42; )

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 type casting do in C?

I have a question about type casting in C:
void *buffer;
(int *)((int)buffer);
what is this type casting doing? and what does the ((int)buffer) doing?
Imagine you are on a Linux/x86-64 computer like mine. Then pointers are 64 bits and int is 32 bits wide.
So, the buffer variable has been initialized to some location; Perhaps 0x7fff4ec52020 (which could be the address of some local variable, perhaps inside main).
The cast (int)buffer gives you an int, probably the least significant 32 bits, i.e. 0x4ec52020
You are casting again with (int*)((int)buffer), which gives you the bogus address 0x000000004ec52020 which does not point into valid memory. If you dereference that bogus pointer, you'll very probably get a SIGSEGV.
So on some machines (notably mine) (int *)((int)buffer) is not the same as (int*)buffer;
Fortunately, as a statement, (int *)((int)buffer); has no visible side effect and will be optimized (by "removing" it) by the compiler (if you ask it to optimize).
So such code is a huge mistake (could become an undefined behavior, e.g. if you dereference that pointer). If the original coder really intended the weird semantics described here, he should add a comment (and such code is unportable)!
Perhaps #include-ing <stdint.h> and using intptr_t or uintptr_t could make more sense.
Let's see what the C Standard has to say. On page 55 of the last freely published version of the C11 standard http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf we have
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.
What does this mean in your example? Section 6 says that the cast (int)buffer will compile, but if the integer is not big enough to hold the pointer (which is likely on 64-bit machines), the result is undefined. The final (int*) casts back to a pointer.
Section 5 says that if the integer was big enough to hold the intermediate result, the result is exactly the same as just casting to (int*) from the outset.
In short the cast (int) is at best useless and at worst causes the pointer value to be lost.
In straight C code this is pointless because conversions to and from void* are implicit. The following would compile just fine
int* p = buffer;
Worse though is this code potentially introduces errors. Consider the case of a 64 bit platform. The conversion to int will truncate the pointer to 32 bits and then assign it to an int*. This will cause the pointer value to be truncated and certainly lead to errors.

Resources