52.2 구조체와 메모리 복사하기

매번 내용이 같은 구조체를 만들려면 상당히 번거롭습니다. 또는 이미 생성하여 값을 저장한 구조체나 메모리를 다른 곳에 복사할 경우가 자주 있습니다. 이때는 memcpy 함수를 사용하여 메모리의 내용을 다른 곳으로 복사할 수 있으며 함수 이름은 memory copy에서 따왔습니다(string.h 헤더 파일에 선언되어 있습니다).

  • memcpy(목적지포인터, 원본포인터, 크기);
    • void *memcpy(void *_Dst, void const *_Src, size_t _Size);
    • 목적지 포인터를 반환

다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.

struct_variable_memory_copy.c

#include <stdio.h>
#include <string.h>    // memcpy 함수가 선언된 헤더 파일

struct Point2D {
    int x;
    int y;
};

int main()
{
    struct Point2D p1;
    struct Point2D p2;

    p1.x = 10;    // p1의 멤버에만 값 저장
    p1.y = 20;    // p1의 멤버에만 값 저장

    memcpy(&p2, &p1, sizeof(struct Point2D));    // Point2D 구조체 크기만큼 p1의 내용을 p2로 복사

    printf("%d %d\n", p2.x, p2.y);    // 10 20: p1의 내용을 p2로 복사했으므로 10 20

    return 0;
}

실행 결과

10 20

먼저 구조체 변수 p1, p2를 선언하고 p1의 멤버에만 값을 저장했습니다. 그러므로 p2에는 저장된 값이 없겠죠?

이제 memcpy 함수를 사용하여 p1의 내용을 p2에 복사합니다. 여기서 &p1, &p2와 같이 구조체 변수 앞에 주소 연산자 &를 사용하여 변수의 메모리 주소를 구해서 넣어줍니다. 마지막에는 복사할 크기를 넣어주는데 여기서는 구조체 크기를 구해서 넣어줍니다.

//      ↓ 목적지 포인터
memcpy(&p2, &p1, sizeof(struct Point2D));    // Point2D 구조체 크기만큼 p1의 내용을 p2로 복사
//           ↑ 원본 포인터

memcpy 함수에서 목적지 포인터와 원본 포인터가 헷갈릴 수 있습니다. 반드시 앞쪽이 목적지, 뒤쪽이 원본이라는 점을 기억하세요(← 방향).

printf 함수로 p2의 각 멤버를 출력해보면 p1의 멤버에 저장했던 10 20이 나옵니다.

그림 52‑3 구조체 변수에서 구조체 변수로 내용 복사

이번에는 malloc 함수로 할당한 동적 메모리끼리 복사하는 방법입니다.

struct_pointer_memory_copy.c

#include <stdio.h>
#include <stdlib.h>    // malloc, free 함수가 선언된 헤더 파일
#include <string.h>    // memcpy 함수가 선언된 헤더 파일

struct Point2D {
    int x;
    int y;
};

int main()
{
    struct Point2D *p1 = malloc(sizeof(struct Point2D));
    struct Point2D *p2 = malloc(sizeof(struct Point2D));

    p1->x = 10;    // p1의 멤버에만 값 저장
    p1->y = 20;    // p1의 멤버에만 값 저장

    memcpy(p2, p1, sizeof(struct Point2D));    // Point2D 구조체 크기만큼 p1의 내용을 p2로 복사

    printf("%d %d\n", p2->x, p2->y);    // 10 20: p1의 내용을 p2로 복사했으므로 10 20

    free(p2);
    free(p1);

    return 0;
}

실행 결과

10 20

먼저 구조체 포인터 p1, p2를 선언하고 메모리를 할당한 뒤 p1의 멤버에만 값을 저장했습니다. 그러므로 p2에는 저장된 값이 없습니다.

이제 memcpy 함수를 사용하여 p1의 내용을 p2에 복사합니다. 여기서 p1p2는 메모리 주소를 담고 있는 포인터이므로 &를 사용하지 않고 그대로 넣어줍니다.

//     ↓ 목적지 포인터
memcpy(p2, p1, sizeof(struct Point2D));    // Point2D 구조체 크기만큼 p1의 내용을 p2로 복사
//         ↑ 원본 포인터

printf 함수로 p2의 각 멤버를 출력해보면 p1의 멤버에 저장했던 10 20이 나옵니다.

그림 52‑4 동적 메모리에서 동적 메모리로 내용 복사

지금까지 구조체 변수에서 구조체 변수로, 동적 메모리에서 동적 메모리로 내용을 복사했지만 구조체 변수에서 동적 메모리로, 동적 메모리에서 구조체 변수로 내용 복사도 할 수 있습니다.

struct Point2D p1;
struct Point2D *p2 = malloc(sizeof(struct Point2D));

memcpy(p2, &p1, sizeof(struct Point2D));    // 구조체 변수에서 동적 메모리로 복사
struct Point2D *p1 = malloc(sizeof(struct Point2D));
struct Point2D p2;

memcpy(&p2, p1, sizeof(struct Point2D));    // 동적 메모리에서 구조체 변수로 복사

지금까지 구조체와 메모리 함수에 대해 알아보았습니다. 실제로 구조체는 포인터로 선언하고 메모리를 할당한 뒤 memset, memcpy 함수를 자주 사용하므로 눈에 익혀두는 것이 좋습니다.