본문 바로가기

Studying/Computer Programs

C언어 프로그래밍 기초 : 함수와 라이브러리

이번 포스팅에서는 C언어 혹은 C++를 이용해서 복잡하면서도 규모가 큰 프로그램을 작성하는데 필수적인 요소인 함수 (function)와 라이브러리 (library)에 대해서 알아봅시다. 함수를 정의하고 호출하기 위한 방법과 함께, 라이브러리가 어떻게 구성되어 있는지도 짚어보겠습니다.

 

반응형

 

함수를 정의하고 사용하기 위해서는 함수가 어떤 형태의 데이터를 받아들이고 돌려주는지를 명확히 해야 합니다. 이를 위해서 매개변수와 리턴값의 자료형을 지정하게 되는데요. C언어나 C++ 프로그램에 등장하는 변수들의 여러가지 자료형의 특징에 대해서는 다음 포스팅에 더 자세하게 소개되어 있습니다.

 

 

C언어 프로그래밍 기초 : 변수와 자료형

여기서는 C언어로 프로그램을 작성하는데 있어서 가장 기본적인 요소들 중 하나라고 할 수 있는 변수 (variable)에 대해서 알아봅시다. 변수들의 종류를 지정하기 위한 다양한 자료형 (data type)들이

swstar.tistory.com

 

C/C++ 함수의 역할을 정의하는데 있어서 변수들의 값이 어떻게 변하는지를 지정하는 것이 상당히 큰 비중을 차지합니다. 변수의 값을 지정하거나, 사칙연산 등을 통해 그 값을 바꾸는 방법에 대한 더 자세한 내용은 다음 포스팅에 소개되어 있습니다.

 

 

C언어 프로그래밍 기초 : 연산문과 사칙연산

이번 포스팅에서는 C언어 또는 C++ 프로그램에 등장하는 변수들의 값을 바꾸기 위한 연산문들에 대해 알아봅시다. 여러 개의 연산문들을 순차적으로 실행함으로써, 궁극적으로는 원하는 기능을

swstar.tistory.com

 

함수

C/C++ 프로그램에서의 함수는 매개변수의 값들을 받아서 리턴값을 돌려주는 일련의 과정을 정의한 것으로서, 수학에서 등장하는 함수와 상당한 유사점이 있습니다. 함수의 매개변수가 가질 수 있는 값들의 집합이 정의역이라고 볼 수 있고, 함수가 되돌려 줄 수 있는 값들이 치역을 이룬다고 볼 수 있죠.

 

프로그램을 작성하다보면 동일한 기능을 여러번 반복해서 써야 할 때가 있는데, 이를 구현한 소스 코드를 필요할때마다 복사해서 붙여넣으면 코드가 불필요하게 길어지고 그 과정에서 실수를 할 가능성도 있습니다. 특정한 기능을 구현하는 소스 코드를 묶어서 함수로 정의해 놓으면, 그 함수를 여러번 호출하기만 하면 되기 때문에 편리해집니다.

 

함수를 정의하는 것은 일반적으로 다음과 같은 형식을 따릅니다.

 

[리턴 자료형] [함수 이름]([매개변수1 자료형] [매개변수1 이름],
        [매개변수2 자료형] [매개변수2 이름], ... ) {
    [명령문1]
    [명령문2]
    ...

    return [리턴 값];
}

 

함수를 호출했을 때 돌려받는 리턴값의 자료형이 맨 앞에 등장하고, 그 다음에 함수의 이름이 나옵니다. 함수의 이름은 알파벳 대소문자 및 밑줄 문자 (_)를 사용해서, 기능에 맞게 지어주면 되겠습니다. 만약 함수가 특정한 값을 돌려주지 않고, 명령문들만 실행한다면 리턴 자료형으로는 void를 사용하면 됩니다.

 

함수 이름 다음에는 함수가 받아들이는 매개변수 (parameter)의 자료형과 이름을 괄호 안에 넣어주면 되는데요. 여러개의 매개변수를 받는 다변수함수의 경우, 쉼표 (,)로 구분을 해주면 됩니다. 매개변수의 이름은 중복되지 않는 범위 내에서 임의대로 지을 수 있습니다. 여기서 사용되는 매개변수의 이름들은 함수의 기능을 정의하는 데 사용되고, 함수를 호출할 때는 다른 이름을 가진 변수도 매개변수로 사용할 수 있습니다.

 

마지막으로 중괄호 ({  })안에 순차적으로 실행시킬 명령문들을 넣어주면 되고, 매개변수의 이름을 통해 그 안에 저장된 값들을 가져다 쓸 수 있습니다. 그리고 return 실행문을 통해서 함수가 돌려주고자 하는 값을 지정해줄 수 있습니다. 만약 리턴 자료형을 void로 했다면, 별도의 리턴 값 없이 return 실행문만 있으면 함수가 종료됩니다. 한가지 더 짚고 넘어가자면, 함수가 호출되었을 때 return 실행문을 만나면 그 다음에 다른 실행문들이 남아있는 경우에도 함수가 종료됩니다.

 

프로그램의 가장 간단한 예시인 "Hello World!"를 출력함과 더불어 정수의 제곱을 계산하는 간단한 프로그램을 통해서 함수의 기본 개념과 구성요소들을 더 자세히 짚어봅시다.

 

C언어 소스 코드입니다.

 

/* 출력을 위한 printf 함수의
 * 프로토타입이 선언된 헤더 파일
 * (표준 입출력 라이브러리) */
#include<stdio.h>

/* get_square 함수의 프로토타입
 * (prototype) */
int get_square(int);

// main 함수의 정의
int main(int argc, char *argv[]) {
    printf("\n");
    printf("Hello World!\n");
    printf("\n");

    int a;
    int a_sqr;

    // 변수 a의 값을 2로 저장
    a = 2;
    /* 변수 a에 저장된 값을 가지고
     * get_square 함수를 호출한 다음,
     * 리턴값을 변수 a_sqr에 저장 */
    a_sqr = get_square(a);
    printf("  %d square = %d\n",
        a, a_sqr);

    a = 3;
    a_sqr = get_square(a);
    printf("  %d square = %d\n",
        a, a_sqr);

    printf("\n");

    return 0;
}

// get_square 함수의 정의
int get_square(int a) {
    int val_ret = a * a;
    return val_ret;
}

 

소스 코드를 컴파일해서 실행시켜보면 다음과 같은 결과를 얻을 수 있습니다.

 

screenshot of terminal console, showing the result of an example program to print

 

예제 프로그램에는 두 개의 함수가 정의되어 있습니다. main 함수와 get_square라는 함수가 있는데요. 제일 먼저 주목해야 할 것은 C/C++ 프로그램에서 가장 핵심이 되는 main 함수입니다. 소스 코드를 가지고 프로그램을 만든 다음, 이를 실행시키면 바로 main 함수에 있는 내용들을 순차적으로 실행하게 됩니다. 따라서 소스 코드 전체를 통틀어서 main 함수가 반드시 하나 있어야 합니다. 아예 없어도 안되고 2개 이상 중복으로 있어도 안됩니다. main 함수의 맨 마지막에서 0을 리턴하는 것은 프로그램이 정상적으로 실행되었다는 것을 나타내기 위한 일반적인 방법이고, 이는 생략이 가능합니다.

 

정수의 제곱을 계산해서 돌려주는 기능이 get_square 함수에 정의되어 있습니다. 매개변수의 이름이 a라고 주어졌을 때, a의 제곱을 계산해서 그 값을 정수형 변수 val_ret에 저장하는 연산문이 포함되어 있는 것을 볼 수 있죠. 마지막으로 return을 사용해서 val_ret에 저장된 제곱 값을 돌려주게 됩니다. main 함수에서 get_square 함수를 호출할 때는 그 리턴값을 a_sqr이라는 변수에 저장하고 있습니다.

 

참고로 main 함수에서 get_square 함수를 호출할 때 들어가는 매개변수 역시 그 이름이 a인 것을 볼 수 있는데요. get_square 함수를 정의할 때와 같은 이름을 쓰는데도 중복이 되지 않는 이유는 main 함수 내에서 선언된 a는 변수의 범위 (scope)가 서로 겹치지 않아서 다른 변수로 간주되기 때문입니다.

 

C언어나 C++ 함수와 관련된 또 다른 중요한 개념으로서 원형 또는 프로토타입 (prototype)이라는 것이 있습니다. 예제 코드를 살펴보면 get_square 함수의 기능이 main 함수 밑에 정의되어 있는 반면에, get_square 함수의 이름과 함께 매개변수와 리턴값의 자료형이 main 함수 위에 선언되어 있는 것을 볼 수 있는데요. 이를 두고 함수의 프로토타입을 선언한다고 하며, 다음과 같은 형식을 가지고 있습니다.

 

[리턴 자료형] [함수 이름]([매개변수1 자료형], [매개변수2 자료형], ... );

 

함수의 내용을 정의할때와는 달리 마지막에 세미콜론 (;)이 들어가는 것이 특징입니다. 프로토타입의 주요 목적은 함수의 존재를 알리는 것으로서, 여러개의 소스 파일로 이루어진 대규모 프로그램을 작성하는데 있어서 필수적인 개념이라 할 수 있죠. 일반적으로 프로토타입은 헤더 파일에 선언되어 있고, 여러 소스 파일들의 맨 위에 헤더 파일의 내용을 #include 전처리기를 통해 포함시키는 방법이 사용됩니다.

 

예제 프로그램에서도 맨 위에 stdio.h라는 헤더 파일을 포함시켰습니다. 문자열을 화면에 출력하기 위해서 printf라는 함수를 사용했는데요. 이 함수를 호출할 수 있는 이유는 printf 함수의 프로토타입이 stdio.h 헤더 파일에 선언되어 있고, 그 기능이 C 표준 라이브러리에 구현되어 있기 때문입니다.

 

라이브러리

C언어나 C++ 라이브러리는 자주 사용하는 함수들을 모아놓은 일종의 패키지라고 할 수 있습니다. 우리가 앞에서 printf라는 함수를 사용할 수 있었던 것도 이 함수가 C언어 표준 입출력 라이브러리에 포함되어 있기 때문이죠. printf 함수의 프로토타입이 선언되어 있는 stdio.h 헤더 파일을 포함시키고, 소스 코드로부터 실행 파일을 만드는 과정에서 표준 입출력 라이브러리가 링크 되었던 것입니다.

 

라이브러리는 일반적으로 함수들의 프로토타입이 선언되어 있는 헤더 파일들과, 함수들의 구체적인 역할에 대한 정보를 담고 있는 라이브러리 파일들로 구성되어 있습니다. 소스 코드를 작성할 때는 헤더 파일을 포함시켜서 함수들의 존재를 알리고, 실행파일을 만들기 위해 프로그램을 빌드하는 단계에서 라이브러리 파일을 링크함으로써 함수들이 어떤 역할을 하는지에 대한 구체적인 정보가 들어가게 됩니다.

 

C언어 컴파일러를 설치하면 기본적으로 포함되어 있는 표준 라이브러리 (C standard library)라는 것이 있는데요. 미국 국가표준 협회 (American National Standard Institution, 줄여서 ANSI) 및 국제 표준화 기구 (International Organization for Standardization, 줄여서 ISO)에서 발표한 표준 C언어에 내장된 라이브러리를 말합니다. 표준 라이브러리 중에서 자주 사용되는 것들을 열거하자면 다음과 같습니다.

 

  • 입출력 (I/O)
    헤더 파일 : stdio.h
    모니터 스크린이나 파일에 데이터를 출력하거나, 반대로 데이터를 입력받기 위한 함수들이 정의되어 있습니다. 대표적으로 화면에 문자열을 출력하는 printf 함수와 화면을 통해 데이터를 입력할 수 있는 scanf 함수가 있죠. 파일 입출력을 위한 함수들도 있습니다. 문자열을 통한 입출력을 위해서는 fscanffprintf 함수들을 사용할 수 있고, freadfwrite 함수들을 통해 바이트 단위의 입출력도 가능합니다.
  • 일반 유틸리티
    헤더 파일 : stdlib.h
    동적 메모리 할당을 위한 mallocfree 함수 등과 더불어, 문자열을 정수나 실수로 변환하기 위한 atoiatof 함수들이 정의되어 있습니다. 난수생성 함수를 초기화하는 srand 함수와 난수를 발생시기키 위한 rand 함수 역시 이 일반 유틸리티 라이브러리에 포함되어 있습니다.
  • 문자열 처리
    헤더 파일 : string.h
    문자열을 다루기 위한 함수들을 포함하고 있습니다. 문자열을 복사하기 위한 strcpy 함수나, 문자열을 이어붙이기 위한 strcat 함수 등을 사용할 수 있습니다. 문자열의 길이를 리턴하는 strlen 함수와 문자열을 비교하는 strcmp 함수 역시 이 라이브러리에 포함되어 있습니다.
  • 수학 함수
    헤더 파일 : math.h
    수학에서 등장하는 여러 함수들이 정의되어 있는 라이브러리입니다. 대표적으로 삼각함수 (코사인 cos, 사인 sin, 탄젠트 tan), 역삼각함수 (아크코사인 acos, 아크사인 asin, 아크탄젠트 atan), 거듭제곱 (pow), 지수함수 (자연지수 exp), 로그함수 (자연로그 log, 상용로그 log10) 등이 있습니다. 원주율 (M_PI)과 오일러 상수 (M_E) 등의 수학 관련 상수들 역시 매크로로 정의되어 있죠. 리눅스나 유닉스에서 함수의 정의를 찾을 수 없다면서 링크 오류가 뜨는 경우가 있는데, 이 경우 -lm 옵션을 통해 라이브러리 파일 (libm.a)을 직접 링크해주면 문제를 해결할 수 있습니다.

 

표준 라이브러리에는 여기서 소개된 함수들 이외에도 다양한 함수들이 있는데요. 블로그 포스팅 하나에 모두 담기에는 내용이 워낙 방대해서 필요한 기능을 별도로 찾아가면서 사용하는 것이 좋습니다. 제가 개인적으로 애용하는 웹사이트로 cplusplus.com이 있습니다.

 

 

C library - C++ Reference

 

www.cplusplus.com

 


 

함수의 내용을 정의하는 과정에서 스스로를 호출할 수도 있는데요. 이를 두고 재귀호출 (recursive call)이라고 하며, 이를 잘 활용하면 더 다양한 기능을 쉽게 구현할 수 있습니다. 재귀호출의 알고리즘과 예시에 대한 더 자세한 내용은 다음 포스팅에 소개되어 있습니다.

 

 

C/C++ 함수가 스스로를 부르는 재귀호출

여기서는 C언어 또는 C++ 프로그램에서의 재귀호출 (recursive call)에 대해서 다뤄보겠습니다. 재귀호출에 대해 간단히 말하자면, 특정한 함수가 스스로를 호출하는 과정을 말하는데요. 재귀호출의

swstar.tistory.com

 

앞에서 소개한 예제 프로그램의 main 함수가 argcargv라는 이름의 매개변수들을 받고 있는것을 볼 수 있는데, 이들을 명령행 인자 (command-line argument)라고 부릅니다. 터미널이나 명령 프롬프트에서 실행파일 이름 다음에 띄어쓰기로 분리된 여러 단어들을 입력하면, 이들의 갯수와 내용이 명령행 인자들에 저장되게 됩니다. 더 자세한 내용이 궁금하시다면, 다음 포스팅이 큰 도움이 되리라 생각합니다.

 

 

Command-line arguments (C/C++ 명령행 인자)

C언어 또는 C++ 의 메인 (main)함수를 특별한 방법으로 정의해서, 프로그램을 실행시킬때 커맨드 라인에서 문자열들을 전달해줄 수 있습니다. 그러면 이들이 명령행 인자에 저장되어 프로그램 내

swstar.tistory.com

 

표준 라이브러리 이외에도 외부에서 받아오거나 직접 제작한 라이브러리를 사용하는 것이 가능합니다. 외부 라이브러리를 만들고 사용하기 위한 구체적인 방법은 다음 포스팅들에 더 자세하게 소개되어 있습니다.

 

리눅스/유닉스

 

C언어 라이브러리 만들기

자주 쓰는 함수들을 라이브러리 형태로 만들어 두면, 프로그램을 짤 때 편리합니다. 오브젝트 파일로부터 라이브러리를 제작하고, 이를 헤더 파일과 함께 이용하는 방법을 알아볼텐데요. 만약

swstar.tistory.com

 

비주얼 스튜디오

 

비주얼 스튜디오 C/C++ 라이브러리 만들기

이번 포스팅에서는 마이크로소프트 비주얼 스튜디오에서 C언어 또는 C++ 라이브러리를 제작하고 사용하는 법에 대해 알아봅시다. 라이브러리는 정적 (static) 라이브러리와 동적 (dynamic) 라이브러

swstar.tistory.com

 


 

C언어나 C++ 소스 코드를 가지고 실행이 가능한 프로그램을 만드는 과정에 대한 더 자세한 내용은 다음 포스팅에 소개되어 있습니다.

 

 

C/C++ 코드가 프로그램이 되는 과정

여기서는 C언어 또는 C++로 작성된 소스 파일, 헤더 파일의 개념과 이들을 빌드하여 하나의 프로그램을 만드는 과정에 대해 간략하게 짚어보겠습니다. 여러 개의 소스, 헤더 파일들로 이루어진

swstar.tistory.com