Converting any pointer type to any other pointer type is not supported by base C (that is, C without any extensions or behavior not required by the C standard). The 2018 C standard says in clause 6.3.2.3, paragraph 7:
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer…
In that passage, we see two limitations:
- If the pointer is not properly aligned, the conversion may fail in various ways. In your example, converting an
int *
to a short *
is unlikely to fail since int
typically has stricter alignment than short
. However, the reverse conversion is not supported by base C. Say you define an array with short x[20];
or char x[20];
. Then the array will be aligned as needed for a short
or char
, but not necessarily as needed for an int
, in which case the behavior of (int *) x
would not be defined by the C standard.
- The value that results from the conversion mostly unspecified. This passage only guarantees that converting it back yields the original pointer (or something equivalent). It does not guarantee you can do anything useful with the pointer without converting it back—you cannot necessarily use a pointer converted from
int *
to access a short
.
The standard does make some additional guarantees about certain pointer conversions. One of them is in the continuation of the passage above:
… When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
So you can use a pointer converted from int *
to access the individual bytes that represent an int
, and you can do the same to access the bytes of any other object type. But that guarantee is made only for access the individual bytes with a character type, not with a short
type.
From the above, we know that after the short * c = (short *)y;
in your example, y
does not necessarily point to any part of the x
it originated from—the value resulting from the pointer conversion is not guaranteed to work as a short *
at all. But, even if it does point to the place where x
is, base C does not support using c
to access those bytes, because 6.5 7 says:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
So the *c = 0;
in your example is not supported by C for two reasons: c
does not necessarily point to any part of x
or to any valid address, and, even if it does, the behavior of modifying part of the int
x
using short
type is not defined by the C standard. It might appear to work in your C implementation, and it might even be supported by your C implementation, but it is not strictly conforming C code.
The C standard provides the void *
type for use when a specific type is inadequate. 6.3.2.3 1 makes a similar guarantee for pointers to void
as it does for pointers to objects:
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.
void *
is used with routines that must work with arbitrary object types, such as qsort
. char *
could serve this purpose, but it is better to have a separate type that clearly denotes no specific type is associated with it. For example, if the parameter to a function were char *p
, the function could inadvertently use *p
and get a character that it does not want. If the parameter is void *p
, then the function must convert the pointer to a specific type before using it to access an object. Thus having a special type for “generic pointers” can help avoid errors as well as indicate intent to people reading the code.