C와 C++에서 포인터의 개념은 매우 중요하다.

따라서, 코딩을 하는 사람이라면 한번쯤 개념을 정리해놓고 두고두고 보는것도 매우 좋은 방법이다.


하지만, 포인터만 안다고 포인터와 관련된 모든 코드를 해석할 수있는 것은 아니다.

그러므로 먼저, 배열과 포인터의 관련성과 나아가 구조체와의 포인터와의 관련성 그리고 포인터함수에 대해서 고민해보고 이해하여야 한다.


아래의 글은 아주 간단한 예제와 기본적인 개념에 대해서만 언급하고 다룬다. 

따라서 빠른 이해와 사용을 목적으로 참조하기만 바란다. 




먼저 배열에 대해서 이해하자.


배열이란 무엇일까?

배열은 많은 데이터를 저장하고 처리하기에 매우 유용하다.

  배열선언의 형태 : 배열이름, 자료형, 길이정보

   ex)   int Num[10];   //길이가 10인 int자료형 1차원 배열 Num 을 의미한다.

      

            int Num[10]={1,2,3,4}; //이런경우는 선언과 동시에 초기화를 하는 방법이다.

                                           //공간은(길이) 10을 주었지만 4개만 초기화되었기에 나머지는 0으로 채워진다.

                                           //즉 1,2,3,4,0,0,0,0,0,0 의 값들이 저장된다.


1차원은 행1인 행렬을 의미한다. 그냥 수의 나열이라고 생각해도 된다.

하지만 2차원, 고차원으로 갈수록 개념을 잘 잡아야 한다. 후에 다차원 배열때 다시언급.


★배열의 위치정보(인덱스)는 0부터 시작한다.


배열을 이용해서 문자열 변수도 표현가능하다.


char str[10]="Hi hello";

//내부적인 저장은 : H i  h e l l o \0  으로 된다.

널문자는 문자의 끝이라는 의미로 들어간다. 그래서 문자열의 시작과 끝을 알수가 있다.




이제 포인터에 대해서 이야기 해보자.


포인터도 하나의 변수이다. 포인터변수는 메모리의 주소값을 저장하기 위한 변수이다.

이전에 써오던 다른 변수들은 상수값을 저장하기 위한 공간이었다면 포인터는 주소값(메모리)을 저장하기 위한 공간이다.


포인터는 2가지  Type이 존재한다.

1. 변수형태의 포인터 : 초기화 후 수정이 가능

2. 상수형태의 포인터 : 초기화 후 수정이 불가능


:: 변수포인터 ::


간단한 예)


int main(void)

{

int num=1;

int *ponum;

ponum = # // num변수의 주소 값을 포인터 변수에 저장함.


}


포인터변수의 선언형태.


자료형타입 * 변수이름;

ex) int * number;

     char * moonja;


선언시, * 연산자를 사용하는데, 해석시 이는 int형 *number 포인터를 선언한다고 보면 된다.


포인터변수의 선언형태는 보면 자료형 타입을 꼭 붙여준다. 이는 포인터변수에 저장되는 주소값은 저장되는 변수의 주소값의 처음 시작값을 의미하기 때문이다. 따라서 자료형의 크기에 따라서 포인터변수가 할당받는 공간크기가 다르며, 이는 포인터주소값의 증가폭이 자료형의 메모리크기와 동일하다는 뜻이다.


위의 int *number 포인터의 경우 참조하는 변수의 주소시작값으로 부터 4byte(int형크기)의 크기를 할당한단 의미이다.

그리고 포인터변수의 사용은 연산자*의 유무로 나눠진다. 예시는 아래와 같다.


int main(void)

{

int num=3;  //int 형 변수 num의 선언

int *ponum=# // int형 포인터변수 *ponum에 변수num의 주소시작값을 넣는다. 

                          // 혹은 *ponum변수는 변수num의 시작주소값을 가리킨다.

                         // 이는 int *ponum=nulll;

                         // ponum = # 과 동일한 의미이다. 

(*ponum)+=10;    // *pomum이 가리키는 주소값의 데이터에 +10을 한다는 의미이다.

                       // *을 붙이면 가리키는 주소값에 대한 접근, *을 안붙이면 포인터변수자체의 공간에 접근.

printf("%d", *ponum); //*ponum 이 가리키는 주소값의 데이터를 출력하라. 


}


포인터 변수는 선언시 초기화 하지 않으면, 쓰레기값으로 초기화 된다. 따라서 0; null; 로 초기화 해주면 된다.





배열의 이름은 포인터를 의미한다. 즉, 배열변수가 메모리에 할당받은 주소의 시작값을 의미한다.

따라서 배열이름을 가지고서 포인터처럼 사용이 가능하다.

단지, 포인터와 다른점이 있다면. 주소값의 변경이 불가능하다는 것이다. 배열은 데이터를 담고 포인터는 주소값을 담기때문이다.


int main(void)

{

int arr[2]={1,2};

int * ponum=arr;  // int *ponum=&arr[0]; 과 동일한 문장이다.

      ....

      printf("%d" ...)  //생략.


}


변수형태의 포인터변수는 증가 및 감소가 가능하다.

int *ptr=#

ptr++;  


와 같은 형태가 사용가능하다. 의미는 *ptr 포인터변수가 int형 자료형 데이터크기만큼 주소값이 후증가된다는 의미이다.

아래의 예제를 생각해보자


*(++ptr) =1; //ptr포인터에 저장된 값(즉, 주소값) 자체를 선증감시키고 가리키는 주소값의 공간에 1을 대입하라

*(ptr+1)=1;  //ptr포인터가 가리키는 주소값의 공간의 주소를 자료형 공간단위만큼 하나증가 하고 그 공간에 1을 대입하라.


위 두문장의 차이점은, 아래의 문장의 ptr이 처음 가리키는 주소값은 변하지 않는다는 것이다.

즉, 첫번째 문장의 ptr은 ptr이 가지고 있는 메모리주소값이 선증감한것이다.

(포인터변수란 주소값을 저장하고있는 변수이다)


그럼 이제 배열과 포인터를 연관지어서 중요한 공식을 얻어낼수가 있다.


array[i] == *(array+i); 


i번째 배열의 주소값은 포인터의 주소값이 i만큼 증가한 뒤의 주소값과 동일하므로 이 문장이 가리키는 데이터는 같다.


:: 문자열의 표현법과 상수포인터 ::


char num[] = "hello"; //배열의 길이를 자동으로 계산한다.


char *num = "hello2"; // hello2 문자열이 선언된 어느 메모리공간의 시작주소값을 *num포인터가 가리킨다. 

따라서 위의 경우는 상수 포인터가 되서, 포인터변수가 가리키는 주소값의 변경이 불가능하다.


:: 포인터 배열 ::


포인터 배열이란? 포인터 변수로 이루어진, 주소 값의 저장이 가능한 포인터들로 이루어진 배열을 의미한다. 


즉, int * num[10]=null; // 길이가 10인 int형 포인터 배열 num


char * stream[2]={"hi","bye"}; //길이가 2인 char형 포인터 배열을 의미하며, 

                                           //내부적인 동작은  hi와 bye가 어느 메모리공간에 선언되고 그 주소값이

char * stream[2]={0x1001, 0x1005}; //와 같은 형태로 포인터 배열에 저장되는 것이다.


여기서 포인터변수는 분명 char을 참조하기에 1바이트만 소비할텐데, 왜 4바이씩 저장되었을까? 

이는, 컴퓨터의 메모리구조에 관련이 있다. 32비트 시스템의 OS라면 (32비트=4바이트) 4바이트로 포인터변수가 메모리값을 사용할 것이, 요즘 사용중인 64비트 컴퓨터라면 8바이트의 공간을 소비할 것이다.





이제 함수와 포인터를 서로 엮어보자.


포인터를 배우기 전까지는 우리는 함수 인자의 전달은 값을 중심으로 한다고 체득했다.

히자만 이제 주소값을 보낼수 있음을 알게 될것이다.


간단한 예)


int simple_example(int *prr)

{

...

}                       

int main(void)

{

int arr[2]={0};

int *prr=arr;

.

.

simple_example(prr); // simple_example(arr); 동일한 의미이다.

}


:: const 선언의 활용 ::


const는 const가 가리키는 변수의 저장된 값에 대해서 변경을 허락하지 않겠다는 의미이다.


int num=0;

const int* ptr = # // 포인터 ptr이 가리키는 변수 num값의 변경을 허용하지 않겠다. 


"or"


int * const ptr = # // 포인터 ptr자체의 값변경을 허용하지 않겠다. 


이렇게 생각해보자, const와 가까운쪽을 고정시키겠다는 의미로 보자.

즉, const int* ptr = # 의 경우는  int* ptr 전체를 고정하기에 이가 가리키는 num값이 고정되는 것이고

int * const ptr = #의 경우는 ptr 포인터 변수를 고정시키기에 변수가 저장하는 메모리값을 고정하는 것이다.





+ Recent posts