C언어
[C언어] 다중 포인터
simonpark817
2024. 2. 6. 14:10
[포인터를 사용하는 이유]
- 변수는 본질적으로 공간이다.
- 공간은 본질적으로 옮길 수가 없다.
- 그렇게 때문에 어떤 변수에 있는 값을 누군가에게 공유할 때는 주소(포인터)를 사용할 수 밖에 없다.
- 주소가 바로 포인터이다.
- 주소를 담는 변수는 포인터 변수라고 한다.
[2중 포인터]
- 2중 포인터는 말그대로 주소를 2번 옮겨서 어떤 변수의 있는 값을 공유하는 것이다.
- 아래는 2중포인터를 활용하여 change라는 함수를 통해 기존에 있던 값을 바꾸는 코드이다.
#include <stdio.h>
void change(int** num) {
**num = 50;
}
int main(void) {
int x = 20;
int* p = &x;
printf("change 함수 호출하기 전의 x : %d\n", x);
change(&p);
printf("change 함수 호출한 후의 x : %d\n", x);
// 출력 => change 함수 호출한 후의 x : 50
return 0;
}
- change() 함수에서 매개변수로 받는 num에는 p의 주소값이 담겨있다. 즉, *num == p이다.
- main 함수의 포인터변수 p는 x의 주소값이 담겨있다. 즉, p = &x이다.
- 결론적으로 매개변수 num이 int x의 주소에 도달하기 위해서는 두 번의 포인터를 사용하여 접근해야된다.
- *num은 p와 같고 **num은 x와 같다.
[3중 포인터]
- 2중 포인터 외에도 3중, 4중 등 다중 포인터를 사용하는데 그 가운데 3중 포인터의 예시이다.
#include <stdio.h>
int main(void) {
// 1증 포인터
int x;
int* p = &x;
*p = 20; // x = 20
printf("x : %d\n", x);
// 2중 포인터
int** pp = &p;
**pp = 40; // x = 40
printf("x : %d\n", x);
// 3중 포인터
int*** ppp = &pp;
***ppp = 60; // x = 60
printf("x : %d\n", x);
return 0;
}
- 2중 포인터와 마찬가지로 3중 포인터의 의미는 3번의 주소 이동을 거쳐 원하는 변수로 도착하는 것이다.
- 3중 포인터 변수 ppp는 pp의 주소를 가지고있고, pp는 2중 포인터 변수이다.
- x의 주소로 이동하여 값을 넣고 싶을 때는 ***ppp를 통해 총 3번의 이동을 하여 x의 주소로 이동한다.
- * 의 의미는 '~로 간다' 라고 생각하면 이해하기 쉽다.
[문제]
- 다중 포인터를 활용한 문제를 하나 풀어보자.
- 문제는 2중 포인터만 사용해서 함수 안의 모든 지역변수의 값을 변경하는 것이다.
- 조건은 오직 pp와 a1만을 이용할 수 있고, 각 지역변수의 값을 변경하는 포인터 변수는 pp를 이용해서만 가능하다.
#include <stdio.h>
void sol1() {
int* p;
int** pp = &p;
int a1;
int a2;
int a3;
printf("&a1 : %ld\n", (long)&a1);
printf("&a2 : %ld\n", (long)&a2);
printf("&a3 : %ld\n", (long)&a3);
printf("&p : %ld\n", (long)&p);
printf("&pp : %ld\n", (long)&pp);
printf("pp : %ld\n", (long)pp);
// 수정가능지역 시작
*pp = &a1; // p = &a1;
*(*pp - 0) = 100;
*(*pp - 1) = 200;
*(*pp - 2) = 300;
// 수정가능지역 끝
printf("a1 : %d\n", a1);
// 출력 => a1 : 100
printf("a2 : %d\n", a2);
// 출력 => a2 : 200
printf("a3 : %d\n", a3);
// 출력 => a3 : 300
}
int main(void) {
printf("== 정답 ==\n");
sol1();
return 0;
}
- sol1() 함수에서 선언된 변수들을 보면 p는 포인터 변수, pp는 2중 포인터변수이고 각 지역변수 a1, a2, a3가 있다.
- 각 지역변수들의 주소들을 출력문을 통해 출력해보면 a1, a2, a3는 각각 차례대로 하나씩 쌓여 주소가 1씩 차이나는 모습을 확인할 수 있다.
- pp는 p의 주소를 담고 있지만 int를 표현할 수 없는 8바이트의 형식이기 때문에 직접적으로 지역변수의 값을 변경할 수는 없다.
- 그렇기 때문에 *pp를 통해 pp의 주소로 이동하면 p가 나오고, 그 p에 a1의 주소를 넣는다.
- *(*pp - 0) = 100; 의 의미는 pp의 주소로 이동하면 p의 주소가 나오고, 해당 p의 주소의 a1의 주소가 들어가 있으니 a1의 주소로 다시 한번 이동해서 100을 넣어준다 라는 의미이다.
- 여기서 0, -1, -2의 차이는 3개의 변수의 주소 차이가 각가 1씩이기 때문에 주소 이동을 한 것이라고 생각하면 된다.
[문제 2]
- 다음은 main 함수 안에있는 int 변수의 값들 a0 ~ a9 중 최대값을 출력해보는 것이다.
- 조건은 오직 변수 a0만을 이용하는 것이다.
#include <stdio.h>
int main(void) {
int a9, a8, a7, a6, a5, a4, a3, a2, a1, a0;
a0 = -10;
a1 = 10;
a2 = 100;
a3 = 210;
a4 = 322210;
a5 = 1440;
a6 = 1130;
a7 = 33210;
a8 = 1210;
a9 = 33210;
// 반복문과 변수 a0 사용 가능
int max_number = a0;
// 수정가능지역 시작
for(int i = 0; i < 10; i++) {
if(max_number < *(&a0 + i)) {
max_number = max_number + *(&a0 + i);
}
printf("%d\n", max_number);
}
// 수정가능지역 끝
printf("최대값은 %d 입니다.\n", max_number);
return 0;
}
- 변수 a9부터 a0까지는 순서대로 int 타입으로 선언되었다.
- 각 변수의 주소를 출력해보면 주소의 값은 4씩 차이가 나고 a9가 가장 먼저 생성되었기 때문에 가장 무거운 주소값을 가지고있다.
- 위 문제의 조건은 a0만을 이용하여 모든 값 중 최대값을 출력하는 것이기 때문에 포인터를 활용해야한다.
- a0의 주소값에서 +1씩 하게 되면 쌓여진 순서에선 점점 무거운 순서로 이동하기 때문에 다른 변수의 주소값은 (&a0 + i) 로 지정하면 된다.
- 반복문을 이용해서 a0 ~ a9의 주소값에 접근해서 해당 값을 최대값과 비교하고, 기존에 있던 최대값보다 더 크면 max_number 값을 계속 갱신하는 코드를 짠다.
- 이 때 각 변수의 주소값이 아닌 해당 주소가 가지고 있는 값을 읽어야하기 때문에 *(&a0 + i) 과 같이 *을 반드시 붙여야한다.
반응형