Search code examples
cvoid-pointersopaque-pointers

Is casting from TYPE* to unsigned char* allowed?


C99 -- specifically section 6.2.6.1, paragraph 4 -- states that copying an object representation into an array of unsigned char is allowed:

struct {
    int foo;
    double bar;
} baz;
unsigned char bytes[sizeof baz];

// Do things with the baz structure.

memcpy(bytes, &baz, sizeof bytes);

// Do things with the bytes array.

My question: can we not avoid the extra memory allocation and copy operation by simply casting? For example:

struct {
    int foo;
    double bar;
} baz;
unsigned char *bytes = (void *)&baz;

// Do stuff with the baz structure.

// Do things with the bytes array.

Of course, the size would need to be tracked, but is this legal in the first place, or does it fall into the realm of implementation-defined or undefined behavior?

I ask because I'm implementing an algorithm similar to qsort, and I'd like it to work for any array regardless of type, just as qsort does.


Solution

  • 6.5 Expressions

    [...]
    6 The effective type of an object for an access to its stored value is the declared type of the object, if any.87) 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. If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. 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.
    7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types:88)

    • 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.

    Emphasis mine. Thus, you are ok treating any type as an array of characters (unsigned char[], char[] or signed char[]).

    I also quoted paragraph 6 because it makes the reverse not apply.