[C] 포인터

728x90

1. 포인터의 개념

포인터란 변수의 일종입니다. 그러나 특정 데이터값을 가지고 있는 변수와 다르게 포인터는 특정 데이터가 저장된 기억장소의 주소(번지)값을 가지고 있습니다.

 

따라서 포인터는 기억공간을 변수명으로 접근하지 않고 주소로 접근하기 위해서 사용합니다.

 

일반적으로 변수에 데이터를 저장할때는 먼저 선언을 하면서 기억공간이 할당되고 할당된 기억공간에 데이터가 저장됩니다. 이때 할당된 기억공간은 주소(번지)가 부여되어 있습니다.

따라서 주소를 이용하여 변수와 동일한 작업이 가능한데 이것이 포인터를 사용하는 것입니다.

 

이를 위해서는 주소값(포인터값)을 다룰 수 있는 변수가 필요하며 이 변수를 포인터 변수라고 합니다.

 

먼저 변수의 주소를 확인해 보겠습니다.

#include <stdio.h>
void main() {
  int days = 365;
  int month = 12;
  int Table[5] = {1,2,3,4,5};
  printf("days의 주소는 %x\n", &days); // 출력: days의 주소는 11bd6e2c
  printf("month의 주소는 %x\n", &month); // 출력: month의 주소는 11bd6e28
  printf("배열 Table의 주소는 %x\n", &Table); //출력:배열 Taable의 주소는 11bd6e30 -> 배열명은 주소를 나타냄
  printf("배열 Table의 첫번째 요소의 주소는 %x\n", &Table[0]); 
  //출력: 배열 Table의 첫번째 요소의 주소는 11bd6e30
  printf("배열 Table의 두번째 요소의 주소는 %x\n", &Table[1]);
  //출력: 배열 Table의 두번째 요소의 주소는 11bd6e34

}

 

주소는 실행할 때마다 변경됩니다.

 

2. 포인터 변수의 선언과 참조

1) 포인터 변수의 선언

포인터 변수를 선언하는 방법은 아래와 같습니다.

형식: 자료형 *포인터변수명;

사용 예: int *p;

기능: 변수 p는 포인터 변수로서 정수형의 자료를 갖는 변수의 주소를 갖는다.

 

이 때 p는 포인터 변수로서 정수형 자료가 있는 주소를 가지고 있습니다.

반면에 *p는 해당 주소에 수록되어 있는 정수형 자료를 가지고 있습니다.

 

2) 포인터 변수의 참조

포인터 변수를 참조할때는 &,* 연산자를 사용합니다

 

잘못된 참조방법의 예시는 아래와 같습니다.

올바른 참조방법의 예시는 아래와 같습니다.

#include <stdio.h>
void main() {
  int *p,i=3,j;

  p=&i; // 포인토 변수 p는 변수 i의 주소를 가리킴
  j=*p; // 포인터 변수 p가 가리키는 번지의 내용을 변수 j에 대입
  j++; // 변수 j의 값을 1증가

  printf("*p=%d\n",*p); // 출력: p=3
  printf("p=%x\n",p); //출력: p =e0f1dbbc
  printf("j=%d\n",j); //출력: j=4
}

위 코드의 내용을 그림으로 표현하면 아래와 같습니다.(아래의 fff4는 주소입니다.)

3) void 형 포인터

형식 : void * 포인터명 ;
의미:프로그램 실행시에 자료형이 결정되는 경우에 사용
     저장하기 전에 명시적 형변환이 필요

 

예시코드

#include <stdio.h>
void main() {
  int a=100;
  char b ='b';
  void *p = NULL; // void형 포인터를 선언한 후 초기화
  p=(int*)&a; // void형 포인터 p에 int형 변수 a의 주소를 명시적 형변환을 이용하여 대입
  printf ("*p=%d\n",*(int*)p); //출력: *p=100
  p=(char*)&b; // void형 포인터 p에 char형 변수 b의 주소를 명시적 형변환을 이용하여 대입
  printf ("*p=%c\n",*(char*)p); //출력: *p=b

}

 

4) 일반 변수와 포인터 변수의 비교

일반 변수와 포인터 변수를 표로 비교하면 아래와 같습니다.

  일반변수 포인터 변수
선언 int a; 정수형 변수 a 선언 int *a; 포인터 변수 a를 정수형으로 선언
값 할당 a=100; 변수 a에 100할당 *a=100; a주소에 100할당
주소참조 &a 변수 a의 주소 a a자체가 주소
주소연산 연산 불가능   a--; 포인터를 1감소

 

3. 포인터 연산

그림 예시는 아래와 같습니다.

 

코드 예시는 아래와 같습니다.

#include <stdio.h>
void main() {
  int *p,a[]={10,20,30,40,50};
  p=&a[0]; //배열 첫번째 요소의 주소값을 포인터 변수 p에 저장
  printf("*p==%d\n",*p); //출력: *p==10
  printf("*p++==%d\n",*p++); //출력: *p++==10 -> 포인터 p의 값을 출력 후 주소를 1(4byte)증가
  printf("*++p==%d\n",*++p); //출력: *++p==30 -> 포인터 p의 주소를 1(4byte) 증가 후 출력
  p=p+2; //포인터 p의 주소를 2(8byte) 증가
  printf("*p==%d\n",*p); //출력: *p==50
  printf("a[2]==%d\n",a[2]); //출력: a[2]==30
  printf("*p+2==%d\n",*p+2); //출력: *p+2==52 -> 포인터 p의값에 2를 더함

}

 

#include <stdio.h>
void main() {
  int *p,*q;
  int a[]={10,20,30,40,50,60,70,80,90,100};
  p=&a[3];
  printf("*p==%d\n",*p); //출력: *p==40
  printf("*(p+3)==%d\n",*(p+3)); //출력: *(p+3)==70 -> *(p+3)과 *p+3에 주
  q=p+3;
  printf("*q==%d\n",*q); //출력: *q==70
  //두 포인터간의 뺄셈(동일 배열을 가리키는 경우)
  printf("p-q==%d\n",p-q); //출력: p-q==-3
  printf("q-p==%d\n",q-p); //출력: q-p==3
  //printf("q+p==%d\n",q+p); // 두 포인터간의 덧셈은 불가능

}

4. char형 포인터

char형 포인터는 문자열 처리에 효과적인 포인터 입니다.

위 그림을 코드로 표현하면 아래와 같습니다.

#include <stdio.h>
void main() {
  char*cp ="COMPUTER"; //char형 포인터변수 cp의 값은 "COMPUTER"가 수록된 시작 주
  int i=0;
  do
  printf("*(cp+%d):%c\n",i,*(cp+i)); // *(cp+i)를 구한 후 i 를 1증가
  while(*(cp+i++)!=0);
  //출력: 
  //*(cp+0):C
  //*(cp+1):O
  //*(cp+2):M
  //*(cp+3):P
  //*(cp+4):U
  //*(cp+5):T
  //*(cp+6):E
  //*(cp+7):R
  //*(cp+8):
}

 

5. 포인터와 배열의 관계

#include <stdio.h>
void main() {
  static int a[]={10,20,30,40,50};
  int *pt,b,c,d;
  pt =a; //배열명을 사용하여 배열의 시작주소를 할당
  b=*pt +*(pt+3); //b=10+40
  pt ++; //포인터를 1이동
  c=*pt +*(pt+3); //c=20+50
  d=*pt +3; //d=20+3
  printf("b=%d,c=%d,d=%d",b,c,d); //출력: b=50,c=70,d=23
}

 

#include <stdio.h>
void main() {
  static a[3][3]={{1,2,3},{4,5,6},{7,8,-9}};
  int *pt;
  pt=a[0]; //pt=a또는 pt=&a[0][0]과 동일
  while(*pt !=-9){
  printf("%d",*pt);
  pt ++;
  }
  //출력: 12345678
}

 

포인터와 배열은 서로 호환적입니다.

아래는 호환에 대한 예시입니다

#include<stdio.h>
void main()
{
  char A[]="ARRAY";
  char *p="POINTER";
  int i;
  for(i=0;i<5;i++)
    printf("*(A+%d):%c\n",i,*(A+i)); //배열을 포인터 형식으로 참조
    //출력: 
    //*(A+0):A
    //*(A+1):R
    //*(A+2):R
    //*(A+3):A
    //*(A+4):Y
    
  for(i=0;i<7;i++)
    printf("p[%d]:%c\n",i,p[i]); // 포인터를 배열 형식으로 참조
    //출력:
    //p[0]:P
    //p[1]:O
    //p[2]:I
    //p[3]:N
    //p[4]:T
    //p[5]:E
    //p[6]:R
}

1) 포인터와 배열의 값 변경 여부

2) 배열과 포인터의 기억공간 확보 방식 차이

배열은 기억공간 중 자료영역을 고정적으로 확보합니다.

반면에 포인터는 기억공간 중 자료영역을 유동적으로 확보합니다. 필요할때만 자료용 기억공간을 확보할 수 있습니다 따라서 자료의 개수가 가변적인 경우 효과적입니다.

6. 포인터 배열

포인터 배열이란 포인터가 여러개 있을때 포인터의 집합이라고 할 수 있습니다.

포인터가 여러 개 사용될때 포인터 배열로 선언하며 주로 문자열 배열처리에 사용합니다.

위는 포인터를 하나하나 선언한 것이고 아래는 포인터 배열 하나를 선언한 것입니다. 훨씬 간단해진 것을 볼 수 있습니다.

1) 포인터 배열 선언과 기억공간 표현

2) 포인터 배열의 사용예시

#include<stdio.h>
void main()
{
  int a[]={1,2,3,4};
  int b[]={5,6,7,8};
  int *PA[2]; //포인터 배열을 선
  PA[0]=a; //배열 a[]의 시작주소를 포인터 배열요소에 전달
  PA[1]=b; //배열 b[]의 시작주소를 포인터 배열요소에 전달
  printf("*(PA[0])=%d\n",*(PA[0])); //출력: *(PA[0])=1
  printf("*(PA[0]+1)=%d\n",*(PA[0]+1)); //출력: *(PA[0]+1)=2
  printf("*PA[1]=%d\n",*PA[1]); //출력: *PA[1]=5
  printf("*PA[1]+15=%d\n",*PA[1]+15); // 출력: *PA[1]+15=20
}

7. 이중포인터

이중 포인터는 자료가 있는 곳을 이중으로 가리키는 포인터입니다. 구체적으로 설명하자면 이중 포인터가 가리키는 주소로 가보면 자료가 아닌 주소값이 들어있고, 그 주소에 자료가 들어있는 구조입니다.

 

1) 이중 포인터의 선언

형식: int **p;
의미: 포인터 변수에 다시 포인터를 지정하는 것으로서, 포인터에 대한 포인터이다.

 

 

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

[C] 공용체  (0) 2024.05.31
[C] 구조체  (0) 2024.05.31
[C] 배열  (1) 2024.05.02
[C] 기억 클래스  (1) 2024.04.26
[C] 함수  (0) 2024.04.26