memmove 함수의 manual은 다음과 같다!

#include <string.h>
void *memmove(void *dst, const void *src, size_t len);
memmove함수는 len 바이트를 src문자열에서 dst문자열로 복사한다.
memcpy에서는 src와 dst를 메모리 영역이라고 칭했는데 왜 여기서는 문자열이라고 했는지 잘 모르겠다. 다만 일반적인 사용 사례 중 하나이므로
string을 쓴 것이 아닐까 생각이 든다.
두 문자열은 겹칠 수 있으며 원본 데이터를 손상시키지 않고 안전하게 복사한다.
memcpy와의 차이점이 여기서 나타난다!
memmove함수는 dst의 값을 반환한다.구현 시 가장 중요한 부분은 두 문자열이 겹쳤을 때 원본 데이터를 손상시키지 않아야 한다는 것이다.
예를 들어 memcpy 함수 구현에서 들었던 예시를 가지고 오겠다.
#include <stdio.h>
#include <string.h>
int main(void)
{
int arr[5] = {0, 1, 2, 3, 4};
memcpy(arr + 2, arr, 12);
//memcpy는 바이트 단위로 복사하기에 정수 3개 복사하기에는 12바이트가 필요하다.
//정수를 3개만 복사하는 이유는 4개 이상을 복사하면 arr 영역을 벗어나 오버플로우가 발생하기 때문이다.
for (int i = 0; i < 5; i++)
printf("%d ", arr[i]);
return (0);
}
여기서 memcpy함수 대신 memmove함수를 이용하여 배열 {0, 1, 2, 3, 4}를 {0, 1, 0, 1, 2}로 만들고 싶은 것이다.
여기서 arr를 arr + 2로 이동시킬 때 앞에서부터 옮긴다고 하면 아래와 같이 된다.
- arr[2]에 arr[0]인 0이 저장된다. >> arr[2]가 오염이 되어 나중에 문제를 일으킨다.
- arr[3]에 arr[1]인 1이 저장된다.
- arr[4]에 arr[2]인 0이 저장된다. >> 오염된 arr[2]를 사용하여 우리의 의도와 다르게 된다.
위와 같은 방법을 적용시켰을 때는 데이터가 오염되어 문제가 발생한다.
이때 어떻게 하면 데이터 오염을 방지할 수 있을까??
바로 마지막부터 이동을 시키는 것이다.
arr + 2와 arr를 12 바이트씩 이동시켜 arr[2]를 arr[4]로 먼저 이동시키는 것이다.
1. arr[4]에 arr[2]인 2가 저장된다.
2. arr[3]에 arr[1]인 1이 저장된다.
3. arr[2]에 arr[0]인 0이 저장된다.
따라서 arr = {0, 1, 0, 1, 2}가 저장된다.
위와 같은 방법을 통해 우리는 이동시키는 데이터를 오염없이 이동시킬 수 있다.
그럼 항상 뒤에서부터 이동을 시작하면 될까???
그건 아니다.
memmove(arr, arr + 2, 12)를 생각해보자.
아래는 arr + 2를 arr로 이동시킬 때 마지막부터 이동을 단계별로 설명한 것이다.
arr와 arr + 2를 12 바이트씩 이동시켜 arr[4]를 arr[2]로 먼저 이동시키는 것이다.
1. arr[2]에 arr[4]인 4가 저장된다.
2. arr[1]에 arr[3]인 3이 저장된다.
3. arr[0]에 arr[2]인 4가 저장된다. >> arr[2]는 1번에서 오영되었다.
데이터가 오염이 되어 의도한 동작이 나오지 않았다.
이 경우는 memcpy함수를 구현했던 방식대로 앞에서부터 이동시켜야 오염이 없다.
따라서src와 dst의 위치에 따라 이동 방식을 달리해야 한다.
static void copy_forward(unsigned char *d, const unsigned char *s, size_t len)
{
while (len-- > 0)
*d++ = *s++;
}
static void copy_backward(unsigned char *d, const unsigned char *s, size_t len)
{
while (len-- > 0)
*d-- = *s--;
}
void *ft_memmove(void *dst, const void *src, size_t len)
{
unsigned char *d;
const unsigned char *s;
// 널 포인터 검사
if (dst == NULL || src == NULL)
return (NULL);
d = (unsigned char *)dst;
s = (const unsigned char *)src;
//d와 s의 대소 비교
if (d < s)
copy_forward(d, s, len);
else if (d > s)
{
d += len - 1;
s += len - 1;
copy_backward(d, s, len);
}
return (dst);
}
static이란?static 키워드를 사용하여 선언된 함수는 해당 파일 내에서만 접근할 수 있어 동일한 프로젝트 내에서 다른 파일의 동일한 이름의 함수와 충돌을 방지한다.위의 내용은 함수 선언에 사용하는 static 키워드이다.
함수 선언 외에도 정적 변수, 정적 전역 변수, 클래스 정적 멤버를 선언할 때 사용하는데 앞의 3개에 대해서는 이후 과제에서 나오기에 그때 설명하겠다!