DNF LOVE

C/C++ Pointer 개념과 실행 예제 본문

Programming/C++

C/C++ Pointer 개념과 실행 예제

botho 2019. 10. 22. 20:57
반응형

C와 C++의 가장 큰 장점이자 특징은 무엇일까? 그것은 바로 메모리 관리를 개발자가 직접한다는 것이다.

JAVA와 C#같은 경우는 각각 JVM, .Net 의 VM에 의해 GC가 관리된다.

그러나 C와 C++은 각각 malloc / new 연산자를 통해 메모리를 할당하고 delete를 사용하여 메모리를 해제한다. 이를 동적할당이라 한다.

프로그램이 변수를 인스턴스화(메모리에 데이터를 할당하여 사용할 수 있도록 함) 할 때 사용 가능한 메모리 주소가 변수에 자동으로 할당되고(OS의 몫) 변수에 할당된 값은 이 메모리 주소에 저장된다.

메모리 관리를 개발자가 직접하기 때문에, JAVA와 c#과 다르게 C와 C++에서는 메모리에 직접 다가가는 포인터 개념을 사용한다.

포인터란? 어떠한 값을 저장하는 것이 아닌 어떠한 값의 주소(=인스턴스화 할 때 할당되는 메모리 주소)를 저장하는 것이다.

포인터를 사용하는 이유는 다양하다.

  • 포인터는 배열을 반복할 때 사용할 수 있다 - 배열 인덱스 대신 사용이 가능하다.
  • C++에서 동적으로 메모리를 할당하는 방법이다.
  • 데이터를 복사하지 않고도 많은 양의 데이터를 함수에 전달할 수 있다.
  • 상속을 다룰 때 다형성을 달성하기 위해 사용한다.
  • 연결리스트 및 트리와 같은 자료구조에서 체인을 형성하는데 사용할 수 있다.

아래부터는 포인터 개념에 사용되는 연산자(&, *)를 사용하여 다양한 예시를 보여주도록 하겠다.


1. 주소연산자(The Address-of Operator, &) : 주소연산자를 사용하면 변수에 할당된 메모리 주소를 확인할 수 있다.

#include <iostream>

using namespace std;

int main()
{
	int value = 10;
	cout << value << endl;
	cout << &value << endl;

	return 0;
}

여기서 10은 정수형 변수 value 가 갖고있는 데이터 값이며 002AF8B4는 OS가 value게 할당해준 메모리 주소를 가리킨다.

이 메모리 주소를 참고하고자 할 때 주소 연산자&를 사용한다.


2. 역참조 연산자(The dereference operator, *) : 역참조 연산자를 사용하면 특정 주소에서 값에 접근할 수 있다.

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

using namespace std;

int main()
{
	int value = 10;
	cout << value << endl;
	cout << &value << endl;
//	cout << *value << endl;
	cout << *&value << endl;

	return 0;
}

일반 변수로 선언한 value는 역참조 연산자 혼자서는 사용을 못하고, *&value로 주소연산자와 함께 써야 한다. 특정 주소(value의 주소)를 참조하여 데이터 값을 찾아간다는 뜻이기 때문이다.

** 역참조 연산자는 단항 연산자(부호 연산자, 증감 연산자, 형변환 연산자와 같은 피 연산자가 1개인 연산자)이며, 곱셈 연산자는 이항 연산자(피 연산자가 2개인 연산자)이다.


3. 포인터(Pointer) : 어떠한 값을 저장하는 것이 아닌 메모리 주소를 저장하는 '변수'이다.

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

using namespace std;

int main()
{
	int value = 10;
	int *ptr;	// 포인터 변수 ptr 선언
	ptr = &value;	// 포인터 변수 ptr에 value의 메모리 값으로 초기화한다.
	// 포인터 변수는 데이터 값이 아니라 메모리 값을 참조하여 넣는다.

	cout << value << endl;
	cout << &value << endl;
	cout << *ptr << endl;
	cout << ptr << endl;
	cout << &ptr << endl;

	return 0;
}

value : 각각 정수형 변수 value의 데이터 10을 출력한다.

&value : 정수형 변수 value가 갖고있는 메모리 주소를 출력한다.

*ptr : value의 메모리 주소를 갖고 있는 포인터 변수 ptr을 역참조하여 ptr이 가리키는 데이터 값을 출력한다.(ptr의 데이터 값 = value의 메모리 주소, 이 value의 메모리 주소를 역참조 = value가 가리키는 값)

ptr : ptr이 담고 있는 value 메모리 주소를 출력한다.

&ptr : value의 메모리 주소와 ptr의 메모리 주소는 독립적이기 때문에 ptr이 할당받은 메모리 주소값을 출력한다.

** 단, C++에서는 포인터에 리터럴 메모리 주소를 직접 할당할 수는 없다.

#include <iostream>

using namespace std;

int main()
{
	int value = 10;
	int *ptr;

	ptr = &value;
	*ptr = 5;

	cout << value << endl;
	cout << *ptr << endl;

	return 0;
}

- 포인터 변수 ptr에 value 메모리 값을 참조하도록 하였다.

이때, ptr를 역참조하여(ptr이 가리키는 메모리 값의 데이터 값 참조) 5로 바꾸어주었다.

그러고 value와 *ptr은 둘 다 전부 값이 5로 출력되었다.

이는 왜 이렇게 되는 것일까? 바로 Call By Reference 성질에 의한 것이다. 

프로그래밍 언어를 공부하면서 기본적으로 배운 내용 중 [call by reference], [call by value]일 것이다. 이들의 차이점과 설명은 다음 포스터에서 마저 설명하도록 하겠다.

반응형