테마
C 구조체·함수·표준 라이브러리
구조체(structure)는 서로 다른 자료형을 한 묶음으로 다루는 틀이고, 함수(function)는 일을 이름 붙여 나눈 단위다. 시험에서는 구조체 멤버에 .과 -> 중 무엇으로 접근하는지, 함수에 값을 넘길 때와 주소를 넘길 때의 차이, 재귀 호출의 출력 순서, 표준 라이브러리 함수가 어느 헤더에 있는지를 묻는다.
구조체와 멤버 접근
학생의 이름과 점수처럼 함께 다니는 값을 변수 여러 개로 흩어 두면 넘기고 정렬하기가 번거롭다. struct로 묶으면 한 변수처럼 다룰 수 있다. 멤버에 접근하는 방법은 무엇을 들고 있느냐에 따라 둘로 나뉜다.
| 가진 것 | 접근 | 예 |
|---|---|---|
| 구조체 변수 | . | a.x |
| 구조체 포인터(pointer) | -> | p->x |
p->x는 (*p).x를 줄인 표기다. 괄호가 필요한 이유는 .이 *보다 우선순위가 높기 때문이다. 구조체 struct S가 이름 name과 점수 score를 멤버로 가진다면, 구조체 배열은 struct S arr[2] = { {"kim", 70}, {"lee", 90} };처럼 원소마다 중괄호로 묶어 초기화한다. 구조체 포인터 p에 p = arr을 대입해 두면 (p + i)->score는 i번째 원소의 점수다.
값에 의한 전달과 주소에 의한 전달
C의 함수 인자는 기본적으로 값이 복사되어 넘어간다. 이를 값에 의한 호출(call by value)이라 한다. 함수 안에서 매개변수를 바꿔도 복사본만 바뀌므로 호출한 쪽 변수는 그대로다. 호출한 쪽 변수를 바꾸려면 주소를 넘기고 함수 안에서 역참조(dereference)한다. 이를 주소에 의한 호출(call by reference)이라 부른다.1
c
void swapV(int a, int b) { int t = a; a = b; b = t; }
void swapP(int *a, int *b) { int t = *a; *a = *b; *b = t; }
// x = 1, y = 2일 때 swapV(x, y) 뒤 12, swapP(&x, &y) 뒤 21함수 원형과 함수 포인터
C는 위에서 아래로 읽으므로, main 뒤에 정의한 함수를 main에서 부르려면 앞에 int twice(int n);처럼 이름·반환형·매개변수만 적은 함수 원형(function prototype)을 둔다. 함수 이름도 배열 이름처럼 식에서 함수의 시작 주소가 된다. 배열 이름이 첫 원소의 주소로 쓰이듯, 함수 이름은 그 함수 코드가 놓인 메모리 주소로 쓰인다는 뜻이다. 그래서 그 주소를 담는 함수 포인터(function pointer)를 만들어 함수 대신 부를 수 있다.
c
int twice(int n); // 원형: 정의는 main 뒤에 있다
int (*fp)(int) = twice; // int 하나를 받아 int를 돌려주는 함수를 가리킴
printf("%d", fp(4)); // twice(4)와 같다 → 8재귀 함수
재귀 함수(recursive function)는 자기 자신을 다시 호출하는 함수다. 반드시 멈추는 조건이 있어야 하고, 없으면 호출이 끝없이 쌓여 스택 오버플로(stack overflow)로 멈춘다. 출력 문제에서는 재귀 호출 앞에 둔 출력은 내려가며, 뒤에 둔 출력은 되돌아오며 실행된다는 점이 핵심이다.
c
void f(int n) {
if (n == 0) return;
printf("%d", n); // 내려가며 3 2 1
f(n - 1);
printf("%d", n); // 돌아오며 1 2 3
} // f(3) 출력: 321123표준 라이브러리
| 헤더 | 용도 | 대표 함수 |
|---|---|---|
stdio.h | 입출력 | printf, scanf, fprintf, fopen, fclose |
string.h | 문자열 | strlen, strcpy, strcat, strcmp |
stdlib.h | 변환·난수·메모리 | atoi, atof, srand, rand, malloc, free |
math.h | 수학 | sqrt, pow |
strcmp(a, b)는 두 문자열이 같으면 0, a가 사전순으로 앞이면 음수, 뒤면 양수를 돌려준다. malloc(크기)는 실행 중에 바이트 단위로 메모리를 빌려 오는 동적 할당(dynamic allocation) 함수이고, 할당에 실패하면 NULL을 반환하므로 결과를 검사해야 한다. 다 쓴 메모리는 free로 돌려준다. 돌려주지 않으면 프로그램이 도는 동안 쓸 수 있는 메모리가 줄어드는 메모리 누수(memory leak)가 생긴다.
시험 포인트
- 구조체 변수는
., 구조체 포인터는->.p->x == (*p).x. - 값 전달은 원본 불변, 주소 전달은 원본 변경.
- 재귀 호출 앞 출력은 내려갈 때, 뒤 출력은 돌아올 때.
int (*fp)(int) = f;뒤fp(3)은f(3)과 같다. 괄호 없는int *fp(int)는 int 포인터를 돌려주는 함수 선언이다.- malloc은 stdlib.h, 실패 시 NULL 반환, 해제는 free. strlen·strcmp는 string.h.
참고
C에는 C++의 참조형이 없어서 엄밀히는 "주소 값을 복사해 넘기는" 것이다. 시험에서는 이를 주소에 의한 호출로 부른다. ↩︎
