포인터 종결자 C/C++

1. 포인터(포인터 변수)의 개념

(1) 일반 변수와 포인터 변수의 차이점

   일반 변수는 정수/실수/문자 중 1개의 값을 저장한다.
   포인터 변수는 값이 아니라 "주소"(변수, 배열 등 저장공간이 할당된 것)를 저장한다.

 int i=100, data[100]={ 1, 3, 5, 7, 9 };
 int *p, *q;

 p = &i;  // 변수 i의 주소
 q = data; // 배열 data의 주소

 *p = 200; // i = 200; 과 동일
 *(q+1) = 30; // data[1] = 30; 과 동일

(2) 포인터 변수를 이용하여 값을 사용/변경

    포인터를 이용하여 값을 사용하거나, 변경할 수 있는 범위는
    포인터가 가리키는 주소에 따라 결정된다.

    즉, 위 예에서 포인터 p는 i를 가리키므로 1개의 값만 사용/변경할 수 있다.
    그러나 q는 배열 data를 가리키므로 100개의 값을 사용/변경할 수 있다.

(3) 포인터 변수가 배열을 가리킬 때 --- 포인터 변수의 증감 연산

    - 포인터 연산자 '*' 대신에 배열의 인덱스 사용
 즉, *(q+3) 은 q[3] 과 동일하며, 배열과 동일한 형태로 사용할 수 있다.

    - 포인터 변수가 가리키는 배열의 위치 이동
 아래와 같이 포인터 변수가 가리키는 위치를 변경할 수 있다.
 단, 포인터가 가리키는 배열 data[100]의 인덱스 범위를 벗어날 수 없다.

  q = q + 2;
  q++;
  q--;


2. 포인터 배열

   여러 개의 변수(예: 100개의 변수)를 선언하는 대신에 배열을 선언하는 것과 같다.
   즉, 포인터 배열은 포인터 변수를 여러 개 선언하는 대신에 포인터 배열을 사용한다.

 int *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10;

   와 같이 10개의 포인터 변수를 선언하기 보다는

 int *p[10];

   로 선언하여 p1, p2, ..., p10 대신에 p[0], p[1], ..., p[9] 를 사용한다.


3. 다중 포인터

    1차원 포인터는 값(정수/실수/문자)이 저장된 변수(또는 배열)의 주소.
    2차원 포인터는 1차원 포인터 변수의 주소.
    3차원 포인터는 2차원 포인터 변수의 주소.
    n차원 포인터는 n-1차원 포인터 변수의 주소.

 int i=100, data[100]={ 1, 3, 5, 7, 9 };
 int *p, *q[10];  // 1차원 포인터
 int **dp1, **dp2; // 2차원 포인터
 int ***tp1, ***tp2; // 3차원 포인터

 p = &i;
 dp1 = &p;  // p는 포인터 변수
 tp1 = &dp1;

 q[2] = data;
 dp2 = q;  // q는 포인터 배열
 tp2 = &dp2;

   위 예에서

 i, *p, **dp1, ***tp1

   은 모두 동일하다. 마찬가지로 data[0], *q[2], **dp2, ***tp2 도 동일하다.


4. 포인터와 문자 배열, 스트링 상수의 관계

주의 1. 모든 배열의 이름은 배열에 할당된 공간의 첫번째 "주소"이다.

주의 2. 스트링 상수 "ABC"는 그 자체가 "주소"이며, 값을 변경할 수 없다.
 ('A', 'B', 'C', '\0' 순서의 4문자가 저장된 "주소"를 지칭한다.
  이 문자들은 static area에 자동으로 저장된다.)

 단, 문자배열의 초기화(예: char a[] = "ABC";)에 사용된 "ABC"는
 문자배열의 초기화를 편리하게 하기 위한 것일 뿐이며, 스트링 상수가 아니다.

<예1>
 char *p;
 char a[] = { 'A', 'B', 'C', '\0' }; // char a[] = "ABC"; 와 동일함!

 p = a;
 *p = 'X';
 *(p+1) = 'Y';
 *(p+2) = 'Z';

<예2>
 char *p = "ABC"; // p는 스트링 상수 "ABC"를 가리킴
 char b[100];

 strcpy(b, p);
 b[3] = *(p+1);
 b[4] = 'B';
 b[5] = '\0';

   포인터 p는 "스트링 상수"를 가리키고 있으므로 각 문자를 사용할 수 있으나,

 *p = 'X';
 strcpy(p, "XYZ");

   와 같이 값을 변경하는 것은 허용되지 않는다. 그러나 모든 포인터는 다른 주소를
   가리키게 할 수 있으므로

 p = "XYZ";
 p = b;

   와 같은 문장은 당연히 허용된다.(포인터의 정확한 이름은 "포인터 변수"이며,
   포인터 변수가 다른 주소를 가리키게 할 수 있다.)


<예3> char *p = "ABC"; 는

 char *p;
 p = "ABC"

   와 같다. 그러나 char a[] = "ABC"; 를

 char a[4];
 a = "ABC"

   와 같이 쓸 수는 없다.(배열이름은 고정된 주소값이며, 포인터 변수가 아니다.)


5. "구조체 변수"와 "구조체 포인터"에서 항목 선택 방법

(1) 연산자 '.' --- "구조체 변수"의 각 항목을 선택할 때(field selection)

 struct ABC {
    int a;
    int b;
 } x, *p;

    의 경우에 "구조체 변수" x의 항목 a 혹은 b를 선택할 때는

 x.a = 100;
 x.b = 200;

    과 같이 사용한다.

(2) 연산자 '->' --- "구조체 포인터"의 각 항목을 선택할 때(field selection)
  
    "구조체 포인터" p를 이용하여 a, b를 가리킬 때는 아래와 같이 사용한다.

 p = &x;  // 포인터 p는 변수 x를 가리키게 했음.
 p->a = 100;
 p->b = 200;

위 예에서 p는 포인터이므로 *p는 x와 동일하다. 따라서 일반적인 포인터 사용법에 따라
위 예는 아래와 같이 써도 무방하다.

 p = &x;
 (*p).a = 100;
 (*p).b = 200;

다만, p->a 가 (*p).a 보다 이해하기가 더 편하고 readability가 좋으므로
일반적으로 이렇게 쓰는 경우는 거의 없다.

<참고> (*p).a를 *p.a 라고 쓰면 안된다. 왜냐하면 '.'이 '*'보다 우선순위가 높아서
       *p.a 는 *(p.a) 와 같아지게 되기 때문이다.


6. 포인터 관련 선언문 예

 int *p;  --- 포인터 변수 p, 정수형 주소 저장
 char *p; --- 포인터 변수 p, 문자형 주소 저장
 int *p[n]; --- 크기 n인 포인터 배열 p, (p[0], …, p[n-1]에 정수형 주소 저장)

 int *p(); --- 함수 p의 선언, "return값이 int pointer"인 함수 p
 int *p()[]; --- 함수 p의 선언, "return값이 int pointer 배열"인 함수 p

 int (*p)[n]; --- 포인터 변수 p, "크기 n인 정수형 배열 구조를 갖는 주소"를 저장
 int (*p)(); --- 포인터 변수 p, "return값이 int인 함수"의 주소 저장
 int (*p[])(); --- 포인터 배열 p, "return값이 int인 함수"의 주소 저장

 


덧글

댓글 입력 영역