Consider the following code:
*((unsigned int*)((unsigned char *)0x400000));
Does it violate strict aliasing?
From the strict aliasing rule:
An object shall have its stored value accessed only by an lvalue
expression that has one of the following types:
...
For a violation to occur, there must be an object of type unsigned char, so when accessed with unsigned int* a violation will occur.
Now, does (unsigned char *)0x400000 constitute an unsigned char object at address 0x400000? if not, than there is actually no object with stored value of type unsigned char here, so the access to it with a unsigned int does not violate the rule.
Note the following phrase about object:
Object
region of data storage in the execution environment, the
contents of which can represent values
NOTE When referenced, an object may be interpreted as having a
particular type; see 6.3.2.1.
So, since (unsigned char *)0x400000 does not constitute an unsigned char object reference (to my understanding) there is no object of type unsigned char in the presented code, so it seems that there is no violation.
Am I correct?
With respect to #Antti Haapala answer:
If we assume that integer to pointer conversion of both converting 0x400000 to unsigned char* and to unsigned int* has on my system a defined behavior with no trap representation and well aligned, and that is in order to fill the implementation-defined gap from the standard:
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
Will that change the answer to my question?
Essentially, strict aliasing isn't applicable when dealing with hardware registers, since the committee apparently never considered hardware-related programming scenarios or memory-mapped registers.
Strict aliasing only applies in scenarios where the compiler is able to determine an effective type of what's actually stored in memory. If you take a hardware address and lvalue access it, the contents there can never have an effective type that the compiler is aware of. This means that when reading the data, I suppose this part from 6.5 would apply:
For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access.
In your case unsigned int. Please note that the middle cast to unsigned char* is completely superfluous both in terms of strict aliasing and in terms of lvalue access to the hardware. It should be removed.
If you do a write access however, the compiler is free to treat the value at that address as the effective type used through the write access:
If a value is stored into an object having no declared type through an
lvalue having a type that is not a character type, then the type of the lvalue becomes the
effective type of the object for that access and for subsequent accesses that do not modify
the stored value.
Basically the rules of effective type and strict aliasing don't make sense when dealing with hardware addresses, so the compiler can't do much with them. In addition, such accesses often don't make any sense unless the pointer is volatile qualified, preventing any attempts of optimization based on strict aliasing.
Just to be sure, always compile with -fno-strict-aliasing when doing any form of embedded systems programming.
Undecided. The standard does not even tell what happens if you convert an integer to a pointer, except that the result is implementation-defined with several possible behaviours that might be undefined...
What is certain though is that *(unsigned int*)(unsigned char *)0x400000; and *(unsigned int*)0x400000 do not differ in undefinedness, as merely pointing to an object using a certain value does not change its effective type.
Another question is what is the type of the object at 0x400000 and what is the value - if you never set it, the contents are indeterminate and it might not have an effective type. Reading it will have undefined behaviour.
What is certain though is that if you write an object of type float at that address and considering that it is a valid access, the effective type of the object at address 0x400000 will be float. If you then read the object at that address with an lvalue of type unsigned int it will be a strict aliasing violation.
The standard does not take stances on any of these. You're on your own. Check your compiler manuals. As soon as you convert an integer to a pointer and start poking at memory there you're not strictly conforming and you don't find any backing in the standard, period.
The Standard only tries to define the behavior of portable programs. It consequently leaves questions about whether or how to support non-portable programs to the judgment of compiler writers, who were expected to know more about their customers' needs than the Committee ever could.
There are no circumstances in which the Standard would require implementations to guarantee anything meaningful about the effects of casting an integer of unknowable provenance to a pointer and then using that pointer to access storage. The ability to meaningfully process such code would be a Quality of Implementation issue regardless of the types used for access. If the pointers are not qualified volatile, operations involving them may get optimized out regardless of the types involved; if they are volatile, operations will be sequenced with respect to other volatile accesses, again regardless of the types.
While some compilers may allow for the possibility of interaction between a volatile object and a non-qualified object of the same type, without doing so for other interactions between volatile objects and unqualified ones, compilers that seek maximum compatibility with low-level code will allow for interaction between volatile accesses and all objects of all types, while those that don't may not always accommodate interactions between objects they regard as unrelated, even if they happen to have the same type and same address.
Related
Convert a integer address to double pointer and the read it, but the size of integer is less than double type, and the read operation will read exceed the object size. I believe that this is undefined behavior but I didn't find the description in C standard, so I post this question to seek for an answer to confirm my point.
#include <stdio.h>
#include <stdint.h>
int main() {
int32_t a = 12;
double *p = (double*)(&a);
printf("%lf\n", *p);
return 0;
}
It's undefined behavior per C11 6.5 ("the strict aliasing rule"):
6 The effective type of an object for an access to its stored value is the declared type of the object, if any.
...
In this case the effective type is int32_t (which is a typedef corresponding to something like int or long).
7 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,
...
double is not compatible with int32_t, so when the code access the data here: *p, it violates this rule and invokes UB.
See What is the strict aliasing rule? for details.
From C99 Committee Draft 6.5 Expressions point 7:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:76)
— 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,amember of a subaggregate or contained union), or
— a character type.
Object of type int has it's address accessed using lvalue expression of double type. int and double types are not compatible anyhow, they are not aggregate and double is not a character type. Dereferencing a pointer (an lvalue expression) of a type double that points to an object with type int is undefined behavior. Such operations are called strict aliasing violation.
The Standard does not require that compilers behave predictably if an object of type "int" is accessed using an lvalue that has no visible relation to that type. In the rationale, however, the authors note that the classification of certain actions as Undefined Behavior is intended to allow the marketplace to decide what behaviors are considered necessary in quality implementations. In general, the act of converting a pointer to another type and then immediately performing an access with it falls in the category of actions which will be supported by quality compilers that are configured to be suitable for system programming, but may not be supported by compilers that act obtusely.
Even ignoring the lvalue-type issue, however, the Standard imposes no requirements as to what happens if an application tries to read from memory it does not own. Here again, the choice of behavior may sometimes be a quality-of-implementation issue. There are five main possibilities here:
On some implementations, the contents of the storage might be
predictable via means not described by the Standard, and the read
would yield the contents of such storage.
The act of reading might behave as though it yields bits with
Unspecified values, but have no other side-effect.
The attempted read may terminate the program.
On platforms which use memory-mapped I/O, the out-of-bounds read could
perform an unexpected operation with unknown consequences; this
possibility is only applicable on certain platforms.
Implementations that try to be "clever" in various ways may try to
draw inferences based on the notion that the read cannot occur, thus
resulting in side-effects that transcend laws of time and causality.
If you know that your code will be running on a platform where reads have
no side-effects, the implementation won't try to be "clever", and your code
is prepared for any pattern of bits the read might yield, then under those
circumstances such a read may have useful behavior, but you would be limiting
the situations where your code could be used.
Note that while implementations that define __STDC_ANALYZABLE__ are required
to have most actions obey the laws of time and causality even in cases where
the Standard would impose no other requirements, out-of-bounds reads are
classified as Critical Undefined Behavior, and should thus be considered
dangerous on any implementation that does not expressly specify otherwise.
Incidentally, there's another issue on some platforms which would apply even if e.g. code had used an int[3] rather than a single int: alignment. On some platforms, values of certain types may only be read or written to/from certain addresses, and some addresses which are suitable for smaller types may not be suitable for larger ones. On platforms where int requires 32-bit alignment but double requires 64-bit alignment, given int foo[3], a compiler might arbitrarily place foo so that (double*)foo would be a suitable address for storing a double, or so that (double*)(foo+1) would be a suitable place. A programmer who is familiar with the details of an implementation may be able to determine which address would be valid and exploit that, but code which blindly assumes that the address of foo will be valid may fail if double has a 64-bit alignment requirement.
Consider this code in block scope:
struct foo { unsigned char a; unsigned char b; } x, y;
x.a = 0;
y = x;
C [N1570] 6.3.2.1 2 says “If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.”
Although a member of x has been assigned a value, no assignment to x has been performed, and its address has not been taken. Thus, it appears 6.3.2.1 2 tells us the behavior of x in y = x is undefined.
However, if we had assigned a value to every member of x, it would seem unreasonable to consider x to be uninitialized for the purposes of 6.3.2.1 2.
(1) Is there anything in the standard which, strictly speaking, causes 6.3.2.1 2 not to apply to (make undefined) the code above?
(2) Supposing we were modifying the standard or determining a reasonable modification to 6.3.2.1 2, are there reasons to prefer one of the following over the others? (a) 6.3.2.1 2 does not apply to structures. (b) If at least one member of a structure has been assigned a value, the structure is not uninitialized for purposes of 6.3.2.1 2. (c) If all named1 members of a structure have been assigned a value, the structure is not uninitialized for purposes of 6.3.2.1 2.
Footnote
1 Structures may have unnamed members, so it is not always possible to assign a value to every member of a structure. (Unnamed members have indeterminate value even if the structure is initialized, per 6.7.9 9.)
My opinion is that it is undefined behaviour simply because it is not explicitly defined by the standard. From 4 Conformance §2 (emphasize mine) :
...Undefined behavior is otherwise
indicated in this International Standard by the words ‘‘undefined behavior’’ or by the
omission of any explicit definition of behavior.
After many reads in N1570 draft I cannot find any explicit definition of behaviour for using a partially initialized struct. On one hand 6.3.2.1 §2 says:
...If
the lvalue designates an object of automatic storage duration that could have been
declared with the register storage class (never had its address taken), and that object
is uninitialized (not declared with an initializer and no assignment to it has been
performed prior to use), the behavior is undefined
so here x is automatic, has never be initialized (only one of its members), and admitedly its address is never taken so we could think that it is explicitely UB
On the other hand, 6.2.6.1 §6 says:
... The value of a structure or union object is never a trap representation, even though the value of a member of the structure or union object may be a trap representation.
As 6.2.6.1 §5 has just defined a trap representation:
Certain object representations need not represent a value of the object type. If the stored
value of an object has such a representation and is read by an lvalue expression that does
not have character type, the behavior is undefined. If such a representation is produced
by a side effect that modifies all or any part of the object by an lvalue expression that means 0 value for a member and an undefined value for b member.
does not have character type, the behavior is undefined.50) Such a representation is called
a trap representation.
we could think that it is always legal to take the value of a struct because it cannot be a trap representation
In addition, it is not clear for me if setting the value of a member of a struct actually leaves the struct in an unitialized state.
For all those reasons, I think that the standard does not clearly defines what the behaviour should be and simply for that reason it is undefined behaviour.
That being said I am pretty sure that any common compiler will accept it and will give y the current representation of x, that means 0 value for a member and an indeterminate value of same representation as the current one for x.b for the b member.
Firstly, let's note that the quoted part of 6.3.2.1/2, the so-called "Itanium clause" is the only clause under which this code might have a problem. In other words, if this clause were not present, the code is fine. Structs may not have trap representations, so y = x; is otherwise OK even if x is entirely uninitialized. The resolution of DR 451 clarifies that indeterminate values may be propagated by assignment, without causing UB.
Back to the Itanium clause here. As you point out, the Standard does not clearly specify whether x.a = 0; negates the precondition "x is uninitialized".
IMO, this means we should turn to the rationale for the Itanium clause to determine the intent. The purpose of the wording of the standard document, in general, is to implement an intent; generally speaking, I don't agree with being dogmatic about minute detail of the standard: taking shades of meaning out of the wording that were not intended by those who created the wording.
This Q/A gives a good explanation of the rationale. The potential problem is that x might be stored in a register with the NaT bit set, and then y = x will cause a hardware exception due to reading a register that has that bit set.
So the question is: On IA64 does x.a = 0; clear the NaT bit? I don't know and I guess we would need someone familar with that platform to give a conclusive answer here.
Naively, I imagine that if x is in a register then, in general, x.a = 0; will need to read the old value, and apply a mask to clear the bits for a, thereby triggering the exception if x was NaT. However, x.a = 0; cannot trigger UB, so that logic must be incorrect. Perhaps IA64 compilers never store a struct in a register, or perhaps they clear the NaT bit on declaration of one, or perhaps there's a hardware instruction to implement x.a = 0; on a previously-NaT register, I don't know.
Copying a partially-written structure falls in the category of actions which quality implementations will process in consistent fashion absent a good reason to do otherwise, specialized implementations might process differently because they have a good reason to do so, and poor-quality-but-conforming implementations may use as an excuse to behave nonsensically.
Note that copying uninitialized values of an automatic-duration or malloc-created character array would fall in a similar category of actions, except that implementations that would trap on such an action (e.g. to help programmers identify and track down potential information leaks) would not be allowed to describe themselves as "conforming".
An implementation which is specialized to diagnose accidental information leaks might sensibly trap efforts to copy a partially-written structure. On an implementation where using an unitialized value of some type could result in strange behavior, copying a structure with an unitialized member of that type and then attempting to use that member of the copy might sensibly do likewise.
The Standard doesn't particularly say whether a partially-written structure counts as having been written or not, because people seeking to produce quality implementations shouldn't care. Quality implementations specialized for detecting potential information leakage should squawk at any attempt to copy uninitialized data, without regard for when the Standard would or would not allow such behavior (provided that they describe themselves as non-conforming). Quality general-purpose implementations designed to support a wide variety of programs should allow partially-initialized structures to be copied in cases where programs don't look at the uninitialized portions outside the context of whole-structure copying (such treatment is useful and generally costs nothing in non-contrived cases). The Standard could be construed as granting poor-quality-but-conforming implementations the right treat copying of partially-written structures as an excuse to behave nonsensically, but such implementations could use almost anything as such an excuse. Quality implementations won't do anything unusual when copying structures unless they document a good reason for doing so.
The C Standard specifies that structure types cannot have trap representations, although members of structs may. The primary circumstance in which that guarantee would be useful would be in cases involving partially-written structures. Further, a prohibition on copying structures before one had written all members, even ones the recipient of the copy would never use, would require programmers to write needlessly-inefficient code and serve no useful purpose. Imposing such a requirement in the name of "optimization" would be downright dumb, and I know of no evidence that the authors of the Standard intended to do so.
Unfortunately, the authors of the Standard use the same terminology to describe two situations:
Some implementations define the behavior of some action X in all cases, while some only define it for some; other parts of the Standard define the action in a few select cases. The authors want to say that implementations need not behave like the ones that define the behavior in all cases, without revoking guarantees made elsewhere in the Standard
Although other parts of the Standard would define the behavior of action X in some cases, guaranteeing the behavior in all such cases could be expensive and implementations are not required to guarantee them even cases where other parts of the Standard would define them.
Before the Standard was written, some implementations would zero-initialize all automatic variables. Thus, those implementations would guarantee the behavior of reading uninitialized values, even of types with trap representations. The authors of the Standard wished to make clear that they did not want to require all implementations do likewise. Further, some objects may define the behavior of all bit patterns when stored in memory, but not when stored in registers. Such treatment would generally be limtied to scalar types, however, rather than structures.
From a practical perspective, defining the behavior of copying a structure as copying the state (defined or indeterminate) of all fields would not cost any more than allowing compilers to behave in arbitrary fashion when copying partially-written structures. Unfortunately, some compiler writers erroneously believe that "cleverness" and "stupidity" are antonyms, and thus behave as though the authors of the Standard wished to invite compilers to assume that programs will never receive any input that would cause structures to be copied after having been partially written.
An answer to "C: When is casting between pointer types not undefined behavior?" indicates that casting forth and back via a pointer with no stricter alignment requirement is safe.
The alignment is important here even if the types are incomplete as the standard states:
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. 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.
Now the question is what correct alignment requirement of struct X is. Of course it would depend on it's contents if it is complete, but what if it's an incomplete (that it is only declared as struct X;)? Or put another way, can we cast from struct X* to struct Y* and back and get the same pointer again?
If the alignment requirement of a complete struct X and incomplete differs would that be a complication? I can't come up with an actual conversion between them, but it could be that you pass a struct X* between translation between context where the struct X is complete respectively incomplete. Or is this a special case that's always allowed anyway?
To start with, I don't think you can talk about the alignment requirement of an incomplete type, because alignment requirements are only defined for complete types:
Complete object types have alignment requirements which place restrictions on the addresses at which objects of that type may be allocated. (§6.2.8/1; all standard citations are taken from n1570.pdf, which is effectively C11)
But in a valid program, a pointer must point to an object of complete type (or NULL). So even if the referenced type of a pointer is incomplete in some translation unit, the object referred to by that pointer (if there is one) must have a complete type and thus an alignment requirement. However, in the translation unit in which the referred type is incomplete, there is no way to know what that alignment requirement is, neither for the compiler nor for the person writing the code.
So the only way a pointer to an incomplete type can be legitimately assigned a non-NULL value is by means of a copy of a pointer to the same type from somewhere where the type is complete (perhaps in another translation unit). This is not a conversion, since the two types are compatible, and it is guaranteed to work. Similarly, one of the few legitimate uses for a value of a pointer to an incomplete type is to pass it to a pointer to the same type in a context where the type is complete. (Other uses include comparing it for equality with another pointer value or converting it to an integer and printing it out, but these don't involve converting to a different pointer type.)
So, in summary, pointers to incomplete types are useful and usable precisely in the expected use case -- where the referenced type is opaque -- but cannot be portably converted to pointers of any other type except for possibly qualified char* and void*.
The standard does not guarantee the possibility of converting a pointer to a pointer to a type whose alignment might be more stringent. If the alignment of the referred type is unknown, the only pointers whose target alignment cannot be more stringent are char* and void*. So any conversion of a pointer to an incomplete type to a type other than char* or void* must be regarded as unportable.
Really, the fact that the referred type is incomplete is not hugely relevant. The standard does not specify the alignment of a composite type. Such an alignment must be sufficient to allow the compiler to correctly align members, but it could be arbitrarily large. In other words, there is no guarantee that the types:
typedef char oneChar;
struct oneChar_s { char x; };
union oneChar_u { char x; };
have the same alignment. It is quite possible for the two composite types to have larger alignments than 1. So there is no guarantee that it is possible to convert a oneChar* to a oneChar_s* (unless, of course, the oneChar* were the result of a previous conversion in the opposite direction), and a portable program would not try. In this sense, it makes no difference whether the definition of struct oneChar_s is visible or not.
Not coincidentally, the standard does not guarantee that all object pointers have the same size. The underlying theory is that on some architectures, ordinary pointers are not sufficiently precise to refer to single bytes, but there is the possibility of augmenting the pointer with the addition of, for example, a bit offset. Indeed, it might be the case that there are other small objects which can be packed into words, which also require augmented pointer representations but with less precision than a bit offset.
In such an architecture, it is not possible to take advantage of different pointer precisions for small composite objects, because the standard insists that there be at most two representation of pointers to composites, one for structs and one for unions:
All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. (§6.2.5/27) [Note 2]
In particular, this means that pointers to objects of a given type have the same representation, regardless of whether the type is complete.
Difference in pointer representations is not the only reason that a conversion to a more constrained alignment might fail. For example, an implementation might insert code to verify alignment after a conversion (perhaps in response to a sanitizing compiler option). In the case of an incomplete type, the compiler would not be able to insert static code to do the check (although some kind of runtime check might be possible), but the fact that the compiler might omit the validation code would not alter the undefinedness of the result.
For what its worth, the standard citation in the OP was for C99; in C11, it has been modified slightly (emphasis added to indicate changed wording):
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. 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. (§6.3.2.3/7)
In my opinion, this change is purely editorial. It derives from the decision to change the definition of "object type" in §6.2.5/1. In C99, there were three kinds of type: object types, function types, and incomplete types. In C11, there are only two kinds -- object types and function types -- with the comment that "At various points within a translation unit an object type may be incomplete… or complete…", which is a more accurate description.
Notes
As a completely hypothetical example, consider a machine conceptually similar to the PDP-6/10 architecture. This is a word-addressed machine with a large word size; a word is large enough to contain two addresses (a fact which a hypothetical LISP implementation could take advantage of in order to store a cons node consisting of car and cdr fields into a single word). Because it is desired to represent vectors of small objects efficiently, the machine also has instructions which can extract or overwrite a bitfield within a word, where the bitfield pointer consists of a word pointer accompanied by an offset and length information. (Hence, a word can contain only one bitfield pointer.) (The hardward has an instruction which can increment bitfield pointers by adding the length to the offset and moving to bitfield starting at 0 of the next word if necessary.)
So there could be three different pointer types:
a fullword character pointer consisting of an address and a bitfield offset/length.
an ordinary halfword pointer to any word-aligned object type.
an augmented halfword-plus-one-bit pointer to a pointer to a word aligned object, consisting of an address and an indication of whether the address is in the first or second half of the word. (This representation also probably requires a fullword, but the encoding is simpler. But there might also be some other place for the extra bit. It's a hypothetical example, remember.)
In this hypothetical architecture, the conversion rules become quite complicated. For example, you can convert an char** to an int* because the alignment of int is the same as the alignment of char*. But you cannot convert an int** to an int* because the alignment of int is greater than the alignment of int*.
Rather than memorizing these complex rules, a programmer would probably choose to simply forbear from performing pointer conversions other than those guaranteed to be portable, which is round-tripping through char* or void*.
It would be possible for pointers to all composites to use the larger, more precise pointer type, even if unnecessarily. It seems to me much more likely that an implementation would simply choose to impose a minimum alignment on structs, if not all composite objects. The wording of the standard would allow an implementation to use a minimum alignment for structs and an augmented pointer representation for all unions.
I'm currently working on a project to build a small compiler just for the heck of it.
I've decided to take the approach of building an extremely simple virtual machine to target so I don't have to worry about learning the ins and outs of elf, intel assembly, etc.
My question is about type punning in C using unions. I've decided to only support 32 bit integers and 32 bit float values in the vm's memory. To facilitate this, the "main memory" of the vm is set up like this:
typedef union
{
int i;
float f;
}word;
memory = (word *)malloc(mem_size * sizeof(word));
So I can then just treat the memory section as either an int or a float depending on the instruction.
Is this technically type punning? It certainly would be if I were to use ints as the words of memory and then use a float* to treat them like floats. My current approach, while syntactically different, I don't think is semantically different. In the end I'm still treating 32 bits in memory as either an int or a float.
The only information I could come up with online suggests that this is implementation dependent. Is there a more portable way to acheive this without wasting a bunch of space?
I could do the following, but then I would be taking up more than 2 times as much memory and "reinventing the wheel" with respect to unions.
typedef struct
{
int i;
float f;
char is_int;
}
Edit
I perhaps didn't make my exact question clear. I am aware that I can use either a float or an int from a union without undefined behavior. What I'm after is specifically a way to have a 32 bit memory location that I can safely use as an int or float without knowing what the last value set was. I want to account for the situation where the other type is used.
Yes, storing one member of union and reading another is type punning (assuming the types are sufficiently different). Moreover, this is the only kind of universal (any type to any type) type punning that is officially supported by C language. It is supported in a sense that the language promises that in this case the type punning will actually occur, i.e. that a physical attempt to read an object of one type as an object of another type will take place. Among other things it means that writing one member of the union and reading another member implies a data dependency between the write and the read. This, however, still leaves you with the burden of ensuring that the type punning does not produce a trap representation.
When you use casted pointers for type punning (what is usually understood as "classic" type punning), the language explicitly states that in general case the behavior is undefined (aside from reinterpreting object's value as an array of chars and other restricted cases). Compilers like GCC implement so called "strict aliasing semantics", which basically means that the pointer-based type punning might not work as you expect it to work. For example, the compiler might (and will) ignore the data dependency between type-punned reads and writes and rearrange them arbitrarily, thus completely ruining your intent. This
int i;
float f;
i = 5;
f = *(float *) &i;
can be easily rearranged into actual
f = *(float *) &i;
i = 5;
specifically because a strict-aliased compiler deliberately ignores the possibility of data dependency between the write and the read in the example.
In a modern C compiler, when you really need to perform physical reinterpretation of one objects value as value of another type, you are restricted to either memcpy-ing bytes from one object to another or to union-based type punning. There are no other ways. Casting pointers is no longer a viable option.
As long as you only access the member (int or float) which was most recently stored, there's no problem and no real implementation dependency. It's perfectly safe and well-defined to store a value in a union member and then read that same member.
(Note that there's no guarantee that int and float are the same size, though they are on every system I've seen.)
If you store a value in one member and then read the other, that's type punning. Quoting a footnote in the latest C11 draft:
If the member used to read the contents of a union object is not the
same as the member last used to store a value in the object, the
appropriate part of the object representation of the value is
reinterpreted as an object representation in the new type as described
in 6.2.6 (a process sometimes called "type punning"). This might be a
trap representation.
As a newcomer to C, I'm confused about when casting a pointer is actually OK.
As I understand, you can pretty much cast any pointer type to any other type, and the compiler will let you do it. For example:
int a = 5;
int* intPtr = &a;
char* charPtr = (char*) intPtr;
However, in general this invokes undefined behavior (though it happens to work on many platforms).
This said, there seem to be some exceptions:
you can cast to and from void* freely (?)
you can cast to and from char* freely (?)
(at least I've seen it in code...).
So which casts between pointer types are not undefined behaviour in C?
Edit:
I tried looking into the C standard (section "6.3.2.3 Pointers", at http://c0x.coding-guidelines.com/6.3.2.3.html ), but didn't really understand it, apart from the bit about void*.
Edit2:
Just for clarification: I'm explicitly only asking about "normal" pointers, i.e. not about function pointers. I realize that the rules for casting function pointers are very restrictive. As I matter of fact, I've already asked about that :-): What happens if I cast a function pointer, changing the number of parameters
Basically:
a T * may be freely converted to a void * and back again (where T * is not a function pointer), and you will get the original pointer.
a T * may be freely converted to a U * and back again (where T * and U * are not function pointers), and you will get the original pointer if the alignment requirements are the same. If not, the behaviour is undefined.
a function-pointer may be freely converted to any other function-pointer type and back again, and you will get the original pointer.
Note: T * (for non-function-pointers) always satisfies the alignment requirements for char *.
Important: None of these rules says anything about what happens if you convert, say, a T * to a U * and then try to dereference it. That's a whole different area of the standard.
Oli Charlesworth's excellent answer lists all cases where casting a pointer to a pointer of a different type gives a well-defined result.
In addition, there are four cases where casting a pointer gives implementation-defined results:
You can cast a pointer to an sufficiently large (!) integer type. C99 has the optional types intptr_t and uintptr_t for this purpose. The result is implementation-defined. On platforms that address memory as a contiguous stream of bytes ("linear memory model", used by most modern platforms), it usually returns the numeric value of the memory address the pointer points to, thus simply a byte count. However, not all platforms use a linear memory model, which is why this is implementation-defined :-).
Conversely, you can cast an integer to a pointer. If the integer has a type large enough for intptr_t or uintptr_t and was created by casting a pointer, casting it back to the same pointer type will give you back that pointer (which however may no longer be valid). Otherwise the result is implementation-defined. Note that actually dereferencing the pointer (as opposed to just reading its value) may still be UB.
You can cast a pointer to any object to char*. Then the result points to the lowest addressed byte of the object, and you can read the remaining bytes of the object by incrementing the pointer, up to the object's size. Of course, which values you actually get is again implementation-defined...
You can freely cast null pointers, they'll always stay null pointers regardless of pointer type :-).
Source: C99 standard, sections 6.3.2.3 "Pointers", and 7.18.1.4 "Integer types capable of holding object pointers".
As far as I can tell, all other casts of a pointer to a pointer of a different type are undefined behavior. In particular, if you are not casting to char or a sufficiently large integer type, it may always be UB to cast a pointer to a different pointer type - even without dereferencing it.
This is because the types may have different alignment, and there is no general, portable way to make sure different types have compatible alignment (except for some special cases, such as signed/unsigned integer type pairs).
Generally, if as usual nowadays the pointers themselves have the same alignment properties, the problem is not the cast itself, but whether or not you may access the data through the pointer.
Casting any type T* to void* and back is guaranteed for any object type T: this is guaranteed to give you exactly the same pointer back. void* is the catch all object pointer type.
For other casts between object types there is no guarantee, accessing an object through such a pointer may cause all sorts of problems, such as alignments (bus error), trap representations of integers. Different pointer types are not even guaranteed to have the same width, so theoretically you might even loose information.
One cast that should always work, though, is to (unsigned char*). Through such a pointer you may then investigate the individual bytes of your object.
The authors of the Standard made no attempt to weigh the costs and benefits of supporting conversions among most combinations of pointer types on platforms where such support would be expensive, since:
Most platforms where such conversions would be expensive would likely have been obscure ones the authors of the Standard didn't know about.
People using such platforms would be better placed than the authors of the Standard with the costs and benefits of such support.
If some particular platform uses a different representation for int* and double*, I think the Standard would deliberately allow for the possibility that e.g. round-drip conversion from double* to int* and back to double* would work consistently but conversions from int* to double* and back to int* might fail.
I don't think the authors of the Standard intended that such operations might fail on platforms where such conversions cost nothing. They described the Spirit of C in the charter and rationale documents as including the principle "Don't prevent [or needlessly obstruct] the programmer from doing what needs to be done." Given that principle, there would be no need for the Standard to mandate that implementations process actions in a way that helps programmers accomplish what they need to do in cases where doing so would cost nothing, since implementations that make a bona fide effort to uphold the Spirit of C will behave in such fashion with or without a mandate.