포인터의 포인터는 포인터 변수를 가리키는 또 다른 포인터 변수를 뜻함
"이중 포인터" or "더블 포인터" 라고 부름
** : 이중 포인터 변수 or 더블 포인터 변수
* : 싱글 포인터 변수
포인터 변수를 가리키는 이중 포인터 변수(더블 포인터 변수)
포인터 변수는 종류에 상관없이 무조건 주소 값을 저장하는 변수.
다만 차이가 나는 것은 포인터 변수가 가리키는 대상일 뿐
int main(void)
{
double num = 3.14;
double* ptr = # // 변수 num의 주소 값 저장
}
변수 num과 포인터 변수 ptr의 차이점과 공통점
공통점 : 둘 다 변수임. 따라서 값의 저장이 가능하다.
차이점 : 저장하는 값의 종류(유형)이 다르다.
- ptr도 메모리 공간에 할당이 되는 변수.
- 따라서 이를 대상으로도 & 연산이 가능하며, 이 때 반환되는 주소 값은 'double형 더블 포인터 변수'에 저장이 가능하다.
- 떄문에 싱글 포인터 변수 ptr을 대상으로 다음과 같은 문장이 구성 가능
double** dptr = &ptr;
dptr과 ptr 및 num에 대해 다음과 같이 접근이 가능
*dptr = .....; → *dptr은 포인터 변수 ptr을 의미함
*(*dptr) = ....; → *(*dptr)은 변수 num을 의미함(괄호 생략 가능)
예제
#include <stdio.h>
int main(void)
{
double num = 3.14;
double* ptr = #
double** dptr = &ptr;
double* ptr2;
printf("%9p %9p \n", ptr, *dptr);
printf("%9g %9g \n", num, **dptr);
ptr2 = *dptr; // ptr2 = ptr 과 같은 문장
*ptr2 = 10.99;
printf("%9g %9g \n", num, **dptr);
return 0;
}
ptr2 = *dptr; 이 실행되면 포인터 변수의 참조관계는 다음과 같다.
그리고 변수 num에 접근하는 방법은 다음과 같이 총 4가지
**dptr = 10.1;
*ptr = 20.2;
*ptr2 = 30.3;
num = 40.4
포인터 변수 대상의 Call-by-reference
일전에 두 변수에 저장된 값을 서로 바꿔서 저장하는 함수를 다음과 같이 정의한 바 있다.
void swap(int* ptr1, int* ptr2)
{
int temp = *ptr1;
*ptr1 = *ptr2;
*ptr2 = temp;
}
이번에는 두 싱글 포인터 변수에 저장된 값을 서로 바꿔서 저장하는 함수를 정의
아래와 같이 포인터 변수의 참조 관계를 바꿔준다.
아래 결과는 제대로 만들어내지 못한다.
#include <stdio.h>
void swap(int*, int*);
int main(void)
{
int num1 = 10, num2 = 20;
int* ptr1, * ptr2;
ptr1 = &num1, ptr2 = &num2;
printf("*ptr1, *ptr2 : %d %d \n", *ptr1, *ptr2);
swap(ptr1, ptr2);
printf("*ptr1, *ptr2 : %d %d \n", *ptr1, *ptr2);
}
void swap(int* p1, int* p2)
{
int* temp = p1;
p1 = p2;
p2 = temp;
}
왜냐
"p1은 ptr1과 별개이고 p2는 ptr2와 별개이기 때문에"
다음의 상황과 같음(함수가 실행되지 않고 매개변수에 값만 전달된 상황)
위 swap 함수에 인자 전달 시 아래와 같은 상황(값 변경 전)
swap 함수 몸체부분이 실행되면 p1과 p2에 저장된 값이 바뀌어 다음의 상태
p1과 p2에 저장된 값은 바뀌지만 ptr1과 ptr2와는 별개의 변수이기 때문에 ptr1은 여전히 num1의 주소 값을, ptr2는 여전히 num2의 주소 값을 저장하는 상태
≫ 보면 여전히 0x10은 10을 가리키고, 0x20은 20을 가리킨다.
그렇다면 어떻게 해야 "함수 내에서" 가리키는 대상을 바꿀 수 있음?
이를 위해서는 함수내에서 변수 ptr1과 ptr2에 직접 접근이 가능해야 함.
그래서 이 두 변수에 저장된 값을 서로 바꿔줘야 함
이를 위해서는 int형 더블 포인터가 매개변수로 선언
#include <stdio.h>
void swap(int*, int*);
int main(void)
{
int num1 = 10;
int num2 = 20;
int* ptr1;
int* ptr2;
ptr1 = &num1;
ptr2 = &num2;
printf("*ptr1, *ptr2: %d %d \n", *ptr1, *ptr2);
swap(&ptr1, &ptr2);
printf("*ptr1, *ptr2: %d %d \n", *ptr1, *ptr2);
}
void swap(int** dp1, int** dp2)
{
int* temp = *dp1;
*dp1 = *dp2;
*dp2 = temp;
}
동작원리
swap(&ptr1, &ptr2)
위 코드를 보면 이전과 달리 주소 값이 전달 되었다.
그렇다면 ptr1과 ptr2의 주소 값이 swap의 매개변수로 전달이 되어서 다음의 형태가 된다.
그 다음 세 문장이 실행
int *temp = *dp1;
*dp1 = *dp2;
*dp2 = temp;
dp1이 가리키는 변수와 dp2가 가리키는 변수에 저장된 값을 서로 교환하는 코드
*dp1은 ptr1을 의미하고 *dp2는 ptr2를 의미함
int *temp = ptr1;
ptr1 = ptr2;
ptr2 = temp;
이를 실행하면 다음과 같이 값이 바뀐다.
포인터 배열과 포인터 배열의 포인터 형
int* arr[20]; // 길이가 20인 int형 포인터 배열 arr1
double* arr2[30]; // 길이가 30인 double형 포인터 배열 arr2
int arr[30]; // 배열이름 arr은 int형 포인터
- 1차원 배열이름이 가리키는 요소의 자료형만 고려하면 됨
그렇다면 위에 선언된 int형 포인터 배열 의 이름 arr1과 double형 포인터 배열의 arr2의 포인터 형은?
1차원 배열이기 때문에 배열이름이 가리키는 첫 번째 요소의 자료형에 따라서 포인터 형이 결정된다.
즉, arr1이 가리키는 첫 번째 요소 →→→→→→ int형 싱글 포인터
따라서, 배열이름 arr1은 int형 더블 포인터
예시
#include <stdio.h>
int main(void)
{
int num1 = 10;
int num2 = 20;
int num3 = 30;
int* ptr1 = &num1;
int* ptr2 = &num2;
int* ptr3 = &num3;
int* ptrArr[] = { ptr1, ptr2, ptr3 };
int** dptr = ptrArr; // dprArr과 dptr의 포인터 형이 일치함을 뜻함
printf("%d %d %d \n", *(ptrArr[0]), *(ptrArr[1]), *(ptrArr[2]));
printf("%d %d %d \n", *(dptr[0]), *(dptr[1]), *(dptr[2]));
return 0;
}
헷갈려서 정리...
- ptrArr[]의 배열을 새롭게 정의할 때 앞에 * 연산자를 붙여 배열을 정의했다.
- 그 이유는 ptr1, ptr2, ptr3의 각 요소가 포인터이기 때문에 포인터형 배열을 선언한 것으로 볼 수 있다.
- 그냥 일반 배열은 int arr[3] = { 1, 2, 3 } 이렇게 배열의 요소로 int 자료형들이 있기 때문에 그냥 int arr[3]으로 정의가 가능하지만
- 새롭게 정의한 배열을 가리키는 포인터 변수로 ptrArr[]로 정의하였다. 즉 ptrArr[]은 ptr1, ptr2, ptr3 모두를 가리킨다.
- 그렇다면 위 저자의 설명처럼 "배열이름이 가리키는 첫 번째 요소의 자료형에 따라서 포인터 형이 결정"된다고 하였다.
- 그렇다면 ptrArr[]의 첫 번째 요소는 무엇인가 → 바로 num1을 가리키는 포인터 변수인 ptr1이다.
- 따라서 num1을 가리키는 ptr(싱글 포인터)들을 담고있는 ptrArr을 가리키는 dptr은 더블 포인터 형태로 정의한다.
쉽게 생각해서 싱글 포인터 변수를 다시 가리키는 상황이 있으면 더블 포인터로 정의한다고 생각하면 되지 않을까 싶다
참고 : [윤성우 열혈 C 프로그래밍] - 대부분의 내용 및 코드는 이 책에서 개인 공부 정리 목적으로 참고하였습니다.
'Language&Framework&Etc > C' 카테고리의 다른 글
다차원 배열과 포인터의 관계(18-1) 2차원 배열 이름의 포인터형 (0) | 2020.12.10 |
---|---|
포인터의 포인터(17-2) 다중 포인터 변수와 포인터의 필요성 (0) | 2020.12.10 |
다차원 배열(16-2) 3차원 배열 (0) | 2020.12.09 |
다차원 배열(16-1) 다차원 배열의 이해와 활용 (0) | 2020.12.09 |
포인터와 함수에 대한 이해(14-3) 포인터 대상의 const 선언 (0) | 2020.12.08 |