1. 포인터 개념
가. 포인터란?
사용되는 모든 변수는 메모리의 특정 위치에 저장되는데 그 위치를 나타내는 메모리 주소
나. 포인터변수
메모리의 주소 값을 저장하는 특별한 변수
포인터 변수가 어떤 변수의 주소를 저장하고 있다는 것은 그 변수를 가리키고 있다는 의미
연결된 주소의 변수 영역 액세스 가능
포인터 변수를 간단히 포인터라고 함.
다. 포인터 엑세스 처리
편지봉투에 받는 사람의 집주소를 쓰면 그 주소로 편지가 전달되어 집주인이 편지를 받게 됨.
편지봉투를 포인터라고 생각하면 편지봉투에 쓰는 "받는 사람 주소"는 포인터에 저장된 변수의 메모리 주소가 되고, 주소에 해당하는 집주인이 편지를 받은 것은 포인터를 통한 엑세스 처리로 생각할 수 있음.
라. 포인터 사용 예시
1) 포인터를 사용해 다른 변수 엑세스 가능
int i
에서 할당된 주소를 150이라고 하면 int *ptr = &i
에 의해 변수 i
의 주소 150을 포인터 ptr
에 저장하므로 포인터 ptr
은 변수 i
를 가리킴.
그러면 변수 i
와 포인터 ptr
이 논리적으로 서로 연결되어 ptr
을 사용하여 변수 i
를 액세스 할 수 있음.
마. 포인터 선언
1) 포인터 선언 형식
자료형 *포인터이름;
포인터 자체의 자료형이 아니라 포인터에 저장할 수 있는 일반 변수의 자료형
일반 변수 이름과 구별되도록 앞에 *를 붙여 포인터임을 나타냄.
2) 포인터를 다양한 자료형으로 선언한 예
포인터는 자료형에 상관없이 포인터 자체의 크기는 메모리 주소 한 개의 크기인 2바이트
3) 포인터에서 선언한 자료형에 따른 메모리 액세스 범위
char *ptr;
1바이트의 char형 변수의 주소를 저장할 포인터를 선언한 예이다.
ptr에 저장된 메모리 주소로부터 1바이트의 char 데이터를 액세스한다.
short *ptr;
2바이트의 short형 변수의 주소를 저장할 포인터를 선언한 예이다.
ptr에 저장된 메모리 주소로부터 2바이트의 short 데이터를 액세스한다.
int *ptr;
4바이트의 int형 변수의 주소를 저장할 포인터를 선언한 예이다.
ptr에 저장된 메모리 주소로부터 4바이트의 int 데이터를 액세스한다.
바. 포인터 연산
1) 주소연산자: &
포인터 = &변수;
변수의 주소를 얻기 위해 사용
변수 앞에 주소연산자를 사용하면 그 변수의 주소를 사용 가능
주소연산자를 사용할 변수와 포인터는 같은 자료형으로 선언되어 있어야 함.
int i = 10;
int *ptr;
ptr = &i;
2) 참조연산자: *
다른 변수의 주소가 저장된 포인터에 참조 연산자를 사용하면 저장된 주소 영역(참조 영역) 또는 주소에 있는 값(변수에 저장된 값)을 액세스할 수 있음.
*포인터 = 값; // (1)
변수 = *포인터; // (2)
(1)과 같이 지정연산자의 좌변에 있는 포인터에 참조연산자를 사용하면 포인터가 가리키고 있는 주소 영역을 액세스하여 값을 저장할 수 있음.
(2)와 같이 지정연산자의 우변에 있는 포인터에 참조연산자를 사용하면, 포인터가 가리키는 주소 영역에 있는 값을 액세스하여 변수에 저장할 수 있음.
int i, j;
int *ptr;
ptr = &i;
*ptr = 10; // i에 10이 저장됨.
j = *ptr; // j에 10이 저장됨.
사. 포인터의 초기화
1) 주소연산자를 사용하여 변수 주소를 지정
int i;
int *ptr = &i;
2) 동적 메모리를 할당하고 그 시작 주소를 포인터값으로 지정
char *ptr = (char *)malloc(100);
3) 문자형 포인터에 문자열의 시작 주소를 지정
char *ptr = "korea";
4) 배열 이름을 이용하여 배열 시작 주소를 지정
char A[100];
char *ptr = A;
5) 배열의 첫 번째 요소(0번 인덱스)의 주소를 이용하여 배열 시작 주소를 지정
char A[100];
char *ptr = &A[0];
2. 문자 배열과 포인터 배열 차이점
가. 포인터와 문자 배열
void main() {
int i;
char string1[20] = "Dreams come true!", string2[20], *ptr1, *ptr2;
ptr1 = string1;
printf("\n string1의 주소 = %u \t ptr1 = %u", string1, ptr1);
// string1의 주소 = 1374236 ptr1 = 1374236
printf("\n string1 = %s \n ptr1 = %s", string1, ptr1);
// string1 = Dreams come true!
// ptr1 = Dreams come true!
printf("\n\n %s", ptr1 + 7);
// come true!
ptr2 = &string1[7];
printf("\n %s \n\n", ptr2);
// come true!
for (i=16; i>=0; i--) putchar(*(ptr1 + i));
// !eurt emoc smaerD
for (i=0; i<20; i++) string2[i] = *(ptr1 + i);
printf("\n\n string1 = %s", string1);
// string1 = Dreams come true!
printf("\n string2 = %s", string2);
// string2 = Dreams come true!
*ptr1 = 'P';
*(ptr1 + 1) = 'e';
*(ptr1 + 2) = 'a';
*(ptr1 + 3) = 'c';
*(ptr1 + 4) = 'e';
printf("\n\n stirng1 = %s\n", string1);
// string1 = Peaces come true!
getchar();
}
나. 포인터 배열
포인터 자료형을 배열로 구성
여러 개의 포인터를 하나의 배열로 구성한 배열의 특징과 포인터의 특징을 모두 활용할 수 있음.
포인터 배열의 선언형식
자료형 *포인터배열이름 [배열크기];
포인터 배열에서 각 배열 요소는 포인터가 됨.
2차원 문자 배열을 1차원 포인터배열로 표현할 수 있음.
다. 2차원 배열과 1차원 포인터배열의 비교
1)
char string3[3][10] = { "Dreams", "come", "true!" };
배열이 선언될 때 지정한 크기의 배열 공간이 메모리에 할당됨.
한 번 할당된 배열의 크기를 줄이거나 늘리기 어렵기 때문에 처음 선언한 배열크기보다 실제 사용 공간이 작으면 메모리가 낭비되고, 실제 필요한 공간이 할당 크기보다 크면 배열을 새로 만들어야 하는 문제가 발생할 수 있음.
2)
char *ptr[3] = { { "Dreams" }, { "come" }, { "true!" } };
포인터 배열을 사용하여 문자열을 저장하면 저장하는 문자열의 길이에 따라 메모리가 할당됨.
그러므로 저장할 문자열의 길이를 정확히 예측할 수 없거나 문자열의 길이가 자주 변하는 경우에 메모리를 좀 더 효율적으로 사용할 수 있음.
3. 포인터의 포인터(이중포인터) 개념
가. 포인터의 포인터
포인터를 가리키고 있는 포인터: 이중 포인터(**)
일반 변수의 주소가 아니라 포인터의 주소를 가지고 있는 포인터를 의미함.
자료형 **포인터이름;
void main() {
char *ptrArray[2];
char **ptrptr;
int i;
ptrArray[0] = "Korea";
ptrArray[1] = "Seoul";
ptrptr = ptrArray;
printf("\n ptrArray[0]의 주소 (&ptrArray[0]) = %u", &ptrArray[0]);
// ptrArray[0]의 주소 (&ptrArray[0]) = 3255210528
printf("\n ptrArray[0]의 값 (ptrArray[0]) = %u", ptrArray[0]);
// ptrArray[0]의 값 (ptrArray[0]) = 121637464
printf("\n ptrArray[0]의 참조값 (*ptrArray[0]) = %c", *ptrArray[0]);
// ptrArray[0]의 참조값 (*ptrArray[0]) = K
printf("\n ptrArray[0]의 참조문자열 (*ptrArray[0]) = %s", *ptrArray);
// ptrArray[0]의 참조문자열 (*ptrArray[0]) = Korea
printf("\n ptrArray[1]의 주소 (&ptrArray[1]) = %u", &ptrArray[1]);
// ptrArray[1]의 주소 (&ptrArray[1]) = 3255210536
printf("\n ptrArray[1]의 값 (ptrArray[1]) = %u", ptrArray[1]);
// ptrArray[1]의 값 (ptrArray[1]) = 121637470
printf("\n ptrArray[1]의 참조값 (*ptrArray[1]) = %c", *ptrArray[1]);
// ptrArray[1]의 참조값 (*ptrArray[1]) = S
printf("\n ptrArray[1]의 참조문자열 (*ptrArray[1]) = %s", *(ptrArray + 1));
// ptrArray[1]의 참조문자열 (*ptrArray[1]) = Seoul
printf("\n ptrptr의 주소 (&ptrptr) = %u", &ptrptr);
// ptrptr의 주소 (&ptrptr) = 3255210520
printf("\n ptrptr의 값 (ptrptr) = %u", ptrptr);
// ptrptr의 값 (ptrptr) = 3255210528
printf("\n ptrptr의 1차 참조값 (*ptrptr) = %u", *ptrptr);
// ptrptr의 1차 참조값 (*ptrptr) = 121637464
printf("\n ptrptr의 2차 참조값 (**ptrptr) = %c", **ptrptr);
// ptrptr의 2차 참조값 (**ptrptr) = K
printf("\n ptrptr의 2차 참조문자열 (**ptrptr) = %s", *ptrptr);
// ptrptr의 2차 참조문자열 (**ptrptr) = Korea
printf("\n\n *ptrArray[0]: ");
for (i=0; i<5; i++) printf("%c", *(ptrArray[0] + i));
// *ptrArray[0]: Korea
printf("\n **ptrptr: ");
for (i=0; i<5; i++) printf("%c", *(*ptrptr + i));
// **ptrptr: Korea
printf("\n\n *ptrArray[1]: ");
for (i=0; i<5; i++) printf("%c", *(ptrArray[1] + i));
// *ptrArray[1]: Seoul
printf("\n **(ptrptr+1): ");
for (i=0; i<5; i++) printf("%c", *(*(ptrptr + 1) + i));
// **(ptrptr+1): Seoul
getchar();
}