memcpy 함수 구현하기

윤효준·2024년 7월 24일

42 Libft 복습

목록 보기
9/28

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

Synopsis

#include <string.h>

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

restrict 키워드는 Description 이야기하면서 설명하겠다!

Description

  • memcpy 함수는 src가 가리키는 메모리 영역에서 dst가 가리키는 메모리 영역으로 n바이트를 복사한다.

  • src가 가리키는 영역과 dst가 가리키는 영역이 겹칠 때의 behavior는 undefined이다!

이게 무슨 이야기냐면 아래 코드를 보자.

#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);
}

위 코드를 실행하면 어떤 결과가 나올 거 같나요????
혹시 0 1 0 1 2를 생각하고 계신가요??

맞습니다.

그런데 제 노트북에서의 실행 결과는 다음과 같습니다.

오잉 왜 0 1 0 1 0이 나오죠???
이 경우에는 이렇게 이해할 수 있습니다.

  1. arr[2]에 arr[0]인 0이 저장된다.
  2. arr[3]에 arr[1]인 1이 저장된다.
  3. arr[4]에 arr[2]인 0이 저장된다.

그래서 출력 결과가 0 1 0 1 0이 나오는겁니다.
왜 같은 코드인데 다른 결과가 나오는 걸까요???
그 이유는restrict 키워드에 있습니다. (물론 내부 구현 방식에 따른 복사 순서를 보장하지 않는 것 때문도 있습니다.)

restrict이란???

restrict 키워드는 포인터가 가리키는 메모리 영역이 다른 포인터와 겹치지 않음을 컴파일러에게 보장한다.
이는 컴파일러가 최적화를 수행할 때 매우 중요한 정보이고 최적화 수준에 따라 프로그램의 동작이 달라질 수 있다.
어떤 방식으로 최적화하냐에 따라 복사 순서가 달라지고 이에 따라 중간 상태의 데이터 오염 문제를 발생시킬 수 있어 예측 불가능한 동작이 발생합니다.

그럼 이러한 문제는 어떻게 해결하는가???
Description에 작성되어 있다! Description 내용 설명으로 다시 돌아가겠다!

  • src의 영역과 dst의 영역이 겹친다면 memmove를 사용하자!(memmove는 다음 게시글에서 설명하겠다!)

memcpy 함수 사용 예시

//배열 복사
int src[5] = {1, 2, 3, 4, 5};
int dst[5];
memcpy(dst, src, 5 * sizeof(int));

//구조체 복사
//구조체의 멤버를 하나씩 복사하는 대신, 전체 구조체를 한 번에 복사할 수 있다.
struct MyStruct {
    int a;
    float b;
} src, dst;

src.a = 1;
src.b = 3.14;
memcpy(&dst, &src, sizeof(struct MyStruct));


/* 네트워크 프로그래밍
* 송신 버퍼나 수신 버퍼에 데이터를 복사할 때 사용된다.
* 네트워크 패킥을 생성하거나 ㅓ리할 때, 패킷의 헤더와 데이터를 조합하는 데 유용하다.
*/
char packet[256];
char header[64];
char data[192];
memcpy(packet, header, 64);
memcpy(packet + 64, data, 192);

/* 파일 입출력
* 파일에서 읽은 데이터를 버퍼에 복사하거나, 버퍼의 데이터를 파일에 쓸 때 사용된다.
* 대용량 데이터 처리가 필요한 경우 유용하다.
*/
void *src = (void *)0x1000;
void *dst = (void *)0x2000;
memcpy(dst, src, 4096);

/* 시스템 프로그래밍
* 저수준 메모리 작업이 필요한 경우, 커널이나 드라이버 개발 시 사용된다.
* 직접 메모리 주소를 다룰 때 유용하다.
*/
void *src = (void *)0x1000;
void *dst = (void *)0x2000;
memcpy(dst, src, 4096);

Return Values

  • memcpy 함수는 dst의 값을 반환한다.

구현

void	*ft_memcpy(void *dst, const void *src, size_t n)
{
	unsigned char		*d;
	const unsigned char	*s;

	//NULL 포인터 검사
    //가독성을 위해 NULL 메크로를 쓰는 것이 좋다.
    //NULL 메크로는 stdlib.h, stdio.h, string.h 등의 헤더 파일에 정의되어 있다.
	if (dst == NULL && src == NULL)
		return (NULL);
	d = (unsigned char *) dst;
	s = (const unsigned char *) src;
	while (n > 0)
	{
		*d = *s;
		d++;
		s++;
		n--;
	}
	return (dst);
}

위의 구현에서 while문을 아래와 같이 바꿀 수 있다.

while (n-- > 0)
	*d++ = *s++;
profile
작은 문제를 하나하나 해결하며, 누군가의 하루에 선물이 되는 코드를 작성해 갑니다.

0개의 댓글