정보처리기사 실기에서 코딩 문제는 “직접 짜라”가 아니라 “이 코드의 출력은 무엇인가”를 묻는다. 특히 C언어는 포인터와 배열의 관계를 손으로 추적할 수 있으면 대부분 풀린다. 암기가 아니라 규칙을 아는 문제다.
배열 이름은 첫 원소의 주소다
C에서 배열 이름 a는 사실상 &a[0], 즉 첫 원소의 주소로 취급된다. 그래서 아래 두 표현은 완전히 같다.
a[i] == *(a + i)
여기서 a + i는 “주소에 i를 더한다”가 아니라 “i번째 원소로 건너뛴다”는 뜻이다. int가 4바이트든 뭐든, a + 1은 다음 int를 가리킨다. 자료형 크기는 컴파일러가 알아서 곱해준다. 이 규칙 하나가 포인터 문제의 절반이다.
대표 유형 1 — 주소 연산 출력
#include <stdio.h>
int main() {
int a[5] = {10, 20, 30, 40, 50};
int *p = a;
printf("%d\n", *(p + 2)); // ①
printf("%d\n", *p + 1); // ②
p++;
printf("%d\n", *p); // ③
printf("%d\n", *(a + 3)); // ④
return 0;
}
손으로 따라가 보자.
- ①
*(p + 2)— p는 a[0]을 가리키므로 두 칸 건너 a[2] = 30. - ②
*p + 1— 괄호가 없으니 먼저*p(=a[0]=10)를 구하고 1을 더한다. 11.*(p+1)과 헷갈리게 하는 단골 함정이다. - ③
p++뒤 p는 a[1]을 가리킨다.*p= 20. - ④
*(a + 3)= a[3] = 40. 배열 이름 a는 그대로 a[0] 주소라 p와 무관하다.
핵심은 *와 +의 우선순위다. *p + 1은 (*p) + 1이고, 값을 하나 더하는 것이지 다음 칸이 아니다. 이런 문제는 종이에 인덱스와 값을 표로 적어 내려가면 실수가 사라진다.
대표 유형 2 — 포인터로 값 바꾸기(swap)
#include <stdio.h>
void swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
int main() {
int a = 3, b = 7;
swap(&a, &b);
printf("%d %d\n", a, b); // 7 3
return 0;
}
값을 넘기면(call by value) 함수 안의 복사본만 바뀌고 원본은 그대로다. 그래서 &a, &b로 주소를 넘기고, 함수는 *x로 그 주소의 값을 직접 고친다. 출력은 7 3. “포인터를 안 쓰면 왜 안 바뀌나”를 물으면, 인자로 값만 복사되기 때문이라고 답하면 된다. 반대로 swap(a, b)처럼 값을 넘겼다면 main의 a·b는 그대로 3 7이 나온다.
대표 유형 3 — 포인터와 문자열
char *s = "ABCDE";
printf("%c\n", *(s + 2)); // C
printf("%c\n", s[4]); // E
문자열 리터럴도 결국 문자 배열의 첫 주소다. s[i]와 *(s + i)가 같다는 규칙이 그대로 적용된다. %c는 문자 하나, %s는 그 주소부터 널 문자(\0)까지를 통째로 출력한다는 점도 함께 나온다.
시험에서 실수하는 지점
*p++—*(p++)로 읽는다. 현재 값을 쓰고 나서 p가 다음 칸으로 이동한다.(*p)++(값 자체를 증가)와 다르다.a는 상수 주소 — 배열 이름에는a++를 못 한다. 이동하려면 별도 포인터 p에 담아p++한다.- 주소 vs 값 —
printf에서%d로p를 찍으면 값이 아니라 주소가 나온다. 값은*p. &와*는 짝 —&는 값에서 주소를 얻고,*는 주소에서 값을 얻는다.*(&a)는 다시a가 된다.
C 포인터 문제는 규칙이 몇 개 안 된다. a[i] == *(a+i), 연산자 우선순위, 그리고 주소를 넘겨야 원본이 바뀐다는 것. 이 셋을 쥐고 한 줄씩 표로 값을 적어 내려가면 대부분의 실기 코드 추적 문제가 풀린다.
이 글에 대한 의견은 아래 댓글로 남겨주세요 (GitHub 계정 필요). 로그인 없이 남기고 싶다면