C언어는 실기에서 가장 배점이 큰 언어
회차마다 다르지만, C언어 문제는 대체로 3문제 내외로 가장 많이 나옵니다. 포인터·배열·재귀가 주축이고, 비트 연산과 구조체가 섞여 나오는 해도 있어요.
코드 한 줄에 담긴 포인터 연산을 정확히 따라가는 게 합격 여부를 가르는 편이라, 이 과목만큼은 "대충 아는 것"과 "정확히 아는 것"의 차이가 점수로 바로 드러납니다.
포인터 — 주소, 참조, 값을 분리해서 보기
포인터에서 혼동이 생기는 지점은 거의 항상 세 가지입니다.
&a: 변수a의 주소p: 포인터가 저장하고 있는 주소값*p: 포인터가 가리키는 곳의 값
int a = 10;
int *p = &a;
*p = 20;
printf("%d", a); // 20
포인터를 통해 값을 바꾸면 원래 변수에도 반영됩니다. *p = 20은 "p가 가리키는 곳에 20을 넣어라"라는 뜻이므로 a가 20으로 바뀝니다.
포인터 산술과 배열
int arr[] = {10, 20, 30, 40};
int *p = arr;
printf("%d ", *(p + 2)); // 30
printf("%d ", *p + 2); // 12 ← 괄호 위치 주의
printf("%d", arr[3]); // 40
*(p + 2)는 주소 기준으로 2칸 뒤(=30)*p + 2는*p(=10)에 2를 더한 값(=12)
*(p+2)와 *p+2는 완전히 다른 연산이에요.
이중 포인터
int a = 5;
int *p = &a;
int **pp = &p;
printf("%d", **pp); // 5
**pp는 "pp가 가리키는 포인터가 가리키는 값" → a → 5.
문자열 — strlen vs sizeof
char s[] = "HELLO";
printf("%d ", strlen(s)); // 5 (널 문자 제외)
printf("%d", sizeof(s)); // 6 (널 문자 포함)
strlen은 문자열 길이(널 문자\0제외)sizeof는 배열 전체 바이트 수(널 포함)
이 차이를 묻는 문제가 자주 나옵니다.
비트 연산 — AND / OR / XOR / NOT
비트 연산은 2진수로 변환해서 계산해야 합니다.
int a = 12; // 1100
int b = 10; // 1010
printf("%d ", a & b); // 1000 = 8
printf("%d ", a | b); // 1110 = 14
printf("%d ", a ^ b); // 0110 = 6
printf("%d ", a << 2); // 110000 = 48 (왼쪽 시프트: × 4)
printf("%d", a >> 1); // 0110 = 6 (오른쪽 시프트: ÷ 2)
&: 둘 다 1일 때 1|: 하나라도 1이면 1^: 서로 다르면 1 (같으면 0)<<,>>: 각 비트를 왼쪽/오른쪽으로 이동
재귀 함수 — 호출 스택을 그리면서 따라가기
int fn(int n) {
if (n <= 1) return 1;
return n * fn(n - 1);
}
printf("%d", fn(5)); // 120
fn(5) = 5 × fn(4) = 5 × 4 × fn(3) = 5 × 4 × 3 × fn(2) = 5 × 4 × 3 × 2 × fn(1) = 120.
실기에서는 재귀식이 팩토리얼처럼 단순하지 않은 경우가 많습니다. 종료 조건을 먼저 확인하고, 값이 들어가는 방향을 종이에 그려보세요.
구조체와 typedef
typedef struct {
char name[20];
int age;
} Person;
Person p = {"Kim", 25};
printf("%s %d", p.name, p.age); // Kim 25
typedef를 쓰면 struct Person p가 아니라 Person p로 선언할 수 있습니다. 구조체 포인터일 때는 -> 연산자를 씁니다.
Person *pp = &p;
printf("%s", pp->name); // Kim
pp->name은 (*pp).name과 같은 의미입니다.
동적 할당 — malloc과 free
int *arr = (int*)malloc(sizeof(int) * 5);
for (int i = 0; i < 5; i++) arr[i] = i * 10;
printf("%d", arr[3]); // 30
free(arr);
malloc은 요청한 크기의 힙 메모리를 할당하고 그 주소를 반환- 반환 타입이
void*이므로 필요한 타입으로 캐스팅 - 다 쓴 뒤
free로 반환하지 않으면 메모리 누수
시험에서 자주 틀리는 지점 정리
| 헷갈리는 지점 | 정답 기준 |
|---|---|
*(p+1) vs *p+1 | 괄호 안이 먼저 계산 |
strlen(s) vs sizeof(s) | 널 문자 포함 여부 |
a++ vs ++a | 전자는 반환 후 증가, 후자는 증가 후 반환 |
&a[i] | a + i와 동일 |
배열 이름 arr | 배열의 첫 원소 주소(&arr[0])와 동일 |
정리
C언어는 "포인터가 가리키는 것이 무엇인가"를 매 순간 머릿속에서 그릴 수 있으면 거의 다 풀립니다. 다른 영역과 달리 암기보다 추적 능력이 점수를 결정하는 과목이에요.
한 문제를 풀 때 값이 어떻게 변했는지를 주석처럼 여백에 써 보는 연습을 반복해 두면, 시험장에서 긴장한 상태에서도 흔들리지 않습니다.