memcpy?

#include <string.h>

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

Linux manpage description

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

해석 및 부연설명

: src에 저장되어있는 값을 n만큼 복사해 dst의 시작주소부터 붙여넣은 후 그 주소를 반환한다. 각 메모리가 overlap되어있어서는 안되며, 그럴 경우엔 memcpy 대신 memmove를 사용해야한다.

ex)

char	dst[] = "abcdefghijklmnop";
char	src[] = "12345";
memcpy(dst, src, sizeof(src) - 1);
printf("%s\n", dst);

코드 실행 결과

12345fghijklmnop

복사할 src의 맨 마지막에 있을 NUL을 고려하여 size를 설정해야한다.

의문점 및 생각해볼점

  1. overlap?
  2. 복사할 size가 dst의 크기를 넘어간다면?
  3. strcpy와의 차이점은?
  4. (unsigned char)* vs *(unsigned char *)

overlap이란

알기쉽게 정리되어있는 링크
두 변수의 메모리주소가 겹칠 경우 복사 및 붙여넣기시에 덮어씌워지는 현상이 문제가 되는 모양이다. 예를들어

dst   O O O O 1 2 3 4 5 6 7 8    
src   a b c d 1 2 3 O O O O O    

이런식으로 존재할경우 src의 값을 dst에 하나씩 복붙하다보면

dst   O O O O a b c 4 5 6 7 8    
src   a b c d a b c O O O O O    

이런식으로 아직 복사되지 않은 src의 뒷부분의 값이 원본 src와 달라지게 되어 최종적으로

dst   O O O O a b c d a b c 8    
src   a b c d a b c O O O O O    

라는 엉뚱한 결과가 나오게 될 것이다. 그러니까 memcpy 함수에서는 이런 문제가 생길 경우 핸들링할 수 없으니 memmove라는 다른 함수를 써서 처리하라고 한다.


dst의 크기보다 더 큰 값을 복붙할경우

그냥 dst의 크기에 관계없이 복붙하겠다는 메모리 그대로 dst에 넣어버려 에러 혹은 엉뚱한 결과가 발생했다. memset함수 자체에서도 이를 따로 핸들링하지 않았으니 구현시에도 따로 뭔가 해줄 필요는 없을 것 같다.

  • 비슷한 케이스들을 slack에서 검색해보다가 느낀건데, NULL포인터를 입력받았을시 원 함수에 특별한 처리사항이 없는 한 protect를 하느냐(NULL 반환), 마느냐(segfault 에러 등)는 사람에 따라 생각이 다를 수 있는 것 같다. 원하지 않는 입력이 들어왔을 때 에러가 발생해 프로그램이 종료되느냐 혹은 프로그램은 꺼지지 않게 하느냐 둘중 취향에 맞는 쪽으로 하는걸로. 난 전자가 나은 것 같다.

strcpy와의 차이점

strcpy는 애초에 문자열 끝 NULL문자가 나올때까지 복붙하고, memcpy는 복붙할 byte 수를 지정하는 점부터 다르니 비교 대상을 strncpy로 잡는게 더 적절해보인다.
strncpy와 memcpy의 차이점 링크
둘 다 거의 똑같다고 볼 수 있지만 결과적으로 strncpymemcpy와 달리 복사하는 src의 NULL이 언제나오는지를 계속 체크한다는 점이 달랐다.
n만큼 복사하는 도중에 NULL이 나오면 strncpy는 거기서 끝이고, memcpyNULL과 상관없이 딱 정해진 n만큼을 무조건 복사한다.
그래서 NULL을 꾸준히 확인하는 strncpy보다 memcpy가 좀 더 빠르다고 한다.


형변환은 어디에서?

  • (unsigned char)*ptr
    포인터 ptr을 타입에 따른 메모리만큼 읽은 후, 그 값을 unsigned char 형으로 형변환한다.
  • *(unsigned char *)ptr
    포인터 ptr을 unsigned char *형으로 바꾼 후 unsigned char *형에 해당하는 메모리를 읽어 그 값을 구한다.

ft_memcpy 구현

void	*ft_memcpy(void *dst, const void *src, size_t size)
{
	void	*result;

	result = dst;
	if (dst == src)
		return (result);
	while (size-- > 0)
		*(unsigned char *)dst++ = *(unsigned char *)src++;
	return (result);
}

dst나 src 한쪽만 NULL인 경우는 segmentation fault가 발생하여서 따로 protect 하지 않을 생각이었으나, dst와 src가 둘다 NULL로 입력될경우 원 함수인 memset에서는 NULL포인터를 반환하였다.
따라서 dst == src 조건을 부여해 memset 함수와 동일하게 작동되도록 구현하였다.