Howard Chu writes:
In the latest C spec it is impossible to write a "legal" implementation of malloc or memcpy.
Is this right? My impression is that in the past, the intent (at least) of the standard was that something like this would work:
void * memcpy(void * restrict destination, const void * restrict source, size_t nbytes)
{
size_t i;
unsigned char *dst = (unsigned char *) destination;
const unsigned char *src = (const unsigned char *) source;
for (i = 0; i < nbytes; i++)
dst[i] = src[i];
return destination;
}
What rules in the latest C standard are violated here? Or, what part of the specification of memcpy
is not correctly implemented by this code?
memmove() is similar to memcpy() as it also copies data from a source to destination.
The article itself describes a safer alternative: memcpy_s, which requires you to specify the maximum length of the target. When that number is provided independent of the amount of bytes to copy, it acts as a barrier to prevent buffer overflow. Of course, you can abuse that by giving the same number to both.
In the C Programming Language, the memcpy function copies n characters from the object pointed to by s2 into the object pointed to by s1. It returns a pointer to the destination. The memcpy function may not work if the objects overlap.
A simple loop is slightly faster for about 10-20 bytes and less (It's a single compare+branch, see OP_T_THRES ), but for larger sizes, memcpy is faster and portable.
For the malloc
function, paragraph 6.5 §6 makes it clear that it is not possible to write a conformant and portable C implementation :
The effective type of an object for an access to its stored value is the declared type of the object, if any(87)...
The (non normative) note 87 says:
Allocated objects have no declared type.
The only way to declare a object with no declared type is... through the allocation function which is required to return such an object! So inside the allocation function, you must have something that cannot be allowed by the standard to setup a memory zone with no declared type.
In common implementations, the standard library malloc and free are indeed implemented in C, but the system knows about it and assumes that the character array which has been provided inside malloc
just has no declared type. Full stop.
But the remaining part of the same paragraph explains that there is no real problem in writing a memcpy
implementation (emphasize mine):
... 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.
Provided you copy the object as an array of character type, which is a special access allowed per the strict aliasing rule, there is no problem in implementing memcpy
, and your code is a possible and valid implementation.
IMHO the rant of Howard Chu is about that old good memcpy
usage, which is no longer valid (assuming sizeof(float) == sizeof(int)
):
float f = 1.0;
int i;
memcpy(&i, &f, sizeof(int)); // valid: copy at byte level, but the value of i is undefined
print("Repr of %f is %x\n", i, i); // UB: i cannot be accessed as a float
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With