If you have memory that has been defined as an unsigned long
, as with unsigned long L;
, then you may access its bytes through a char
value, as with:
char *c = (char *) &L;
printf("Byte 3 of L is %d.\n", c[3]);
This works because C 2018 6.3.2.3 7 says:
… 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.
and 6.5 7 says we can access any object through a character type:
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 character type.
This is also true for an unsigned long
that was created by storing an unsigned long
in allocated memory, as with unsigned long *p = malloc(sizeof *p); *p = 300;
.
However, the order of the bytes in the unsigned long
is implementation-defined. C requires unsigned integer types to use a pure binary notation but does not specify the order of the bits within the bytes or the bytes within the representation, and it also allows padding.
Also, it is generally preferable to use an unsigned char
to access the bytes, rather than a char
, to avoid potential complications with signedness. And note that character types are special in this regard, per the quotes from the standard above. Reinterpreting through other types is not generally supported in C, except with unions as described below.
If you just have “some memory,” say a special region in an embedded system, and you want to access it both as an unsigned long
and a char
as described above, then you may need support from your compiler beyond what the C standard. Of course, C implementations intended for use in embedded systems generally provide support for accessing such memory as needed.
However, even if you do not have a defined or allocated object and do not have special support from the compiler, you can put the bytes of an unsigned long
in that memory by using character types. In particular, memcpy
is defined to copy byte-by-byte, so it would serve:
unsigned long L = 300; // Prepare a normal unsigned long.
memcpy(target, &L, sizeof L); // Copy bytes of L to the address in pointer "target".
Then, of course, you can also set a character pointer to point to the same place as target
and use that pointer to access the bytes, as above.
Another way to use the same memory for two or more object types is through a union. This is defined in C but not C++:
typedef union
{
unsigned long L;
char c[4];
} MyUnion;
MyUnion x = { .L = 300 };
printf("Byte 3 of the unsigned long is %d.\n", x.c[3]);
This works because C 2018 6.5.2.3 3 tells us that the .
operator accesses the value of the named member of the union, and note 99 makes it clear that, if the requested member is not the last one stored, “the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type”.