본문 바로가기

Programming/C++ 2

[C++] 4장. 복합 데이터형 - (7) 포인터와 메모리 해제

반응형
  • 데이터를 저장할 때 프로그램이 알아야 하는 것
    1. 어디에 저장되는가?
    2. 어떤값이 저장되는가?
    3. 어떤 종류의 정보인가?

      ->변수 / 포인터를 이용
  • 포인터
    • 값의 주소를 저장하는 변수
    • 메모리 관리에 필수적이다
    • 포인터의 이름이 주소를 나타낸다
  • &
    • 참조연산자
    • 주소연산자
      • 변수 앞에 붙이면 그 변수의 주소
  • *
    • 역참조 연산자
      • 포인터 이름 앞에 붙이면 그 주소에 저장되어 있는 값

 

& 연산자로 주소를 알아낸다

int donuts = 6;
double cups = 4.5;

cout<< "donuts의 값 = " << donuts;
cout<< ", donuts의 주소 = " << &donuts <<endl;

cout<< "cups의 값 = " << cups;
cout<< ", cups의 주소 = " << &cups <<endl;

-> 16진수 표기를 사용한 주소값 출력

-> 4바이트 차이 [int 이므로]

-> 8바이트 차이 [double 이므로]

 

 


  • 포인터와 C++
    • 절차지향 [C]
      • 컴파일 시간에 결정
    • 객체지향 (OOP) [C++]
      • 실행시간에 결정
        • 상황에 대처할 수 있는 유연성

 

포인터 변수

int updates = 6;
int * p_updates;

p_updates = &updates;

-> * : 포인터 이름앞 -> 그 주소의 값

-> & : 변수 앞 -> 그 변수의 주소

-> *p_updates는 int형 변수 updates와 동등하게 취급하고 사용할 수 있다

 

=

int jumbo = 23;
int *pe = &jumbo;
jumbo
*pe
&jumbo
pe
값 23 주소 0x2ac8
=> 값과 주소를 두 가지의 형태로 표현할 수 있다.
=> 어떤 형태를 쓰든 동등하게 취급한다.

 


  • 포인터 선언과 초기화
    • 포인터를 선언하는 과정
      • 포인터가 지시하는 값의 데이터형을 파악 해야한다. [주소값으로 데이터형 구분이 불가하므로]
int* p_updates;

-> p_updates : 포인터

-> * p_updates : int형 변수

-> int* p_updates : int형 포인터

 

-> int* : 하나의 데이터형

 

  • 포인터 변수 선언시 주의
    • int* p1, p2;
      • int형 포인터 p1
      • int형 변수 p2 [int형 포인터가 아니라 변수를 선언한다.]

포인터와 수

int* pt
pt = 0XB8000000;
pt = (int *) 0XBB8000000;

-> pt에 바로 주소를 넣는 경우 데이터형 불일치

-> (int *)라는 데이터형 변환자를 넣어줘야 데이터형 일치

 

-> 데이터형 변환자 : 뒤의 값이 주소값임을 명시

 

// init_ptr.cpp -- 포인터를 초기화한다
#include <iostream>
int main()
{
    using namespace std;
    int higgens = 5;
    int * pt = &higgens;

    cout << "higgens의 값 = " << higgens
         << ", higgens의 주소 = " << &higgens << endl;
    cout << "*pt의 값 = " << *pt
         << ", pt의 값 = " << pt << endl;
    return 0;
}

 

실행결과:

-> pt가 higgens의 주소로 초기화

 

포인터를 사용할 때는 *를 사용하기 전에 반드시 &로 초기화를 해야 한다.

 


  • 포인터들을 변수의 주소로 초기화
    • 프로그램 실행동안에 unnamed 메모리를 대입
    • C : malloc() / free()
    • C++ : new / delete
  • new 연산자
    • 이름이 없는 데이터의 주소를 반환한다.
      1. 필요한 데이터형을 전달
      2. 적당한 메모리를 찾아 블록 대입
      3. 주소를 반환, 주소 포인터에 대입

차이 비교

int* pn = new int;

-> pn이 데이터 객체[메모리 블록]를 지시

-> pn이 값에 접근할 수 있는 유일 경로

int higgens;
int* pt = &higgens;

-> pt, higgens 둘 다 int형 값에 접근 가능

 

기본형

typename* pointer_name = new typename;

-> 메모리 종류 지정

-> 포인터 선언

 

 

  • delete를 사용한 메모리 해제
    • delete를 이용해 new로 할당한 메모리 해제 [동일한 주소]
    • 해제한 이후에 다시 해제 불가
    • 보통의 변수로 선언한 메모리는 delete로 해제불가

// arraynew.cpp -- 배열을 위해 new 연산자 사용
#include <iostream>
int main()
{
	using namespace std;
	double * p3 = new double [3];	// double형 데이터 3개를 저장할 수
					// 있는 공간을 대입한다
	p3[0] = 0.2;			// p3을 배열 이름처럼 취급한다
	p3[1] = 0.5;
	p3[2] = 0.8;
	cout << "p3[1]은 " << p3[1] << "입니다.\n";
	p3 = p3 + 1;			// 포인터를 증가시킨다
	cout << "이제는 p3[0]이 " << p3[0] << "이고, ";
	cout << "p3[1]이 " << p3[1] << "입니다.\n";
	p3 = p3 - 1;			// 다시 시작 위치를 지시한다
	delete [] p3;			// 배열 메모리를 해제한다
	return 0;
}

-> p3 = p3+1;

-> p3가 포인터면 가능 [값 변경 가능]

-> p3가 배열이면 불가능 [값 변경 불가능]

 

  • new를 사용한 동적 배열의 생성
    1. 선언을 이용해 배열을 생성
      • 컴파일 시간에 메모리에 대입 
        • 정적 바인딩 
          • 사용이 되든 안되든 정해둔 배열의 크기로 항상 메모리를 차지
    2. new를 사용해 배열을 생성
      • 실행 시간에 생성여부 결정
        • 동적 바인딩
          • 실행되든 동안에 배열의 크기와 생성여부 결정

배열의 생성

int* psome = new int[10];

-> 배열 원소의 데이터형 : int 

-> 배열 원소의 개수 : [10] 

 

=> new 연산자는 그 블록의 첫번째 원소의 주소를 반환한다.

 

 

배열의 해제

delete [] psome;

 

동적 배열을 위한 메모리 대입하고, 그 배열의 시작 주소를 포인터에 대입

type_name* pointer_name = new type_name [num_elements];

 

동적 배열의 사용

int* psome = new int [10];

-> psome[0], psome[1] 등으로 접근

반응형