Home [C++ 프로그래밍] 4강 - 함수
Post
Cancel

[C++ 프로그래밍] 4강 - 함수

💡해당 게시글은 방송통신대학교 이병래 교수님의 'C++ 프로그래밍' 강의를 개인 공부 목적으로 메모하였습니다.



학습 개요


  • 함수는 C++ 프로그램을 구성하는 기본 단위
  • 함수의 기본적인 형식과 함께, 함수에 인수를 전달하는 방법, 개념적으로 동일한 처리를 대상에 맞게 정의할 수 있게 하는 함수 다중 정의, 효율적인 프로그램 작성이 매우 중요한 경우 활용할 수 있는 inline 함수에 대하여 학습함
  • 함수 자체는 C 언어에서와 동일하나, 참조 호출, 함수 다중 정의와 같은 기능은 C++에서 확장된 부분으로 많이 사용되는 내용임



학습 목표


  • 함수의 머리부와 몸체부를 문법에 맞게 작성할 수 있음
  • 값 호출과 참조 호출을 할 경우 형식 매개변수와 실 매개변수 사이에 인수가 어떻게 전달되는지 설명할 수 있음
  • 동일한 이름의 함수가 인수에 따라 서로 다른 동작을 하도록 함수를 정의할 수 있음
  • 필요한 경우 함수의 호출 과정이 효율적으로 이루어질 수 있도록 지정할 수 있음



주요 용어


  • 순환 호출(recursive call)
    • 함수에서 직접 또는 간접적으로 그 함수를 호출하는 것
  • 인수(argument)
    • 함수 호출 문장에서 함수에 전달하기 위해 나열된 식
  • 실 매개변수(actual parameter)
    • 함수 호출 문장에서 함수의 형식 매개변수에 전달할 인수
  • 형식 매개변수(formal parameter)
    • 인수를 전달 받기 위해 함수에 선언된 매개 변수
  • 값 호출(call-by-value)
    • 실 매개 변수의 값을 형식 매개변수에 복사하는 매개변수 전달 방식
  • 참조 호출(call-by-reference)
    • 실 매개 변수의 참조를 형식 매개변수에 전달하는 매개변수 전달 방식
  • 함수 다중 정의
    • 동일한 이름을 갖는 함수를 여러 개 정의하는 것



강의록


함수의 정의와 호출

함수의 개념

  • 다음 프로그램이 하는 작업은?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      int main()
      {
          double a[50], b[100];
          // 생략: a와 b에 각각 50개와 100개의 데이터 준비
          double max = a[0];
          for (int i = 1; i < 50; i++)
              if (max < a[i])
                  max = a[i];
          cout << max << endl;
          max = b[0];
          for (int i = 1; i < 100; i++)
              if (max < b[i])
                  max = b[i];
          cout << max << endl;
      }
    
  • 함수를 이용하여 작성된 프로그램

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      double maximum(const double arr[], int n)
      {
          double max = arr[0];
          for (int i = 1; i < n; i++)
              if (max < arr[i]) max = arr[i];
          return max;
      }
        
      int main()
      {
          double a[50], b[100];
          // 생략: a와 b에 각각 50개와 100개의 데이터 준비
          cout << maximum(a, 50) << endl;
          cout << maximum(b, 100) << endl;
      }
    
  • 함수란?

    • 특정 작업을 수행하는 프로그램 문장들을 하나의 단위로 모아 놓고 이름을 부여한 것
    • 함수에 정의된 처리가 필요한 부분에서 호출하여 사용함
      • 매개 변수를 통해 함수에서 처리할 데이터(인수)를 전달함
      • 호출된 함수로 이동하여 함수 몸체 블록을 실행함
      • 정해진 처리를 한 후 결과 값을 반환할 수 있음
      • 함수의 실행을 완료하면 호출한 곳으로 복귀함
    • C++ 프로그램은 함수를 기본 단위로 하여 구성됨

함수 사용 형식

  • 함수 정의 형식

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
      ReturnType functionName(fParameterList) // 머리부
      { // 몸체 블록
          Type1 localVar1; // 지역변수 선언
          Type2 localVar2;
        
          statement1; // 처리 작업을 수행하는 문장
          statement2;
        
          return returnExpression; // 결과 값을 반환함
      }
    
    • fParameterList
      • 인수를 받기 위한 형식 매개 변수 선언
    • ReturnType
      • 함수의 결과로 반환하는 값의 자료형
    • returnExpression
      • 함수의 결과로 반환하는 값
  • return명령
    • 함수를 마치고 함수를 호출한 곳으로 복귀하는 명령
    • 함수 안의 어느 곳이든 복귀를 위해 사용할 수 있음
    • returnExpression은 함수 머리 부에 선언한 ReturnType과 일치하는 자료형의 수식 또는 묵시적 형 변환이 가능한 자료형의 수식을 사용함
    • 반환하는 값이 없는 함수는 ReturnTypevoid로 선언함
    • main함수에서는 return명령을 만나지 않은 상태로 함수의 끝에 도달하면 return 0;을 실행한 것 같음
  • 함수 호출 형식
    1. functionName(aParameterList);
    2. varName = functionName(aParameterList);
      • aParameterList
        • 인수로 전달할 실 매개 변수 나열
      • 형식 1
        • 반환 값 유무에 관계없이 사용 가능함
      • 형식 2
        • ReturnTypevoid가 아닌 함수에 사용할 수 있음

함수 사용 예 - ConvFtoC.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

// 화씨온도를 섭씨온도로 변환하는 함수
// 매개변수 float fahr: 화씨온도
// 반환값 섭씨온도(float)
float FahrToC(float fahr)
{
    return (fahr - 32) * 5 / 9;
}

int main()
{
    float fTemp, cTemp;
    cout << "화씨온도 : ";
    cin >> fTemp;
    cTemp = FahrToC(fTemp); // 함수 호출
    cout << "---> 섭씨온도 : " << cTemp << endl;
    return 0;
}

함수의 원형

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
float FahrToC(float fahr); // 함수의 원형

int main()
{
    float fTemp, cTemp;

    cout << "화씨온도 : ";
    cin >> fTemp;
    cTemp = FahrToC(fTemp); // 함수 호출
    cout << "---> 섭씨온도 : " << cTemp << endl;
    return 0;
}

float FahrToC(float fahr)
{
    return (fahr - 32) * 5 / 9;
}

함수의 순환 호출

  • 순환 호출 (recursive call)
    • 함수를 정의하는 몸체 블록 안에서 직접 또는 간접적으로 그 함수를 호출하도록 코드를 작성하는 것
  • 순환 호출의 예
    • 팩토리얼 계산 함수의 순환 호출 구현
    1
    2
    3
    4
    5
    6
    7
    
      int factorial(int n)
      {
          if (n <= 1)
              **return 1;
          else
              return n * factorial(n - 1);
      }
    

함수의 장점과 단점

  • 장점
    • 크고 복잡한 프로그램을 작은 크기의 의미 있는 작업 단위로 분할하여 구성
      • 간결하고 이해하기 쉬운 프로그램을 만들 수 있음
    • 반복 사용되는 코드의 중복 방지
    • 잘 설계된 함수는 다른 응용에서 재 사용할 수 있음
  • 단점
    • 함수 호출과 복귀 과정에서 처리 시간이 추가됨
      • 매우 효율적으로 동작해야 하는 함수라면 inline함수로 선언함

인수의 전달

인수(argument)와 매개 변수(parameter)

  • 인수(argument)
    • 함수 호출 문장에서 함수에 전달하는 식(expression)
    • 여러 개의 인수가 있을 경우 콤마(,)로 구분하여 나열함
    • 매개 변수를 통해 인수를 전달함
  • 실 매개 변수(actual parameter)
    • 함수 호출 문장에서 함수의 형식 매개 변수에 전달할 인수
    1
    2
    3
    4
    5
    6
    
      int main()
      {
          // ...
          cTemp = FahrToC(fTemp); // fTemp : 실 매개 변수
          // ...
      }
    
  • 형식 매개 변수(formal parameter)
    • 인수를 전달 받기 위해 함수에 선언된 매개 변수
    • 함수 헤더에 매개 변수의 자료형과 이름을 선언함
    1
    2
    3
    4
    
      float FahrToC(float fahr) // fahr : 형식 매개 변수
      { 
          return (fahr - 32) * 5 / 9;
      }
    

인수 전달 방식 - 값 호출

  • 값 호출(call-by-value)
    • 실 매개 변수의 값을 형식 매개 변수에 복사하는 방식

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      
        float FahrToC(float fahr) // fahr에 값 복사
        {
            return (fahr - 32) * 5 / 9;
        }
              
        int main()
        {
            float fTemp, cTemp;
            cout << "화씨온도 : ";
            cin >> fTemp;
            cTemp = FahrToC(fTemp); // 함수 호출
            cout << "---> 섭씨온도 : " << cTemp << endl;
            return 0;
        }
      
    • 형식 매개 변수의 값을 변경해도 실 매개 변수는 변함 없음

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      
        float FahrToC(float fahr) // 형식 매개변수 수정 (복사본)
        {
            fahr -= 32;
            return fahr * 5 / 9;
        }
              
        int main()
        {
            float fTemp, cTemp;
            cout << "화씨온도 : ";
            cin >> fTemp;
            cTemp = FahrToC(fTemp); // 원본은 변함 없음
            cout << "---> 섭씨온도 : " << cTemp << endl;
            return 0;
        }
      
  • 값 호출 방식의 장점과 단점
    • 장점
      • 실 매개 변수와 형식 매개 변수는 별개의 데이터이므로 불필요한 부작용이 발생하지 않음
    • 단점
      • 구조체와 같이 많은 양의 데이터로 구성된 인수를 전달할 경우 데이터의 복사 량이 많아짐

인수 전달 방식 - 참조 호출

  • 참조 호출(call-by-reference)
    • 실 매개 변수의 참조를 형식 매개 변수에 전달하는 방식
  • 참조 호출의 용도
    • 함수에서 처리한 결과를 매개 변수를 통해 받아 오려는 경우
      • 함수에서 형식 매개 변수의 값을 변경하는 것은 실 매개 변수의 값을 변경하는 것과 같음
    • 많은 양의 데이터로 구성되는 구조체와 같은 인수를 함수에 효율적으로 전달하는 경우
      • 형식 매개 변수에 복사 되는 데이터의 양은 실 매개 변수의 크기와 관계 없이 일정함
  • 참조 호출의 예 - SwapInt.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
      #include <iostream>
      using namespace std;
      void SwapValues(int& x, int& y); // 원형
        
      int main()
      {
          int a, b;
        
          cout << "두 수를 입력하시오 : ";
          cin >> a >> b;
          if (a < b) SwapValues(a, b); // 순서를 바꿔 a에 큰 값을 넣음
          cout << "큰 수 = " << a << " 작은 수 = " << b << endl;
          return 0;
      }
        
      void SwapValues(int& x, int& y)
      {
          int temp = x;
          x = y;
          y = temp;
      }
    

    image.png

    image.png

  • 참조 호출을 통한 효율적인 인수 전달
    • 많은 양의 데이터로 구성되는 객체나 객체를 인수로 전달하는 경우 값 호출을 사용하는 것에 비해 참조 호출을 사용하는 것이 효율적임
    • 함수 호출의 효율성을 위해 참조 호출을 하지만 실 매개 변수의 값이 변경되는 것을 원하지 않는 경우에는 형식 매개 변수에 const한정어를 지정하여 실 매개 변수를 보호

const매개변수

  • const한정어를 이용한 실 매개 변수 보호 - PrintRec.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      struct SalesRec {
          char pID[10];
          int dYear, dMonth, dDate;
          char deliverAddr[40];
      }; // 62바이트(64바이트)
        
      void PrSalesRec(SalesRec srec) // 64바이트 복사
      {
          cout << "품목코드 : " << srec.pID << endl;
          cout << "배달일자 : " << srec.dYear << "년 ";
          cout << srec.dMonth << "월 ";
          cout << srec.dDate << "일" << endl;
          cout << "배달주소 : " << srec.deliverAddr << endl;
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      struct SalesRec {
          char pID[10];
          int dYear, dMonth, dDate;
          char deliverAddr[40];
      }; // 62바이트(64바이트)
        
      void PrSalesRec(SalesRec &srec) // 4바이트 복사
      {
          cout << "품목코드 : " << srec.pID << endl;
          cout << "배달일자 : " << srec.dYear << "년 ";
          cout << srec.dMonth << "월 ";
          cout << srec.dDate << "일" << endl;
          cout << "배달주소 : " << srec.deliverAddr << endl;
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      struct SalesRec {
          char pID[10];
          int dYear, dMonth, dDate;
          char deliverAddr[40];
      }; // 62바이트(64바이트)
        
      void PrSalesRec(const SalesRec &srec) // srec 보호
      {
          cout << "품목코드 : " << srec.pID << endl;
          cout << "배달일자 : " << srec.dYear << "년 ";
          cout << srec.dMonth << "월 ";
          cout << srec.dDate << "일" << endl;
          cout << "배달주소 : " << srec.deliverAddr << endl;
      }
    

디폴트 인수

  • 인수의 디폴트 값을 지정하는 방법
    • 일반적으로 사용되는 디폴트 값이 있는 인수의 경우 함수를 정의할 때 그 값을 미리 지정할 수 있음
  • ex)

    1
    
      istream& getline(char* str, int count, char delimiter='\n');
    
    1
    2
    3
    
      char str1[10], str2[10];
      cin.getline(str1, 10, ','); // ','가 나올 때까지 최대 9자 입력
      cin.getline(str2, 10);      // cin.getline(str2, 10, '\\n')과 동일
    
  • 반올림 함수 Round - Round.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
      double Round(double x, int d = 0);
        
      int main()
      {
          double a;
          cout << "값 = ";
          cin >> a;
          cout << "반올림 --> " << Round(a) << endl;
          cout << "            " << Round(a, 1) << endl;
          cout << "            " << Round(a, 2) << endl;
          cout << "            " << Round(a, 3) << endl;
          return 0;
      }
        
      double Round(double x, int d)
      {
          double a = x >= 0 ? 0.5 : -0.5;
          double pow10 = pow(10, d);
          return trunc(x * pow10 + a) / pow10;
      }
        
      // 값 = 12.3456
      // 반올림 --> 12
      //            12.3
      //            12.35
      //            12.346
    
  • 주의 사항
    • 디폴트 인수는 인수 중 끝에만 위치할 수 있음
    • ex)

      1
      2
      
        void f(int x, int y=10, int z=20); // OK
        void g(int x, int y=10, int z);    // 오류
      
      1
      
        f(5); // x=5, y=10, z=20 전달
      
      1
      
        f(5, 100); // x=5, y=100, z=20 전달
      
      1
      
        f(5, 100, 200); // x=5, y=100, z=200 전달
      
      1
      
        f(5, ,300); // 오류: f(5, 10, 300);으로 작성
      

함수의 다중 정의

함수 다중 정의의 개념

  • 다중 정의(overloading)
    • 동일한 이름에 대하여 여러 가지 의미를 부여하는 것
  • 함수 다중 정의
    • 동일한 이름을 갖는 함수를 여러 개 정의하는 것
    • 동일한 개념의 처리를 여러 가지 데이터나 객체에 대해 각각의 대상에 맞는 처리를 해야 할 경우 사용함
    • 다중 정의 된 함수의 구분 기준
      • 인수의 개수 및 자료형
      • 함수의 반환 자료형으로 함수를 구분할 수 없음

함수 다중 정의의 예 - TimeCalc.cpp

  • 시간 구조체 TimeRec

    1
    2
    3
    4
    
      struct TimeRec {
          int hours;
          int minutes;
      };
    
    • AddTime함수의 다중 정의

      1
      
        void AddTime(TimeRec& t1, const TimeRec& t2);
      
      1
      
        void AddTime(TimeRec& t, int minutes);
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct TimeRec {
    int hours;
    int minutes;
};

// 시간을 더하는 함수
// 인수 TimeRec &t1 : 누계할 시간
// const TimeRec &t2 : 더할 시간
// 반환값 없음

void AddTime(TimeRec &t1, const TimeRec &t2)
{
    t1.minutes += t2.minutes;
    t1.hours += t2.hours + t1.minutes / 60;
    t1.minutes %= 60;
}

void AddTime(TimeRec &t, int minutes)
{
    t.minutes += minutes;
    t.hours += t.minutes / 60;
    t.minutes %= 60;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main()
{
    TimeRec tRec1 = { 2, 30 };
    TimeRec tRec2 = { 1, 45 };

    cout << tRec1.hours << "시간 " << tRec1.minutes << "분 + ";
    cout << tRec2.hours << "시간 " << tRec2.minutes << "분 = ";
    AddTime(tRec1, tRec2); // void AddTime(TimeRec &t1, const TimeRec &t2) 호출
    cout << tRec1.hours << "시간 " << tRec1.minutes << "분" << endl;

    cout << tRec1.hours << "시간 " << tRec1.minutes << "분 + ";
    cout << "135분 = ";
    AddTime(tRec1, 135); // void AddTime(TimeRec &t, int minutes) 호출
    cout << tRec1.hours << "시간 " << tRec1.minutes << "분" << endl;
    return 0;
}

주의: 모호한 함수 다중 정의

  • 반환 자료형만 다른 경우

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
      void f(int a) {
          cout << a * a;
      }
        
      void f(int a, int b) {
          cout << a * b;
      }
        
      int f(int a, int b) { // 에러! (반환 자료형만 다름)
          cout << a * b;
      }
        
      int main()
      {
          f(10);
          f(10, 20);
      }
    
  • 선택 기준이 모호한 경우 (디폴트 인수)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      void g(int a)
      {
          cout << a * a;
      }
        
      void g(int a, int b = 100)
      {
          cout << a * b;
      }
        
      int main()
      {
          g(10, 20);
          g(10); // 에러! (선택 기준이 모호함)
      }
    
  • 형 변환 대상이 모호한 경우

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
      void h(int a)
      {
          cout << a * a;
      }
        
      void h(float a)
      {
          cout << a * b;
      }
        
      int main()
      {
          h(10);
          h(10.0f);
          h(10.0); // 에러! (형 변환 대상이 모호함)
      }
    

inline함수

inline함수의 개념

  • inline함수란?
    • 함수 호출 절차를 따르지 않고 함수 호출 위치에 함수의 처리 문장이 삽입되게 번역하도록 선언된 함수
    • 함수를 사용함으로써 얻을 수 있는 모듈화의 장점을 살리면서, 함수 호출에 따른 부수적인 처리 시간이 생략됨
    • 함수 호출 루틴으로부터 함수로 넘어가는 부분의 코드 최적화가 가능해짐
      • 매우 빈번히 호출되며 빠른 실행이 요구되는 함수를 inline함수로 선언하면 성능을 높이는데 도움이 됨
  • 주의
    • inline함수로 선언하더라도 반드시 inline으로 번역되는 것은 아님
    • inline선언을 무시하고 일반 함수로 번역하는 경우
      • 함수가 너무 큰 경우
      • 순환 호출(recursive call)을 하는 경우
      • 프로그램 내에서 그 함수에 대한 포인터를 사용하는 경우



연습 문제


  1. 위 지문의 함수에서 ㈀에 넣을 내용은?

    1
    2
    3
    4
    5
    6
    7
    
     // ___㈀___ f(int a, int b)
     {
        if (a < b)
          cout << "작은 수 = " << a << endl;
        else
          cout << "작은 수 = " << b << endl;
     }
    

    a. void

    • 값을 반환하지 않는 함수는 void형으로 선언함
  2. 다음 중 값 호출(call-by-value)에 대한 올바른 설명은? a. 함수에서 형식 매개 변수의 값을 변경해도 실 매개 변수의 값은 변하지 않는다.

    • 실 매개 변수의 값을 형식 매개 변수에 복사하는 인수 전달 방식으로, 함수 내에서 형식 매개 변수의 값을 수정하는 것은 복사본을 수정하는 것이므로 원본인 실 매개 변수의 값에는 영향이 없음
  3. 함수 f를 위 지문과 같이 선언하였다. int형 변수 a와 b에 각각 10, 15가 저장되어 있을 때, f(a, b)를 호출한 후 변수 a와 b의 값은 무엇인가?

    1
    2
    3
    4
    5
    6
    
     int f(int x, int &y)
     {
       x *= x;
       y /= 2;
       return x * y;
     }
    

    a. a = 10, b = 7

    • x는 값 호출, y는 참조 호출 방식으로 인수를 전달 받음
    • 그러므로 x를 변화 시켜도 x에 값을 전달한 변수 a는 변화하지 않는 반면, y를 변화 시키는 것은 이에 대한 실 매개 변수의 값을 변화 시킨 것이 됨
  4. 위 지문에서 함수의 몸체인 ㈀에서 사용할 수 있는 명령은?

    1
    2
    3
    4
    
     int g(const int arr[ ])
     {
        // ___㈀___
     }
    

    a. cout << arr[0];

    • arr은 상수를 저장하는 배열이므로, arr[1]++;이나 cin >> arr[1];와 같이 arr의 원소 값을 변경할 수 없음
    • 또한 int 형 함수이므로 return명령은 int 형 수식을 반환하도록 해야 함
  5. 함수 다중 정의에 대한 올바른 설명은?

    a. 동일한 이름으로 함수를 정의하되, 인수의 자료형이나 개수 등으로 구분할 수 있게 다중 정의한다.

    • 함수 다중 정의는 동일한 이름의 함수를 여러 개 정의하는 것임
    • 함수의 이름은 갖지만, 인수의 자료형 및 개수가 형식 매개 변수와 맞는 것을 선택하여 호출할 수 있음



정리 하기


  • 함수는 특정 작업을 수행하는 프로그램 문장들을 하나의 단위로 모아 놓고 이름을 부여한 것으로 함수에 정의된 처리가 필요한 부분에서 호출하여 사용할 수 있음
  • 함수를 호출할 때 함수의 처리에 사용할 인수를 매개 변수를 통해 전달함
  • C++에서는 실 매개 변수의 값을 형식 매개 변수에 복사하는 값 호출 방식과 실 매개 변수의 참조를 형식 매개 변수에 전달하는 참조 호출 방식으로 인수를 함수에 전달할 수 있음
  • 많은 양의 데이터로 구성되는 구조체나 객체를 인수로 전달하는 경우 값 호출을 사용하는 것에 비해 참조 호출을 사용하는 것이 효율적임
  • 일반적으로 사용되는 디폴트 값이 있는 인수는 디폴트 인수로 지정할 수 있음
  • 동일한 개념이지만 처리 대상에 맞게 동작하는 여러 개의 함수를 만들 때 이들을 같은 이름의 함수로 다중 정의할 수 있음
  • 매우 빈번히 호출되며 빠른 실행이 요구되는 함수를 inline함수로 선언하면 성능을 높이는 데 도움이 됨

[C++ 프로그래밍] 3강 - C++ 언어의 기초

[C++ 프로그래밍] 5강 - 클래스와 객체