동적 할당 #1

  • 코드 영역 : 실행할 코드가 저장되는 영역
  • 데이터 영역 : 전역(global) / 정적(static) 변수가 저장되는 영역
  • 스택 영역 : 지역 변수 / 매개 변수가 저장되는 영역
  • 힙 영역 : 동적 할당이 이루어지는 영역

스택 영역은 함수가 끝나면 정리되는 불안정한 메모리
따라서 잠시 함수에 매개변수를 넘긴다던가 하는 용도로 적합함
데이터 영역은 프로그램이 실행되는 도중 무조건 사용됨

따라서 필요할 때 사용하고, 필요없으면 반납할 수 있는 힙 영역이 유용하게 쓰임

  • 힙 영역에서 동적 할당이 이루어짐
  • malloc, free, new, delete, new[], delete[]

프로그램의 구동 영역에는 유저 영역과 커널 영역이 있음

  • 커널 영역 : Windows 등의 핵심 코드
  • 유저 영역 : 메모장, 웹브라우저 등의 프로그램
    유저 영역에서 프로그램이 운영체제에서 제공하는 API를 호출하면 커널 영역에서 메모리를 할당하여 건내줌

C++에서는 기본적으로 CRT(C RunTime library)의 힙 관리자를 통해 힙 영역을 사용함
단, 사용자가 직접 API를 통해 힙을 생성하고 관리하는 것도 가능함(MMORPG 서버 메모리 풀링 등)

size_ttypedef로 설정되어있으며 그 크기는 운영체제마다 다름


동적 할당 #2

malloc : 할당할 메모리 크기만큼 메모리를 할당 후 시작 주소를 가리키는 포인터를 반환함

  • void* pointer = malloc(1000)의 형태로 사용
    • void*형이란 타입이 지정되어있지 않아 어떤 타입으로든 변환될 수 있는 타입
    • 메모리 낭비를 막기 위해 malloc(1000) 대신 malloc(sizeof(Monster))의 형태로 사용할 수 있음
  • 메모리 부족시 NULL을 반환

free : malloc(또는 calloc, realloc 등)을 통해 할당된 메모리 영역을 해제

  • free(pointer)의 형태로 사용
  • 메모리 할당시 반드시 해제를 해주어야함

할당한 메모리의 범위를 벗어난 경우 Heap Overflow 에러가 발생
메모리를 해제하지 않을시 메모리 누수 현상이 발생
메모리를 중복해서 해제했을시 Double Free 에러가 발생
메모리 해제 후에도 해당 주소에 접근하여 조작할 경우 Use-After-Free 문제가 발생


동적 할당 #3

malloc / free는 함수
new / delete는 연산자, C++에 추가됨

  • Monster* m2 = new Monster;, delete m2의 형태로 사용
  • 전반적인 역할과 제약은 malloc, free와 같음

배열 할당시에는 new [] / delete []를 사용

  • Monster* m3 = new Monster[5];, delete [] m3의 형태로 사용

사용 편의성은 new / delete가 뛰어남
단, 타입에 상관없이 특정한 크기의 메모리 영역을 할당받고 싶다면 malloc / free를 사용해야할 수도 있음
new / delete는 생성타입이 클래스일 경우 생성자와 소멸자를 호출함!


타입 변환 #1

값 타입 변환 : 의미를 유지하기 위해 원본 객체와 다른 비트열을 재구성함

int a = 123456789;
float b = (float)a;

int형으로 저장된 a는 2의 보수로 메모리에 123456789의 16진수형이 저장됨
afloat형으로 변환한 b는 부동소수점을 표현하기 위해 완전히 다른 비트로 이루어진 비트열로 재구성됨

참조 타입 변환 : 비트열을 재구성하지 않고 관점만 바꿈
거의 쓸일이 없으나 포인터 타입 변환은 참조 타입 변환과 동일한 룰을 따름

int a = 123456789;
float b = (float&)a;

afloat형으로 변환한 ba와 동일한 비트열로 메모리에 저장됨
따라서 출력시의 값은 a와 달라질 수 있음

안전한 변환 : 의미가 항상 100% 완전하게 일치하는 경우

  • 즉, 같은 타입이면서 크기만 더 큰 타입으로 변환(업캐스팅)하는 것은 문제가 발생하지 않음
    불안전한 변환 : 의미가 항상 100% 일치한다고 보장하지 못하는 경우
  • 타입이 다른 변환 또는 같은 타입이지만 크기가 더 작은 타입으로 변환(다운캐스팅)하는 것은 데이터 손실 등의 문제가 발생함

암시적 변환 : 이미알려진 타입 변환 규칙에 따라 컴파일러가 자동으로 타입을 변환

  • int a = 123; float b = a;와 같은 경우 a는 암시적으로 float형으로 바뀜
    명시적 변환 : 사용자가 명시적으로 형 변환을 지시

타입 변환 #2

class Knight
{
	public:
		int _hp = 10;
};

class Dog
{
	public:
		Dog(const Knight& knight) // 타입 변환 생성자
		{
			_age = knight._hp;
		}
		operator Knight() // 타입 변환 연산자
		{
			return (Kngiht)(*this);
		}
};

int main()
{
	Knight = knight;
	Dog dog = (Dog)knight;
	Knight knight2 = dog;
}

연관없는 클래스 사이의 값 타입 변환 : 일반적으로 불가능

  • 단, 타입 변환 생성자 또는 연산자 를 사용시 가능함
int main()
{
	Knight knight;
	Dog& dog = (Dog&)kngiht;
}

연괍없는 클래스 사이의 참조 타입 변환 : 명시적으로는 가능함

  • 참조형은 사실상(어셈블리어 수준에서) 포인터와 작동이 같으므로 주소를 가리키는 변환은 오류를 발생시키지 않음

class BullDog : public Dog
{
	public:
		bool _french;
};

int main()
{
	BullDog = bulldog;
	Dog dog = bulldog;
}

상속 관계 클래스의 값 타입 변환

  • 자식 클래스를 부모 클래스로 변환하는 것은 가능
  • 부모 클래스를 자식 클래스로 변환하는 것은 불가능
int main()
{
	Dog dog;
	BullDog& bulldog = (BullDog&)dog;

	BullDog bulldog2;
	Dog& dog2 = bulldog2;
}

상속 관계 클래스의 참조 타입 변환

  • 자식 클래스를 부모 클래스로 변환하는 것은 가능
  • 부모 클래스를 자식 클래스로 변환하는 것은 명시적 변환으로만 가능

값 타입 변환은 실제로 비트열을 바꾸며, 논리적으로 수용 가능한 변환임
참조 타입 변환은 비트열을 그대로 유지하며, 해당 비트열을 해석하는 관점만 바꿈
이 때 논리적으로 안전한 변환의 경우 암시적으로 가능하며, 논리적으로 위험한 변환의 경우 명시적 변환만 가능함