티스토리 뷰

IT/C, C++

[C/C++] 변수의 메모리 데이터를 다른 변수 메모리 주소에 복사하기 (memcpy_s)

주인장 진빼이
TIP
 
 

memcpy() 함수와 다르게 overflow safe 이슈가 존재하지 않습니다. (memcpy_s() 함수는 안전합니다)

 

 

서론

memcpy() 함수는 복사 대상 버퍼 크기를 초과하여 복사를 진행할 수 있다.

이로인해 오버플로우 발생 위험이 있어 MS(Microsoft)에서 " memcpy_s() " 함수를 공개했다.

복사 대상 버퍼 크기와 복사할 데이터의 크기를 명시함으로써 함수의 안전성이 좋아졌다.

 

memcpy_s 함수에 대해 하나씩 살펴보자.

 

prototype

함수 prototype을 보면 MBCS, WBCS별로 사용하는 함수가 다르다.

하지만 memcpy_s() 함수 하나로도 WBCS 문자열 데이터도 복사할 수 있다. (자세한건 예제를 참고하자)

errno_t memcpy_s(
   void *dest,
   size_t destSize,
   const void *src,
   size_t count
);
errno_t wmemcpy_s(
   wchar_t *dest,
   size_t destSize,
   const wchar_t *src,
   size_t count
);

 

 

메모리 블록과 메모리 데이터

 

 

매개변수와 반환값

memcpy_s() 함수는 safe memory copy 의 줄임말로, 안전하게 버퍼 간에 메모리 데이터를 복사하는 함수이다.

memcpy_s() 함수를 호출할 때 필요한 매개변수와 반환 값은 다음과 같다.

매개변수 목록 설명
void* dest destination 줄임말, 복사 대상 버퍼
size_t destSize 복사 대상의 버퍼 크기
const void *src source 줄임말, 복사할 값을 가지고 있는 버퍼
size_t count 복사할 바이트 수
필요한 헤더 - ANSI: memory.h, string.h / UNICODE: tchar.h
> memcpy_s 함수는 destSize가 명시되어 있어 오버플로우 이슈가 발생하지 않습니다.

source가 가르키는 곳에 count 크기의 메모리 데이터를 dest가 가리키는 곳에 복사하는 것이다.

(단, 대상 버퍼 크기(destSize)는 복사할 바이트 수(count)보다 작을 수 없음)

함수의 반환값은 메모리 복사가 성공적으로 이루어지면 0을 반환, 이외 errno_t 타입의 에러코드가 반환된다.

 

 

memcpy_s() 함수는 void* 타입을 매개변수로 전달할 수 있기 때문에 WBCS 지원 함수인 w_memcpy_s() 함수를 사용하지 않아도 된다.

그러므로 wmemcpy() 함수 관련 설명도 생략한다.

 

바이트 수만 잘 계산한다면 memcpy_s() 함수로도 WBCS 문자열 데이터를 복사할 수 있다.

dest에 복사된 값은 strcpy() 함수와 다르게 NULL-Terminated가 아니라는 것에 주의해야 한다.

만약 문자열을 메모리 복사하는 경우에 NULL값을 포함하여 복사하거나 복사한 문자열 맨 뒤에 NULL값을 대입시켜주도록 하자.

 

아쉽게도 동일한 영역의 메모리 복사는 불가능하다. 이럴 땐 memmove_s() 함수를 사용하도록 하자.

예제 및 그림을 통해 메모리 복사가 이루어지는 과정을 살펴보자

 

 

예제: wchar_t 문자열 멤버 변수 메모리 데이터 복사하기

다음은 stu1.name 변수의 메모리 데이터("Joy")를 stu.name[1] 주소에서 복사한 크기만큼 복사하는 예제이다.

#include "pch.h"
#include <iostream>
#include <tchar.h>

class student {
public:
  wchar_t name[6];
  int id;

  void showData()
  {
  	std::wcout << "name: " << name << ", " << "id: " << id << "\n";
  }
};

int main()
{
  student stu1 = { L"Joy", 40 };
  std::cout << "stu1: ";
  stu1.showData();

  student stu2 = { L"James", 80 };
  memcpy_s(&stu2.name[1], sizeof(stu2.name), stu1.name, (wcslen(stu1.name) * 2) + 2);

  std::cout << "stu2: ";
  stu2.showData();
  return 0;
}

stu2.name 값이 James에서 JJoy로 변경되었다..

 

 

예제에서 사용되는 메모리 복사 호출 함수 코드이다.

이 코드가 작동됨으로서 기존 변수들이 가지고 있는 메모리 데이터의 변화를 그림으로 볼 것이다.

 

wcslen에서 *2를 하는 이유는 memcpy_s는 byte를 기준으로 하기 때문이다.

WBCS는 문자를 표현하는데 2byte를 사용된다.

마지막에 +2를 한 이유는 NULL 값을 포함시켜서 복사하기 위해서이다.

memcpy_s(&stu2.name[1], sizeof(stu2.name), stu1.name, (wcslen(stu1.name) * 2) + 2);

 

 

 

아래 그림에서 stu1 변수의 메모리 블럭 및 메모리 데이터를 볼 수 있다.

stu1 변수의 메모리 블럭 및 메모리 데이터.

 

 

 

stu1.name 주소에서 8Byte만큼 복사 대상 위치에 복사하는 것이다.

중요한건, 복사할 메모리 블럭의 크기는 복사 대상 버퍼 크기를 초과할 수 없다.

중간 버퍼가 없으므로 복사 진행 중에 값이 바뀐다면 바뀌는 값이 복사될 수 있다.

stu2 변수의 메모리 구조이다.

 

 

 

마지막으로 메모리 복사가 이루어지면 stu2 메모리 블럭 및 메모리 데이터는 다음과 같이 변경될 것이다.

메모리 복사가 진행된 후, stu2 메모리 구조이다.

 

노란색으로 칠한 부분이 복사가 진행되어 메모리 데이터가 변경되었다고 볼 수 있다.

만약 "Joy" 가 아니라 "Johnson" 을 복사하려고 했다면 성공했을까 ?

> 당연히 실패할 수 밖에 없다, 복사 대상 버퍼 크기(12Byte)보다 복사할 메모리 블럭의 크기(14Byte)가 크기 때문이다.

 

메모리 복사가 진행된 후 stu2.name 변수 값을 출력하면 " JJoy "가 출력되는 것을 눈으로 볼 수 있다.

 

예제는 매우 간단한 코드로 구성되어 있으니 여러 가지 상황을 만들어서 

직접 실습을 통해 연습하기 바란다.

 

메모리 복사는 어렵지 않고 재밌는 것이다.

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/03   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함