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인 함수"의 주소 저장