I'm trying to reproduce the behaviour of memcpy
. However when I try it with overlapping memory tests, instead of a trace trap I receive a different result. For example with the following main
function, the result would be OveOveOveOveO�
.
#include <stdio.h>
#include <string.h>
int main() {
char buffer[] = "Overlap Test";
ft_memcpy(buffer + 3, buffer, strlen(buffer) + 1);
printf("%s\n", buffer + 3);
return 0;
}
I did put the restrict
keywords in the function declaration, and I don't understand why the compiler wouldn't put it as a trace trap in this case. Of course chatgpt is useless finding an explanation for this as well. Would such a result be expected in case of memory overlap?
void *ft_memcpy(void *restrict dst, const void *restrict src, size_t n)
{
char *ptr;
char *d;
char *s;
d = dst;
s = (char *)src;
ptr = dst;
if (src == NULL || n == 0)
return (NULL);
while (n > 0)
{
*d = *s;
d++;
s++;
n--;
}
return (ptr);
}
I'm trying to reproduce the behaviour of memcpy.
Bug: Wrong return value
When n == 0
, memcpy()
returns dest
. OP's code should perform likewise.
// if (src == NULL || n == 0)
// return (NULL);
if (src == NULL) // memcpy(..., NULL, ...) is not defined. Do whatever you want.
return NULL; // or maybe `n = 0;`
if (n == 0)
return dest;
Bug: Insufficient space
With char buffer[] = "Overlap Test";
, ft_memcpy(buffer + 3, buffer, strlen(buffer) + 1);
is a problem as buffer + 3
does not have enough room for strlen(buffer) + 1
characters. @chqrlie
How is memcpy.c supposed to react to memory overlap?
Would such a result be expected in case of memory overlap?
Given the 2 restrict
in the function signature, using overlapping buffers is undefined behavior (UB). There are no expected results.
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
Even if OP's ft_memcpy()
functioned just like OP's compiler's memcpy()
today, it is not certain it will tomorrow or on some other machine. It is UB.
I recommend to not test for equivalent functionality with overlapping buffers - since it is UB.
Notice, that focus on this issue missed proper behavior when n == 0
.
Consider instead "How is memcpy.c memmove()
supposed to react to memory overlap?"
That is trickier.
Copying takes place as if the
n
characters from the object pointed to bys2
are first copied into a temporary array ofn
characters that does not overlap the objects pointed to bys1
ands2
, and then then
characters from the temporary array are copied into the object pointed to bys1
.
C23dr § 7.26.2.3 2
OP's code needs changes to meet that. To do well is a challenge as the usual approach is to compare addresses. Depending on which is greater, copy from the beginning or end of the source buffer. Yet for user code, there is no defined way to compare arbitrary addresses for order. Various tricks abound that usually work.
The below does not have UB, yet may not compile nor function exactly like memmove()
on all machines.
#include <stdlib.h>
#include <stdint.h>
void *ft_memmove(void *dst, const void *src, size_t n) {
// Not needed as passing `NULL` for either pointer parameter is UB in memmove().
if (dst == NULL || src == NULL) {
n = 0;
}
// Weakness: uintptr_t is an optional type.
uintptr_t d = (uintptr_t) dst;
uintptr_t s = (uintptr_t) src;
// Weakness: `d < s` is not a defined order compare of the pointers.
if (d < s) {
for (size_t i = 0; i < n; i++) {
((char*)dst)[i] = ((const char*)src)[i];
}
} else {
while (n > 0) {
n--;
((char*)dst)[n] = ((const char*)src)[n];
}
}
return dst;
}