According to N1570 6.5/6:
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.
That would suggest that even on a system where "long" and some other integer type have the same representation, the following would invoke Undefined Behavior:
#if ~0UL == ~0U
#define long_equiv int
#elif ~0UL == ~0ULL
#define long_equiv long long
#else
#error Oops
#endif
long blah(void)
{
long l;
long_equiv l2;
long_equiv *p = malloc(sizeof (long));
l = 1234;
memcpy(p, &l, sizeof (long));
l2 = *p;
free(p); // Added to address complaint about leak
return l2;
}
since the data pointed to by l
clearly has effective type long
and the object pointed to by p
has no declared type, the memcpy
should set the effective type of the storage to long
. Since reading use of an lvalue of type long_equiv
to read an object with effective type of long
is not allowed, the code would invoke Undefined Behavior.
Given that prior to C99 memcpy
was one of the standard ways to copy data of one type to storage of another type, the new rules about memcpy
cause a lot of existing code to invoke Undefined Behavior. If the rule had instead been that using memcpy
to write to allocated storage leaves the destination without any effective type, the behavior would be defined.
Are there any compilers which do not behave as though memcpy
leaves the effective type of the destination unset when used to copy information to allocated storage, or should use of memcpy
for purposes of data translation be considered "safe"? If some compilers do apply effective type of the source to the destination, what would be the proper way of copying data in type-agnostic fashion? What is meant by "copied as an array of character type"?
free(p)
, but loose the pointer on return. Your point is not quite clear. IIRC, it was already UB in C89 to copy one type to another that way. It just happened to work and likely still works. The effective type is not "applied" by the compiler, but assumed. Likeconst
qualified objects, the programmer guarantees this and the compiler just relies on it. For the last sentence, please read the description ofmemmove
&memcpy
. They are supposed to copychar
-wise. – Armed~0UL
? Because on two's complement,~0
,~0L
, and~0LL
are all-1
, and all equal to each other, regardless of representational size – Wildenint
andlong
had the same representation; while it would be legal for an implementation to use different representations (even if they both have the same maximum value), such an implementation would be required to document that fact. It is permissible to read or write objects of any effective type using "unsigned char", and in C89 memcpy was defined as writing the unsigned char values at the destination to match those of the source, which would as noted be fully defined if the representations of "int" and "long" match. – AirworthyN1570 6.5/6
out of the criteria for determining whether an issue exists with strict-alias violation. I'm not sure that focusing on one of the allowable criteria exceptions to strict aliasing is meant to provide carte-blanche to use the criteria as a stand-alone rule in and of itself. I read through all the criteria recently and the impression I got was those specific exceptions where strict aliasing was not violated was not intended to act as a recommendation (or validation) of that approach. The limit on modifying values would control – Heribertomalloc
returned a null pointer? OP does not catch that. – Armedstdint.h
types. – Armed(u)int32_t
. Still no reason to use different types for in/out. If in doubt,typedef
your own integer type and use that alias throughout your code. And if you need a "points to anything" pointer, usevoid *
. – Armedstruct
s as the very first field. Alternatively, you can use a struct with the common fields first and an anonymous union ofstruct
s for the rest. To me, "terrible" would be a code which wildly converts between types, not a code which consistently uses one single type. – Armedunion { int length; short dat[1];} moe;
,union { int size; short dat[2];} larry;
etc. up to some unknown limit, that could be rather inconvenient. Further, all accesses to union members must be done through the union. It is not legitimate to use pointers to union members... – Airworthy