[C]memcpy


memcpy함수는 메모리값을 원하는 크기만큼 복사하는 함수입니다.


1️⃣ 함수원형

< memcpy >

void *memcpy(void *dst, const void *src, size_t size)

2️⃣ 헤더파일, 반환값

  • 반환값:

    조건반환값(size_t)
    성공시복사된 포인터주소(dst)
    dst, src모두 NULL포인터일 때0(NULL포인터)
    dst, src둘중하나가 NULL포인터일 때segmentation fault
  • 헤더파일: <string.h>


3️⃣ 함수구현

< memcpy >

void *memcpy(void *dst, const void *src, size_t size)
{
	unsigned char       *dp;
	const unsigned char *sp;

	dp = dst;
	sp = src;
	if (dp == NULL && sp == NULL)
		return (0);
	while (size-- > 0)
		*dp++ = *sp++;
	return (dst);
}

4️⃣ 특징 & 주의사항

  1. 자료형이 size_t인 size의 값이 음수가 되면 버퍼오버플로우(size_t는 unsigned형으로 선언되어 있기 때문)가 일어납니다. 컴파일러에 따라서 경고메시지를 출력해주기도 합니다. 대부분 컴파일러에서 abort오류가 일어납니다.
  2. 받게되는 인자의 자료형이 (void*)로 되어 있기 때문에 dst와 src의 자료형이 통일되지 않아도되며 size가 음수가 아니라면 어느값이든 가능합니다.
    하지만 되도록이면 자료형을 통일되게 사용하고 size에 직접 숫자를 입력하는 대신
    3 * size(char)와 같이 사용하는 것이 좋을 것 같습니다.(아래 자세한 내용)
  3. 메모리를 잘 통제할 수 있다면 memcpy함수로 2차원배열도 복사가 가능합니다.(아래 자세한 내용)
  4. NULL문자(‘\0’)를 문장끝에 붙여주지 않기 때문에 크기를 잘 고려해서 사용해야 됩니다.
    ex> char word[] = "hello";를 복사하고 싶다면 3번째 인자의 값이 6이 되도록합니다.
  5. 복사할 크기가 dst의 크기보다 커지지 않도록 조심해야 됩니다.
  6. dst와 src의 주소가 겹치는 경우 memmove함수를 이용하는 것이 좀 더 안전합니다.
  7. 내부적으로 반복문을 돌때마다 ‘\0’문자인지 확인하는 strcpy함수보다 속도면에서 우수합니다.(주어진 size만큼 확인없이 때려박는 것이 가능)

5️⃣ 코드예시(특이케이스)


1. < size인자를 잘못 입력한 경우(특징2) >

#include <stdio.h>
#include <string.h>

int main(void)
{
	int num[20] = { 0,};							// 원소를 모두 0으로 초기화
	int temp[] = {1, 2, 65281 , 4, 5, 6, 7, 8, 9};    // 원소 9개
	
	memcpy(num, temp, 9);							// temp의 원소갯수 9를 넣어줌
	for (int i = 0; num[i]; i++)
		printf("%d ", num[i]);
}
/* ---결과--- */
1 2 1
  • 기대값: 1 2 257 4 5 6 7 8 9
  • 출력값: 1 2 1
  • 원인: 3번째 인자인 size에 9 * sizeof(int) 대신 실수로 9를 입력
  • 이유: int의 크기는 4byte이므로 3개의 원소만 복사됩니다.
    3번째 원소는 4byte중 1byte만 복사되어 저장됩니다. 리틀 엔디언에서는 (int)65281의 메모리가 01 ff 00 00로 저장되어 있기 때문에 앞의 1바이트인 01만 복사하여 저장됐습니다.
< 3번째 원소(65281)의 실제메모리 >
 리틀 엔디언빅 엔디언
(int)6528101 ff 00 0000 00 ff 01
4byte단위(int)로앞에서 부터 스텍에 쌓임뒤에서 부터 스텍에 쌓임
  • 결론: 예상치 못한 결과를 방지하기 위해서는 memcpy함수 인자들의 자료형을 통일 시켜야되며 size인자 또한 갯수 * sizeof(자료형)으로 입력하는 습관을 가지는 것이 좋습니다.

2. < memcpy함수로 2차원배열 복사할 경우(특징3) >

2차원배열의 각원소의 주소가 연속적으로 존재하기 때문에 단순히 메모리를 복사하는 함수인 memcpy함수로 다음과 같이 복사가 가능합니다.

(1)< 2차원배열에 2차원배열 원소 복사하기 >
int main(void)
{
	char word[8][4] = { 0, };                          //  0으로 초기화
	char temp[7][4] = { "Mon", "Thu", "Wed", "Thu", "Fri", "Sat", "Sun"};

	memcpy(word, temp, sizeof(temp));

	for(int i = 0; i < 8; i++)
		printf("%s ", word[i]);
	printf("\n");
}
/*---출력---*/
Mon Thu Wed Thu Fri Sat Sun 

< 주의할 점 >

  • 2차원배열 의 두번째 메모리의 크기를 통일해 주지않으면 예상치 못한 결과가 나오게 됩니다.
  • 3번째 인자인 size값으로 복사할배열의 메모리크기를 넣어줘야합니다.
    sizeof(temp)와 같이 sizeof(복사할 배열이름)이 안전합니다.
  • 복사할 크기가 dst보다 커지지 않도록 조심해야 합니다.
(2)< 1차원배열에 2차원배열 원소 복사하기 >
int main(void)
{
	char word[30] = { 0, };                    // 초기화
	char temp[7][4] = { "Mon ", "Thu ", "Wed ", "Thu ", "Fri ", "Sat ", "Sun "}; //원소크기를 4로 딱맞춰 '\0'문자를 없앰

	memcpy(word, temp, sizeof(temp));
	word[sizeof(temp)] = '\0';               //문장의 끝을 알리기 위해 '\0'을 대입
	printf("%s\n", word);
}
/*---출력---*/
Mon Thu Wed Thu Fri Sat Sun

< 주의할 점 >

  • ‘\0’문자까지도 복사됨을 생각하고 복사할 2차원배열의 원소를 생각해야 합니다.
    만약 복사할 2차원배열의 원소를 임의로 수정할 수 없는 경우 다음과 같은 코드를 memcpy함수호출코드 다음줄에 넣으면 될 것 같습니다.
    int first_mem = 7;                // temp배열의 원소갯수
    int second_mem = 4;               // temp배열의 각원소크기
    for (int i = 1; i <= first_mem; i++)
      word[second_mem * i - 1] = ' ';    // 각원소의 '\0'문자를 공백문자로 치환
    




© 2021.02. by kirim

Powered by kkrim