Memcpy entry with a strict alias

The general response to the query "how to implement the memcpy function that complies with strict alias rules" is something like strings

void *memcpy(void *dest, const void *src, size_t n) { for (size_t i = 0; i < n; i++) ((char*)dest)[i] = ((const char*)src)[i]; return dest; } 

However, if I understand correctly, the compiler can freely reorder the memcpy call and access dest, because it can reorder char * entries when reading from any other type of pointer (strict pseudo-decreasing rules only prevent reordering reads from char * with writes to any another type of pointer).

Is this correct, and if so, are there ways to implement memcpy correctly, or should we just rely on the built-in memcpy?

Please note that this question concerns not only memcpy, but also deserialization / decoding functions.

+8
c ++ c strict-aliasing memcpy
source share
6 answers

The strict anti-aliasing rule specifically excludes the rejection of char types (see the last paragraph below), so the compiler will do the right thing in your case. The punning type is only a problem when converting things like int to short . Here, the compiler can make assumptions that cause undefined behavior.

C99 §6.5 / 7:

The object must have a stored value, accessed only by the lvalue expression, which has one of the following types:

  • a type compatible with an efficient object type,
  • qualified version of a type compatible with an efficient type of object,
  • a type that is a signed or unsigned type corresponding to the effective type of the object,
  • a type that is a signed or unsigned type corresponding to a qualified version of an effective object type,
  • aggregate or type of association that includes one of the above types among its members (including recursively, a member of a sub-aggregate or contained association) or
  • character type.
+5
source share

Since both (char*)dest and (char const*)src point to char , the compiler should assume that they can be an alias. In addition, there is a rule that states that a pointer to a character type may have an alias.

All this does not matter for memcpy , since the actual signature is:

 void* memcpy( void* restrict dest, void* restrict src, size_t n ); 

which tells the compiler that there can be no aliases because the user guarantees this. You cannot use memcpy to copy overlapping areas without undefined behavior.

In any case, there are no problems with this implementation.

+4
source share

IANALL, but I don’t think the compiler is allowed to mess things up the way you describe. Strict anti-aliasing is “implemented” in the specification by rendering undefined access to an object through an illegal pointer type, and not by specifying another complex partial order of access to objects.

+1
source share

The fact that everything seems to be missing here is that the strict alias (6.5 / 7) depends on the type of effective type (6.5 / 6). And the efficient type has explicit special rules for the memcpy function (6.5 / 6):

If the value is copied to an object that does not have a declared type using memcpy or memmove , or copied as an array of symbol type, then the effective type of the changed object for this access and for subsequent calls that do not change the value is the effective type of the object from which the value is copied , if he is.

Therefore, I don’t think it even makes sense to talk about a strict alias inside the memcpy function. You can only talk about a strict alias if you know the effective type. Now, how do you determine this from the above? Is the inside of memcpy copy with memcpy or not?

As if "in order to understand which efficient type is used in memcpy, you must first understand what effective type is used in memcpy."

So, I don’t quite understand how the question or any of the answers presented makes sense.

+1
source share

Yes, something is missing for you. The compiler can change the write order to dest and read dest . Now, since reading from src will happen - before writing to dest , and your hypothethical reading from dest will happen - after writing to dest , it follows that reading from dest occurs after reading from src .

0
source share

If an object does not have a declared type, any effective type that it can receive will only be valid until the next change to the object. Writing to an object using a character type pointer is considered to be its modification, thereby discarding the old type, but writing it using a character type pointer does not establish a new type if such an operation is not performed as part of "copying as a character type array" so that it it didn’t mean. Objects that do not have an effective type can be legally read with any type.

Since the semantics of the effective type for "copy as an array of character type" will be the same as for memcpy , the implementation of memcpy can be written using character pointers for reading and writing. It may not set the effective type of the destination, the memcpy path must be allowed, but any behavior that will be determined using memcpy will be identical if the destination is left without an effective type [as IMHO should be in the case of memcpy ].

I'm not sure who came up with the idea that the compiler can assume that the repository that has acquired an efficient type retains this efficient type when it is modified with char* , but nothing in the Standard justifies it. If you need code to work with gcc, specify that it should be used with the -fno-strict-aliasing flag, unless gcc starts to execute the Standard. There is no reason to bend back, trying to support the compiler, whose authors are constantly looking for new cases to ignore the aliasing even in cases where the Standard requires them to recognize it.

0
source share

All Articles