Programming/C C++
 포인터

1) 포인터 기본

포인터라는 것은 특정 값을 가지는 일반적인 변수와는 다르게 메모리 주소값을 가지는 변수입니다.

포인터는 다음 형식으로 선언됩니다.
자료형* 포인터변수명;
예를 들어 
int a = 10;
int* b = &a;
int형 변수 a에 10을 담으면 이 10이라는 값은 메모리의 특정 주소에 기억될 것입니다. a라는 변수의 명칭은 그저 이 주소에 대한 별칭에 불과합니다.

그런뒤 int형 변수의 주소값을 담을 수 있는 b라는 포인터주소를 선언하고 &a로 a의 주소값을 대입하면 b는 a의 값이 아닌 주소를 가지게 됩니다. 여기서 &연산자는 피연산자에 해당하는 변수의 주소값을 가져오는 연산자입니다.

만약 이 상태에서
*b = 20;

라고 하면 a는 20값을 갖게 됩니다. 포인터 변수앞에 *은 포인터가 가리키고 있는 주소에 있는 실제 값을 가져오거나 설정할 수 있도록 하는데 이 값은 본래 a변수값인 10을 갖고 있다가 위 처리로 인해 20으로 바뀌게 됩니다.

2) new와 delete

일반적으로 C++에서 변수를 선언하면 이 변수의 데이터는 스택(stack)이라는 메모리 공간에 저장됩니다. 스택은 아래서 부터 위로 차곡차곡 데이터를 쌓는 형태로서 반대로 제거하는 경우에는 위에서 부터 아래로 하나씩 제거하는 형태로 동작합니다.

저장과 제거가 자동적이므로 개발자가 임의로 특정 변수에 대한 생명주기를 관리할 수 없습니다. 하지만 힙(heap)이라고 다른 메모리 구조에 변수를 저장할 수 있다면 스택과 다르게 임의로 생성된 변수를 제거할 수 있습니다.

int* b = new int;
위 구문에서 new는 피연산자로 지정된 자료형이나 클래스등의 생성자크기의 메모리 공간을 힙에 할당하고 그 주소를 반환하도록 합니다. 따라서 대입되는 변수는 포인터여야 합니다.
int* b = new int(30);
필요하다면 위에서 처럼 미리 특정 값을 초기화 할 수도 있습니다. 클래스도 같은 방법으로 힙에 할당할 수 있는데 예를 들어 아래와 같은 클래스가 존재하는 경우
class MyClass
{
public:
    int plus(int a, int b);
};

int MyClass::plus(int a, int b)
{
    return a + b;
}
이 클래스를 힙에 할당하려면
MyClass* p = new MyClass;
위와 같이 new연산자를 사용하면 됩니다. 이렇게 되면 기본생성자를 통해 힙에 할당하는데 별도로 정의한 생성자가 존재하고 이 생성자를 호출하고자 하는 경우 괄호를 열고 필요한 매개변수만 던져주면 됩니다.
MyClass* p = new MyClass(30);

생성된 포인터가 변수포인터의 경우에는 이미 말씀드린대로 *연산자로 값을 대입하거나 가져오면 되는데 클래스의 경우에는 해당 클래스의 멤버에 접근하려면 -> 연산자를 사용해야 합니다.

MyClass* p = new MyClass;
int result = p->plus(10, 20); //MyClass의 plus함수 호출
new연산자는 힙에 생성을 하는 연산자지만 delete는 반대로 해당 포인터가 사용중인 메모리를 해제하는 연산자입니다.
MyClass* p = new MyClass;
delete p; //new를 통해 생성된 포인터변수를 메모리에서 해제
만약 delete를 적절히 사용하지 않으면 메모리를 낭비할 수 있게 되므로 주의가 필요합니다.

3) 함수포인터

함수도 변수처럼 메모리에 위치하므로 포인터로 함수를 가리키는 것 또한 가능합니다. 함수포인터는 아래와 같은 형식으로 사용됩니다.
반환형 (*포인터명)(매개변수);
함수포인터는 함수의 반환형과 필요한 매개변수에 대한 정보를 필요로 합니다. 포인터를 통해 함수를 호출하는 경우에도 필요한 매개변수를 넘기고 값을 받아야 하기 때문입니다. 예를 들어 다음과 같은 함수가 있는 경우
int plus(int a, int b)
{
    return a + b;
}
이 함수에 대한 포인터를 선언하려면
int (*p)(int a, int b);
라고 한 뒤에
p = +
함수에 대한 주소를 &연산자를 사용하여 가져와 포인터에 대입해주기만 하면 됩니다. 그리고 다음과 같이 포인터를 통해 일반적으로 함수를 호출할때 처럼 사용하면 됩니다.
int result = p(10, 20);
재미있는 것이 반환형식과 매개변수 형식만 맞으면(같다면) 전혀 다른 함수의 주소도 대입하여 사용할 수 있다는 것입니다.
int minus(int a, int b)
{
    return a - b;
}
위 함수는 plus함수와는 다른 동작을 하지만 plus함수의 반환형식과 매개변수의 갯수및 형식이 같습니다. 따라서 위에서 생성한 p 포인터에 주소를 대입하여 사용하는 것이 가능합니다.
int (*p)(int a, int b);

p = + //plus함수 포인터
int result_plus = p(10, 20); //plus함수 호출

p = − //minus함수 포인터
int result_minus = p(30, 10); //minus함수 호출

이제 까지의 함수 포인터는 전역적으로 정의된 함수만을 대상으로 하였습니다. 만일 포인터로 대입하고자 하는 함수가 클래스 내부에 있다면 함수포인터 생성시 '*포인터명'앞에 결합연산자(::)를 사용하여 클래스를 지정해 줘야 합니다.

반환형 (클래스::*포인터명)(매개변수);

그런데 클래스도 함수형식의 한 부분이기 때문에 반환형이나 매개변수등 함수형식이 같다해도 하나의 함수포인터로 여러클래스의 멤버함수를 가리킬 수는 없습니다.(물론 형식이 같다면 하나의 클래스에 대한 여러 멤버함수를 대입할 수는 있습니다.)

int (MyClass::*p)(int a, int b);

MyClass myclass;

p = &MyClass::plus;

int result = (myclass.*p)(10, 20);
클래스에 대한 멤버함수 포인터를 사용하는 방법은 먼저 MyClass의 함수포인터를 선언하고 MyClass의 plus함수에 대한 주소값을 포인터에 대입합니다. 그리고 실제 포인터를 사용해 함수를 호출할때는 MyClass의 객체인 myclass에서 호출해야합니다.

멤버함수는 객체별로 메모리에 생성되지 않고 클래스 레벨에서 메모리에 존재하는데(비정적멤버함수와 정적멤버함수 모두 정적메모리에 위치) 함수포인터가 객체의 멤버함수를 호출할 수 있게 함으로서 클래스 레벨에 있는 멤버함수의 메모리에 있는 코드를 실행하도록 해야하기 때문입니다.

 참조

참조를 사용하면 실제값은 포인터가 갖는 주소값이지만 마치 일반변수처럼 포인터를 사용할 수 있습니다.
int a = 10;
int& b = a;
위에서 사용된 &연산자는 참조연산자로서 변수 b는 변수 a의 int형 주소값을 갖는 참조형 변수에 해당합니다. 이 참조형 변수가 생성되면 일반 변수처럼 값을 가져오거나 대입할 수 있습니다.
b = 20;
물론 위와 같이 하면 변수 a의 값도 같이 바뀌기 때문에 참조형 변수도 포인터성격을 갖고 있다고 볼 수 있습니다.

'Programming > C C++' 카테고리의 다른 글

[Visual C++] 클래스(Class) 의 기본  (5) 2012.10.08
[Visual C++] 연산자 오버로딩(Operator Overloading)  (0) 2012.10.05
[Visual C++] 포인터와 참조  (2) 2012.07.12
[Visual C++] typedef  (0) 2012.07.06
[Visual C++] 구조체  (0) 2012.07.05
[Visual C++] 함수  (0) 2012.07.04
2 0