좋아요! 🎯
이번에는 C언어 포인터 트릭 문제를 하나 준비해 드릴게요. 포인터가 꼬이는 문제는 정보처리기사 실기에서 단골이니, 철저히 디버깅식 풀이를 같이 보여드릴 거예요. 💡
다음 코드를 보고 최종 출력값을 유추해 보세요.
#include <stdio.h>
int main() {
int a = 5;
int b = 10;
int c = 15;
int *p1 = &a;
int *p2 = &b;
int *p3 = &c;
int **pp1 = &p1;
int **pp2 = &p2;
*pp1 = p3; // 1
*pp2 = p1; // 2
*p1 = 100; // 3
*p2 = 200; // 4
printf("%d %d %d\n", a, b, c);
printf("%d %d %d\n", *p1, *p2, *p3);
return 0;
}
a = 5, b = 10, c = 15
p1 → a
p2 → b
p3 → c
pp1 → p1
pp2 → p2
메모리 상태 그림:
[ a(5) ] <- p1
[ b(10)] <- p2
[ c(15)] <- p3
pp1 -> p1(->a), pp2 -> p2(->b)
*pp1 = p3; (1)pp1은 p1의 주소를 가지고 있으므로 *pp1 = p3; ⇒ p1 = p3.p1 → c(15)*pp2 = p1; (2)pp2은 p2의 주소를 가지고 있으므로 *pp2 = p1; ⇒ p2 = p1.p1 → c(15)
p2 → c(15)
p3 → c(15)
a(5), b(10), c(15)
→ 이제 p1, p2, p3 모두 c를 가리킴.
*p1 = 100; (3)*p2 = 200; (4)printf("%d %d %d\n", a, b, c); // a=5 b=10 c=200 출력
printf("%d %d %d\n", *p1, *p2, *p3); // p1,p2,p3 → c(200), 출력: 200 200 200
5 10 200
200 200 200
✅ pp1, pp2 같이 이중 포인터가 나올 경우 포인터가 어디를 가리키고 있는지를 반드시 추적.
✅ 포인터에 포인터를 대입하면 대상의 참조가 바뀌므로 이후의 값 변경에 주의.
✅ 최종 출력 전에 메모리 상태를 한 번 그림 그리듯 정리하는 습관 들이면 트릭 문제에도 끄떡없어요.
좋아요! 🔥 이번엔 포인터 트릭 문제를 다양한 유형(포인터+배열, 구조체 포인터, 함수 포인터)으로 준비할게요. 그리고 각 문제마다 자세히 디버깅식 풀이까지 보여 드릴게요. 🎯
다음 코드를 보고 출력값을 예측해보세요:
#include <stdio.h>
int main() {
int arr[2][3] = {{1,2,3},{4,5,6}};
int (*p)[3] = arr;
printf("%d ", *(*(p+1) + 2));
printf("%d ", *(*p + 1));
return 0;
}
arr[2][3] 구조:arr[0]: 1 2 3
arr[1]: 4 5 6
p = arr; → p는 int[3]를 가리키는 포인터 → p가 arr[0] 시작 주소를 참조*(*(p+1) + 2) 부분:p+1 → arr[1]을 가리킴*(p+1) → arr[1] 자체(주소), 거기에 +2 → arr[1][2]의 주소*(*(p+1)+2) → arr[1][2] 값 = 6*(*p + 1) 부분:*p → arr[0]*p + 1 → arr[0][1]의 주소*(*p + 1) → arr[0][1] 값 = 26 2
다음 코드를 보고 출력값을 유추해 보세요:
#include <stdio.h>
typedef struct {
int x;
int y;
} Point;
int main() {
Point p1 = {10, 20};
Point p2 = {30, 40};
Point *pp = &p1;
*pp = p2;
pp->x = 50;
printf("%d %d\n", p1.x, p1.y);
return 0;
}
p1 = (10,20), p2 = (30,40), pp → p1
*pp = p2;pp->x = 50;50 40
다음 코드를 보고 출력값을 유추해 보세요:
#include <stdio.h>
int add(int a, int b) { return a+b; }
int sub(int a, int b) { return a-b; }
int main() {
int (*funcs[2])(int,int) = {add, sub};
int x = funcs[0](10, 5) + funcs[1](10, 5);
printf("%d\n", x);
return 0;
}
funcs[0] → add, funcs[1] → sub
funcs[0](10, 5) → add(10, 5) = 15
funcs[1](10, 5) → sub(10, 5) = 5
x = 15 + 5 = 20
20
20
✅ 포인터 + 배열 트릭: *(p+1) + n → 반드시 구조를 그림 그리며 계산
✅ 구조체 포인터 트릭: *pp = 다른 구조체 → 전체 복사, pp->멤버 → p1의 멤버 참조
✅ 함수 포인터 트릭: 함수 이름을 배열에 담아 호출 가능
좋아요! 🎯
이번에는 **double pointer(이중 포인터)**와 malloc 메모리 할당 트릭 문제를 내고, 한 줄씩 꼼꼼히 풀이해 드릴게요.
포인터와 동적 메모리 관리에서 실수하기 쉽지만, 잘 이해하면 실기 고득점 가능!
#include <stdio.h>
void update(int **p) {
static int x = 10;
*p = &x;
}
int main() {
int a = 5;
int *p = &a;
update(&p);
printf("%d\n", *p);
return 0;
}
a = 5, p는 a 주소를 가리킴update(&p) 호출 → p의 주소를 전달 (즉, int **p는 p를 가리킴)static int x = 10; 선언 → 함수가 끝나도 사라지지 않는 변수 x*p = &x; → p가 가리키는 변수(p 자체)가 이제 x의 주소를 가리키게 변경됨main의 p는 더 이상 a가 아니라 x를 가리킴printf("%d\n", *p); → *p는 x 값 → 10 출력10
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = malloc(sizeof(int) * 3);
p[0] = 1;
*(p + 1) = 2;
p[2] = 3;
int *q = p;
free(p);
printf("%d %d %d\n", q[0], q[1], q[2]);
return 0;
}
malloc(sizeof(int) * 3) → 3개의 int 공간 할당 (정상적으로 할당됨)
p[0] = 1;, p[1] = 2;, p[2] = 3; 값 저장
int *q = p; → q도 같은 메모리를 가리킴 (얕은 복사)
free(p); → 메모리 해제 (p와 q가 가리키는 메모리가 OS에 반환됨)
그 뒤 printf에서 q[0] q[1] q[2]를 출력하려 하지만, 메모리는 해제됐으므로 결과는 불확실
1 2 3 또는 이상한 값이 출력됨(Undefined Behavior)| 포인터 종류 | 특징 및 주의점 |
|---|---|
| 이중 포인터 | 포인터를 가리키는 포인터. 함수에서 포인터 자체를 변경할 때 유용. |
| malloc | 동적 메모리 할당. 할당 후 사용하다가 free 하면 메모리 반환됨. 반환 후 참조는 위험. |
update(&p) 처럼 이중 포인터를 넘겨서 함수 내에서 포인터 값 변경 가능malloc 후 반드시 free 하고, free 후 메모리 참조하면 안 됨(미정의 동작)