ft_memcpy

ft_memcpy

“ft_memcpy에 대하여”

<목차>

1. MY CODES

2. MAN MEMCPY

memcpy — copy memory area

(1) MY CODES

#include "libft.h"

void	*ft_memcpy(void *dest, const void *src, size_t n)
{
	unsigned char	*new_dest;
	unsigned char	*new_src;
	size_t			i;

	if (dest == src)
		return (dest);	// block self-copy
	new_dest = (unsigned char *) dest;
	new_src = (unsigned char *) src;
	i = 0;
	while (i++ < n)
		*new_dest++ = *new_src++;
	return (dest);
}
  • 메모리 영역 src로부터 n bytes 만큼을 메모리 영역 dest로 복사한다. 메모리 영역은 overlap 되어서는 안 된다.

(2) MAN MEMCPY

SYNOPSIS
	#include <string.h>

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

DESCRIPTION
	The memcpy() function copies n bytes from memory area src to memory area dest.
	The memory areas must not overlap. Use memmove(3) if the memory areas do overlap.

RETURN VALUE
	The memcpy() function returns a pointer to dest.

NOTE
	Failure to observe the requirement that the memory areas do not overlap
	has been the source of significant bugs. (POSIX and the C standards are 
	explicit that employing memcpy() with overlapping areas produces undefined 
	behavior.) Most notably, in glibc 2.13 a performance optimization of memcpy()
	on some platforms (including x86-64) included changing the order in which bytes 
	were copied from src to dest. ... (중략)
  • 매개 변수로 들어온 void* dest와 void* src에 대해 n 바이트만큼을 복사하는 함수이다.
  • NOTE에 따르면 메모리가 overlap되지 않아야 한다는 규칙을 준수하지 않는 것은 심각한 버그를 발생시켰다고 한다. 또 POSIX와 C 표준은 memcpy()를 겹치는 영역에서 사용하는 것이 정의되지 않은 동작을 생성한다고 명시한다고 한다. 잘 알려진 예시를 살펴보자.
#include <stdio.h>
#include <string.h>
 
int main() 
{
    int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    memcpy(a + 1, a, 9 * sizeof(int)); 
	// a+1 => dest, a => src, 9 * sizeof(int) => n bytes

    for (int i = 0; i < 10; i++) 
	{
        printf("%d ", a[i]);
    }
    return 0;
}
output:
1 1 2 3 4 5 6 7 8 9 혹은 1 1 1 1 1 1 1 1 1
  • 코드의 의도는 메모리 상의 a의 첫 번째 인덱스(dest, a[1])에, src로 들어온 a 배열의 0번째 데이터로부터 9개 값(9 * sizeof(int))을 복사해 넣는 것이다. 거의 자기 복제의 모습을 하고 있다.
  • 의도대로라면 첫 번째 결과대로 1 1 2 3 4 5 6 7 8 9가 나오는 것이 맞지만 1 1 1 1 1 1 1 1 1이 나올 수 있는 이유는, 메모리가 src에서 dest로 한땀 한땀 복사되는 과정에서 src가 변했을 때 dest에 무슨 값이 복사되는지가 undefined behavior이기 때문이다. 첫 복사가 일어나는 과정에서 a[1]가 2에서 1로 바뀌고, 두 번째 복사에서 원래 a[1]였던 2가 a[2]로 복사되어야 하지만, 이미 첫 번째 복사에서 a[1]이 1로 바뀌었기 때문에 a[2]에도 1이 복사되고 그 과정들이 반복되면서 a 배열이 전부 1로 가득 차버리게 된다.
  • 내가 코드를 실행시켰을 때는 정상적으로 1 1 2 3 4 5 6 7 8 9가 나왔는데, 메모리 overlap 문제를 해결한 개선된 버전이 적용된 것이 아닐까 생각한다. 아무래도 대부분의 경우 개선된 버전이 적용되어 있을 것이라 생각한다.
  • Use memmove(3) if the memory areas do overlap., memmove()는 memcpy()의 overlap 문제를 해결한 함수이며 메모리 overlap이 발생할 수 있는 경우 memmove()를 사용할 것을 권장한다.