본문 바로가기
IT/C

C언어 포인터

by -_-* 2021. 1. 21.
반응형

오늘은 C언어의 포인터에 대해서 알아보도록 하겠습니다. 

 

포인터 

포인터란 메모리의 주소를 말하며, 주소 연산자 & 을 통하여 메모리의 주소에 접근할 수 있습니다. 

주소 연산자는 모든 변수에 사용이 가능합니다. 변수의 주소는 변수를 선언할 때 결정되며, 한번 정해지면 주소 값은 변하지 않으므로 상수입니다. 포인터는 메모리 공간에 직접적으로 접근이 가능하기 때문에 운영체제와 같은 시스템 프로그래밍에서 핵심 요소라고 볼 수 있습니다.

 

포인터변수 

포인터변수란 포인터 즉, 메모리 주소를 값으로 가지는 변수입니다. 일반 변수와 구분하기 위해서 포인터변수 선언 시 포인터 기호 * 를 붙여서 구분합니다. 

 

int *p ; // 포인터변수 선언 

 

포인터변수는 모두 메모리의 주소 값을 저장하기 때문에 선언된 자료형에 관계없이 컴퓨터 환경에 따라 그 크기가 달라집니다. 만약 32bit 환경이라면 포인터변수는 4byte, 64bit 환경이라면 포인터변수는 8byte만큼 크기를 차지하게 됩니다.

 

 

간접연산자

간접연산자란 포인터변수에만 사용할 수 있는 연산자로 포인터 즉 메모리의 주소가 가지고 있는 실제 값을 반환하는 연산자입니다. 그래서 값 연산자라고도 불립니다. 

 

포인터 예제

 

#include <stdio.h>

int main(void) {

int a = 1004;

int *p;

p = &a;

printf("변수 a의 값 : %d 변수 a의 주소값 : %d \n", a, &a );

printf("포인터변수 p의 값 : %d p의 주소값 : %d \n",p, &p);

printf("포인터변수 p가 참조하고있는 주소의 실제값: %d\n\n", *p);

printf("포인터변수 p의 크기 : %d", sizeof(p));

return 0;

}

 

*의 의미 : 포인터변수 기호 vs 간접연산자 

먼저 위의 코드에서 int *p; 의 정수형 포인터변수 선언에서 사용된 * 기호와 printf("포인터변수 p가 참조하고있는 주소의 실제값: %d\n\n", *p); 에서 사용된 기호 *는 의미가 다릅니다. 

처음 사용된 int *p;의 경우 내가 포인터변수를 선언하겠다라는 포인터변수 기호이고 , 두번째 사용된 *p의 경우 포인터변수 p가 메모리 주소를 가지고 있는데, 그 주소가 가지고 있는 실제 데이터를 반환하겠다는 간접 연산자입니다. 

많은 분들이 이 기호를 같이 사용하다보니 코드를 이해할 때 혼란스러운 것 같습니다. 변수의 선언에서 사용되었다면 포인터 기호, 아니라면 포인터변수가 참조하는 실제 값을 반환하는 간접연산자 입니다. 

 

예제 코드 풀이 

위의 예제코드에서 일반 정수형 변수 a는 데이터 1004를 가지고 834426188 주소에 변수 a를 할당받았습니다. 

포인터변수 p는 p= &a 의 문장에 의해서 a의 주소값인 834426188 를 기억합니다. 

p를 통해서 a가 가지고 있는 실질적인 데이터 1004를 표현하고 싶다면 간접연산자를 * 사용하여 *p로 포인터변수 p가 참조하는 값 1004를 가져올 수 있습니다.  

 

 

포인터연산 

포인터 연산의 경우자료형의 메모리 바이트 수에 비례하여 연산합니다. 

 

 int a; 
int *p; 
p= &a; 
p++;  // 포인터 연산

 

포인터연산 예제

#include <stdio.h>

int main(void) {

    // 일반 정수형 변수, 실수형 변수 선언 
    int i_var = 1004;

    double d_var=1004.1004;
   
    // 주소를 저장하는 정수형 포인터변수, 실수형 포인터변수 선언 

    int *i_pnt;

    double *d_pnt;

    // 각각의 일반 변수의 주소값으로 포인터변수 값 초기화 

    i_pnt = &i_var;

    d_pnt = &d_var;

    // 정수형 포인터변수 결과 출력 

    printf ("i_var : %d , i_var주소값 :%d \n", i_var, &i_var);

    printf ("i_pnt : %d \n ", i_pnt);

    i_pnt++;  // 포인터변수 연산

    printf ("변경후 i_pnt : %d \n ", i_pnt); // 정수형 포인터변수 결과 출력

    //실수형 포인터변수 결과 출력 
    printf ("d_var : %lf , d_var주소값 :%d \n", d_var, &d_var);

    printf ("d_pnt : %d \n ", d_pnt);

    d_pnt++;// 포인터변수 연산

    printf ("변경후 d_pnt : %d \n ", d_pnt); // 실수형 포인터변수 결과 출력 

    return 0;

}

 

 

위의 에제 코드에서 정수형 변수 i_var의 주소 값은 630140044입니다. i_var 주소 값을 정수형 포인터변수 i_pnt에 저장했습니다. 그리고 i_pnt++ 명령에 의해서 정수형 포인터변수 i_pnt가 정수형의 크기인 4byte만큼 증가한 것을 확인할 수 있습니다. 

 

마찬가지로 실수형 변수 d_var의 주소값은 630140048이었는데요. d_var 주소값을 실수형 포인터변수 d_pnt에 저장했습니다.  그리고 d_pnt++ 명령에 의해서 실수형 포인터변수 d_pnt가 실수형의 크기인 8byte만큼 증가해 630140056으로 변경됨을 확인할 수 있습니다. 

 

포인터와 배열 

배열의 이름 : 배열의 이름은 배열의 시작 주소를 의미하기 때문에 포인터라고 볼 수 있습니다. 

따라서 배열의 이름으로도 포인터연산이 가능합니다. 

#include <stdio.h>

int main(void) {

    int numbers[5] = {1,2,3,4,5}; // 정수형 배열 선언 

    int *pnt; // 포인터변수 선언

    pnt = numbers; // 포인터변수에 배열 시작주소 할당

    printf("numbers[3]: %d \n" , numbers[3]);

    
    *(pnt+3) = 100; // 포인터를 이용한 배열 값 변경 

    
    printf("change numbers : %d \n", numbers[3]);  //배열을 이용한 결과출력

    printf("pointer numbers : %d \n", *(pnt+3)); //포인터를 이용한 결과출력

    return 0;

}

위의 예제 코드에서 정수형 배열 numbers를 선언하고 동시에 초기화해주었습니다. 그리고 포인터변수 pnt를 선언하였습니다.  배열의 시작 주소 즉 numbers[0]의 주소는 numbers를 의미하므로 pnt = numbers; 라는 명령에 의해서 포인터변수 pnt에 배열의 시작 주소를 할당했습니다. 

 

배열을 이용하여 numbers[3]을 출력하면, c언어에서 배열 인덱스는 0부터 시작하므로 네 번째 데이터인 4가 출력됩니다. 

포인터 변수 pnt = &numbers[0] 를 가지고 있으므로 pnt+3은 numbers[3]의 주소를 의미합니다. 

따라서 간접연산자인 *(numbers[3])을 이용하여 numbers[3]의 주소가 참조하고 있는 실제 데이터의 값을 100으로 변경하는 명령어가 *(pnt+3) = 100; 으로 표현되었습니다. 

 

따라서 numbers[3]을 출력하면 값이 변경되어 더 이상 4가 아닌 100으로 출력됩니다. 다시 말해서 numbers[3]은 *(pnt+3) 과 같은 의미를 가집니다. 

 

포인터와 문자열 

c언어에서 문자열은 문자형 배열로 표현되므로 문자열을 포인터로 처리할 수 있습니다. 

차이점은 포인터로 문자열을 처리한다면 포인터 변수를 키보드 입력을 통한 문자열 초기화는 불가능합니다. 또한 저장된 문자열 중 일부분을 수정하는 것은 불가능하고 전체만 가능합니다. 반면에 배열로 문자열을 처리한다면 키보드 입력을 통한 문자열 초기화가 가능하고, 문자열 중 일부분을 수정할 수 있습니다. 

 

포인터배열 

여러 개의 문자열을 처리할 때 우리는 문자형 포인터의 배열을 사용하거나 2차원 문자형 배열을 사용할 수 있습니다.

 

오늘은 C언어의 포인터에 대한 개념을 살펴보았는데요. 차후 기회가 된다면 포인터와 문자열, 포인터 배열에 대해서 더 자세히 다루도록 하겠습니다. 다음 포스트에서 함수에 대해서 알아볼게요. 감사합니다. 

 

반응형

'IT > C' 카테고리의 다른 글

C언어 공용체, 열거형 , 형정의  (0) 2021.01.16
C언어 구조체  (0) 2021.01.14
c언어 배열  (0) 2021.01.13
C언어 분기문 break /continue /goto / return  (0) 2021.01.12
C언어 반복문  (0) 2021.01.11

댓글