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