It is legal to cast a pointer to type T to a pointer to type S, and there will be no conversion between the values of T and S. This allows C to look at memory from different viewpoints.
In the byte addressable memory model, memory is as if it were an array of bytes and the pointer an index into the array. Bytes are synonymous with type char, except that a byte is agnostic as to the representation: whether the byte is signed or unsigned, or whether it an element of the ASCII (Basic Latin) character set.
A location for a variable is a segment of memory, that is, a consecutive sequence of bytes, of quantity sufficient to hold values variable's type. The sizeof operator applied to a type gives the number of bytes needed for that type.
assert(sizeof(char)==1) ; assert(sizeof(char)<sizeof(short)) ; assert(sizeof(unsigned int)==sizeof(int)) ; assert(sizeof(char *)==sizeof(long *)) ;are all valid assertions.
For composite types, e.g. arrays and structures, the sizeof the composite type is derived from the size of the components of the composite type. An array is a sequence of variables, each of the same type, and the overall size is simply the number of elements times the size of any element. The size of a structure is at least the sum size of all components of the structure, but might be larger to accommodate the machine's preference for efficient data transfers.
assert(sizeof(int [N])==N*sizeof(int)); assert(sizeof(struct{ char c; int i;})>=(sizeof(char)+sizeof(int))) ;are all valid assertions.
Variables of dynamic extent have lifetimes under the control of the programmer. Locations for the variables are created by a call to the Standard C Library function malloc() and are destroyed by a call to the library function free(). The Standard C Library, while not part of the grammar of C, is a part of the C standard.
The value returned by malloc is "pointer to void", void *, a.k.a. "void star", where void is a type with no values, except possibly "null". As malloc does not know the usage of the location, it cannot claim to know the type. It is only told how large a memory location is needed for the intended type of the location. Void * is a generic pointer which ignores the type of what is pointed to but must be cast back to a more specific type in order to be used.
An example of a dynamic extent integer variable is given:
int * pi ; pi = (int *) malloc(sizeof(int)) ; *pi = 1 ; free(pi) ;Note that the lifetime is not that of the variable pi, but of the location pi points to. The variable pi takes values "int *", and the malloc return of void * is cast by the casting operator (int *) to an int *, so that the value can be stored in pi.