본문 바로가기
언어/C++

[C++ 기본 공부정리] 6. 포인터(pointer)

by 민-Zero 2019. 11. 22.

공부 내용을 정리하는 목적 이므로 참고용으로만 읽어 주시기 바랍니다.

틀린 부분에 대한 지적은 감사합니다.

포인터를 이해하기 위해 주소 값이 무엇인지 알아야 한다.

변수 등에서 어떤 데이터를 저장하기 위해 메모리에서 저장할 공간을 할당받는다. 그럼 이 데이터를 찾아서 호출하기 위해서는 이 데이터가 메모리 어느 부분에 저장되어 있는지를 알아야 한다. 이때 데이터가 저장된 메모리의 시작 주소를 주소 값이라고 한다.

예를 들어 int형 데이터를 저장하기 위해 변수를 하나 선언했다면 4 bytes 크기로 메모리에 공간이 할당된다.

주소: 0x0001 / 변수로 할당 받은곳
주소: 0x0002 / 변수로 할당 받은곳
주소: 0x0003 / 변수로 할당 받은곳
주소: 0x0004 / 변수로 할당 받은곳
주소: 0x0005 
주소: 0x0006
주소: .....

이런 식으로 총 4 bytes크기의 공간이 할당되고 이 공간의 시작 주소인 0x0001이 해당 변수를 가리키는 주소 값이 된다.

 

1. 포인터(pointer)

포인터는 변수이다. 일반 데이터를 저장하는 변수가 아닌 메모리의 주소 값을 저장하는 변수이다.

예를 들어 int형 변수에 10이란 값이 저장되어 있고 변수의 메모리 시작 주소가 0x0010이라고 하자. 그럼 포인터 변수를 하나 선언하여 위의 int형 변수를 저장한다면 10이란 값이 아닌 0x0010이라는 시작 주소를 저장한다.

실제 메모리의 위치는 아니지만 이런 식으로 포인터 변수는 int변수의 시작 주소를 저장해 저 변수를 가리키고 있다고 생각하면 된다. 또한 이 포인터도 변수기 때문에 메모리 어딘가에 저장되기 때문에 포인터도 주소 값을 가지고 있다.

 

문법)

타입 변수명 = 데이터;   일반 변수 선언

타입* 변수명 = &일반 변수명;    가리키고자 하는 변수명을 입력

 

포인터 변수 pa에 a변수의 주소 값을 저장해 a를 가리키고 있다. 

 

2. 포인터 연산자

포인터 변수를 이용하면서 2가지의 연산자가 사용되었다. & 과 *이다. 여기서 &는 비트 AND연산자가 아닌 주소 연산자이고 *은 곱하기가 아닌 참조 연산자이다.

 

주소 연산자 &는 변수 이름 앞에 사용하여 해당 변수의 메모리 주소 값을 반환한다.

참조 연산자 *는 포인터의 이름이나 주소 앞에 사용하며  해당 주소를 참조하여 주소에 저장되어 있는 값을 반환한다.

&는 a변수가 할당된 메모리의 시작 주소를 반환하는 모습이고 포인터 pa에 a의 주소를 저장하였으므로 *을 이용해 출력하게 되면 a변수의 값이 반환되며 a의 주소 자체를 이용하여도 가능하다.

그리고 포인터는 할당하려는 변수의 자료형과 일치해야 한다. 이유는 역참 조시 가리키는 내용을 해석하기 위해서 이다. 일치시키지 않을 경우 역참조 될 때 어떤 자료형으로 해석해야 할지 알지 못하기 때문이다.

 

또한 다른 형태의 변수를 가리키는 포인터 변수들은 모두 같은 크기로 나오는데 이유는 포인터 변수가 메모리에서 변수의 위치를 나타내는 주소를 다루는 변수이므로, 그 크기는 변수의 타입에 의해 결정되지 않는다. 메모리 주소에 접근하는데 필요한 비트수는 일정하며 CPU에 따라 결정된다.

 

3. 이중 포인터

이중 포인터도 존재한다. 이는 포인터를 가리키는 포인터라는 뜻으로 이중포인터 변수 ppt 안에는 포인터 변수 pt의 주소 &pt와 같은 값이 들어 있는 것을 확인할 수 있다. 따라서 **ppt는 2번 참조하여 p의 값 10이 나오게 되는 것인데 첫 참조에 ppt가 가진 주소의 값의 데이터를 찾아가게 되어 p의 주소를 반환 거기서 한 번 더 참조하므로 p의 주소를 참조하게 되어 p의 데이터 값이 반환되는 것이다.

즉, **ppt=>*(*ppt) => *(pt의 주소) => p의 값으로 이해하면 된다.

 

※주의

포인터를 역참조 하는 것은 포인터에 저장된 메모리의 주소를 이용하여 해당 주소의 메모리에 있는 내용을 반환하는 것이다. 따라서 우리가 작성한 프로그램이 운영체제에서 할당하지 않은 메모리를 임의로 참조하려 할 경우 보안상의 이유로 운영체제에서 프로그램을 중단시킨다.

 

운영체제에서 메모리를 할당받아 참조할 경우 프로그램 정상 종료 코드인 0 코드로 종료되는 모습을 확인할 수 있다.

운영체제에서 아무런 메모리를 할당받지 않고 임의의 메모리를 참조하려고 할 경우 정상 종료 코드인 0이 아닌 알 수 없는 코드로 프로그램이 종료되는 모습을 확인할 수 있다.

 

4. 포인터 연산

1) 포인터는 주소 값을 저장하는 변수이기 때문에 포인터끼리의 사칙 연산중 뺄셈을 제외한 나머지는 의미가 없다.

2) 뺄셈의 경우 두 포인터 사이의 거리를 나타낸다.

3) 포인터에 정수 값을 더하거나 빼는 것은 가능하지만 실수 값은 안된다.

데이터 타입별로 연산 후 가리키는 주소의 증가량이 다르다. 이는 각 데이터가 저장되는 크기가 다르기 때문에 할당되는 메모리 크기의 차이 때문에 그렇다. int의 경우를 보면 +1을 하면 1만큼 증가하는 게 아니라 int형의 크기인 4만큼 증가하고 double의 경우 8만큼 증가하는 것을 확인할 수 있다.

 

5. 포인터와 배열

앞서 배열에서 배열의 이름만 호출할 경우 배열의 시작 주소가 반환되는 것을 정리했다. 배열 이름만 이용하여 값을 변경할 수 없는 것을 빼면 주소 값을 저장하는 변수인 포인터와 같다. 따라서 배열을 포인터처럼 이용할 수 있다.

따라서 배 열명을 이용하여 출력하는 것처럼 포인터에 []를 붙여 출력하면 배 열명과 동일한 결과를 얻음을 알 수 있다.

포인터를 배열처럼 이용하는 것이 가능하므로 배열을 포인터처럼 이용하는 것 또한 가능하다.

배 열명으로 시작 주소 값을 호출하여 참조 연산자를 붙여 그 값을 호출한 결과 배열에 저장된 값이 호출되며, 배열의 연산에서 배열에 덧셈을 하는 것은 해당 데이터형의 크기만큼 증가되므로 배열의 다음 요소의 시작 주소를 가리키게 되어 *를 이용할 경우 값이 출력되는 것을 알 수 있다.

댓글