Skip to content
난이도 출제 빈출문제은행 6

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.

참고

  1. C에는 C++의 참조형이 없어서 엄밀히는 "주소 값을 복사해 넘기는" 것이다. 시험에서는 이를 주소에 의한 호출로 부른다. ↩︎

관련 문서