C++ 함수 기본 사항

C++Beginner
지금 연습하기

소개

이 랩에서는 C++ 의 함수에 대해 배우게 됩니다. 함수를 정의하고 호출하는 방법, 그리고 함수에 인수를 전달하는 방법을 배우게 됩니다.

콘텐츠 미리보기

때때로, 코드의 특정 부분이 여러 번 사용되어야 합니다. 유지 관리 및 이해의 용이성을 위해, 해당 코드를 "서브루틴" - 함수에 넣고 이 함수를 여러 번 "호출"하는 것이 좋습니다.

함수를 사용하는 데는 두 당사자가 관련됩니다: 함수를 호출하는 호출자 (caller) 와 호출되는 함수 (function) 입니다. 호출자는 함수에 인수 (argument(s)) 를 전달합니다. 함수는 이러한 인수 (들) 를 받아 함수 본문 내에서 프로그래밍된 작업을 수행하고, 결과의 일부를 호출자에게 다시 반환합니다.

함수 사용하기

원의 면적을 여러 번 계산해야 한다고 가정해 봅시다. getArea()라는 함수를 작성하고 필요할 때 재사용하는 것이 좋습니다.

/* Test Function */
#include <iostream>
using namespace std;
const int PI = 3.14159265;

// Function Prototype (함수 선언)
double getArea(double radius);

int main() {
   double radius1 = 1.1, area1, area2;
   // call function getArea()
   area1 = getArea(radius1);
   cout << "area 1 is " << area1 << endl;
   // call function getArea()
   area2 = getArea(2.2);
   cout << "area 2 is " << area2 << endl;
   // call function getArea()
   cout << "area 3 is " << getArea(3.3) << endl;
}

// Function Definition
// Return the area of a circle given its radius
double getArea(double radius) {
   return radius * radius * PI;
}

출력:

area 1 is 3.63
area 2 is 14.52
area 3 is 32.67

image desc

image desc

C++ 에서는 함수를 사용하기 전에 함수 프로토타입을 선언하고, 프로그래밍된 연산을 포함하는 본문과 함께 함수 정의를 제공해야 합니다.

함수 정의의 구문은 다음과 같습니다.

returnValueType functionName ( parameterList ) {
   functionBody ;
}

함수 프로토타입은 컴파일러에게 함수의 인터페이스, 즉 반환 유형, 함수 이름 및 매개변수 유형 목록 (매개변수의 수와 유형) 을 알려줍니다. 이제 함수는 파일의 어느 곳에서나 정의할 수 있습니다. 예를 들어,

// Function prototype - placed before the function is used.
double getArea(double);  // without the parameter name
double getArea(double radius);  // parameter names are ignored, but serve as documentation

"void" 반환 유형

호출자에게 값을 반환할 필요가 없으면 반환 값 유형을 void로 선언할 수 있습니다. 함수의 본문에서 반환 값 없이 "return;" 문을 사용하여 호출자에게 제어를 반환할 수 있습니다.

실제 매개변수 vs. 형식 매개변수

위의 예에서 getArea(double radius)의 시그니처에 선언된 변수 (double radius)형식 매개변수 (formal parameter) 로 알려져 있습니다. 그 범위는 함수의 본문 내에 있습니다. 함수가 호출자에 의해 호출될 때, 호출자는 소위 실제 매개변수 (actual parameter) (또는 인수 (arguments)) 를 제공해야 하며, 그 값은 실제 계산에 사용됩니다. 예를 들어, 함수가 "area1 = getArea(radius1)"을 통해 호출될 때, radius1은 실제 매개변수이며 값은 1.1입니다.

함수의 지역 변수 및 매개변수의 범위

함수 내에서 선언된 함수 매개변수를 포함한 모든 변수는 해당 함수에만 사용할 수 있습니다. 함수가 호출될 때 생성되고 함수가 반환된 후 해제 (파괴) 됩니다. 이러한 변수는 함수에 로컬하고 함수 외부에서 사용할 수 없기 때문에 지역 변수 (local variables) 라고 합니다.

기본 인수

C++ 는 함수에 소위 기본 인수 (default arguments) 를 도입합니다. 호출자가 함수를 호출할 때 해당 실제 인수를 생략하면 이러한 기본값이 사용됩니다. 기본 인수는 함수 프로토타입에서 지정되며 함수 정의에서 반복될 수 없습니다. 기본 인수는 위치를 기준으로 해석됩니다. 따라서 모호성을 피하기 위해 후행 (trailing) 인수를 대체하는 데만 사용할 수 있습니다. 예를 들어,

/* Test Function default arguments */
#include <iostream>
using namespace std;

// Function prototype - Specify the default arguments here
int fun1(int = 1, int = 2, int = 3);
int fun2(int, int, int = 3);

int main() {
   cout << fun1(4, 5, 6) << endl; // No default
   cout << fun1(4, 5) << endl;    // 4, 5, 3(default)
   cout << fun1(4) << endl;       // 4, 2(default), 3(default)
   cout << fun1() << endl;        // 1(default), 2(default), 3(default)

   cout << fun2(4, 5, 6) << endl; // No default
   cout << fun2(4, 5) << endl;    // 4, 5, 3(default)
   // cout << fun2(4) << endl;
   // error: too few arguments to function 'int fun2(int, int, int)'
}

int fun1(int n1, int n2, int n3) {
   // cannot repeat default arguments in function definition
   return n1 + n2 + n3;
}

int fun2(int n1, int n2, int n3) {
   return n1 + n2 + n3;
}

출력:

15
12
9
6
15
12

image desc

함수 오버로딩

C++ 는 함수 오버로딩 (function overloading) (또는 함수 다형성 (function polymorphism)) 을 도입하여, 매개변수 목록 (매개변수의 수, 유형 또는 순서) 에 따라 구분되는 동일한 함수 이름의 여러 버전을 가질 수 있도록 합니다. 오버로딩된 함수는 반환 유형으로 구분할 수 없습니다 (컴파일 오류). 호출자의 인수 목록과 일치하는 버전이 실행을 위해 선택됩니다. 예를 들어,

/* Test Function Overloading */
#include <iostream>
using namespace std;

void fun(int, int, int);  // Version 1
void fun(double, int);          // Version 2
void fun(int, double);          // Version 3

int main() {
   fun(1, 2, 3);   // version 1
   fun(1.0, 2);    // version 2
   fun(1, 2.0);    // version 3
   fun(1.1, 2, 3); // version 1 - double 1.1 casted to int 1 (without warning)

   // fun(1, 2, 3, 4);
      // error: no matching function for call to 'fun(int, int, int, int)'
   // fun(1, 2);
      // error: call of overloaded 'fun(int, int)' is ambiguous
      // note: candidates are:
      //    void fun(double, int)
      //    void fun(int, double)
   // fun(1.0, 2.0);
      // error: call of overloaded 'fun(double, double)' is ambiguous
}

void fun(int n1, int n2, int n3) {  // version 1
   cout << "version 1" << endl;
}

void fun(double n1, int n2) { // version 2
   cout << "version 2" << endl;
}

void fun(int n1, double n2) { // version 3
   cout << "version 3" << endl;
}

출력:

version 1
version 2
version 3
version 1

image desc

함수와 배열

함수에 배열을 전달할 수도 있습니다. 그러나 배열의 크기도 함수에 전달해야 합니다. 이는 호출된 함수 내의 배열 인수를 통해 배열의 크기를 알 수 있는 방법이 없기 때문입니다. 예를 들어,

/* Function to compute the sum of an array */
#include <iostream>
using namespace std;

// Function prototype
int sum(int array[], int size);    // Need to pass the array size too
void print(int array[], int size);

// Test Driver
int main() {
   int a1[] = {8, 4, 5, 3, 2};
   print(a1, 5);   // {8,4,5,3,2}
   cout << "sum is " << sum(a1, 5) << endl;  // sum is 22
}

// Function definition
// Return the sum of the given array
int sum(int array[], int size) {
   int sum = 0;
   for (int i = 0; i < size; ++i) {
      sum += array[i];
   }
   return sum;
}

// Print the contents of the given array
void print(int array[], int size) {
   cout << "{";
   for (int i = 0; i < size; ++i) {
      cout << array[i];
      if (i < size - 1) {
         cout << ",";
      }
   }
   cout << "}" << endl;
}

출력:

{8,4,5,3,2}
sum is 22

image desc

값 전달 vs. 참조 전달

함수에 매개변수를 전달하는 방법에는 두 가지가 있습니다: *값으로 전달 (pass by value)*과 참조로 전달 (pass by reference).

값으로 전달 (Pass-by-Value)

값으로 전달에서는 인수의 "복사본"이 생성되어 함수로 전달됩니다. 호출된 함수는 "복제본"에서 작동하며, 원본 복사본을 수정할 수 없습니다. C/C++ 에서 기본 유형 (예: intdouble) 은 값으로 전달됩니다.

/* Fundamental types are passed by value into Function */
#include <iostream>
using namespace std;

// Function prototypes
int inc(int number);

// Test Driver
int main() {
   int n = 8;
   cout << "Before calling function, n is " << n << endl; // 8
   int result = inc(n);
   cout << "After calling function, n is " << n << endl;  // 8
   cout << "result is " << result << endl;                // 9
}

// Function definitions
// Return number+1
int inc(int number) {
   ++number;  // Modify parameter, no effect to caller
   return number;
}

출력:

Before calling function, n is 8
After calling function, n is 8
result is 9

image desc

참조로 전달 (Pass-by-Reference)

반면에, 참조로 전달에서는 호출자의 변수에 대한 *참조 (reference)*가 함수로 전달됩니다. 즉, 호출된 함수는 동일한 데이터에서 작동합니다. 호출된 함수가 매개변수를 수정하면, 동일한 호출자의 복사본도 수정됩니다. C/C++ 에서 배열은 참조로 전달됩니다. C/C++ 는 함수가 배열을 반환하는 것을 허용하지 않습니다.

/* Function to increment each element of an array */
#include <iostream>
using namespace std;

// Function prototypes
void inc(int array[], int size);
void print(int array[], int size);

// Test Driver
int main() {
   int a1[] = {8, 4, 5, 3, 2};

   // Before increment
   print(a1, 5);   // {8,4,5,3,2}
   // Do increment
   inc(a1, 5);     // Array is passed by reference (having side effect)
   // After increment
   print(a1, 5);   // {9,5,6,4,3}
}

// Function definitions

// Increment each element of the given array
void inc(int array[], int size) {  // array[] is not const
   for (int i = 0; i < size; ++i) {
      array[i]++;  // side-effect
   }
}

// Print the contents of the given array
void print(int array[], int size) {
   cout << "{";
   for (int i = 0; i < size; ++i) {
      cout << array[i];
      if (i < size - 1) {
         cout << ",";
      }
   }
   cout << "}" << endl;
}

출력:

{8,4,5,3,2}
{9,5,6,4,3}

image desc

Const 함수 매개변수

참조를 전달할 때는 가능한 한 const를 사용하십시오. 이는 매개변수를 실수로 수정하는 것을 방지하고 많은 프로그래밍 오류로부터 보호합니다.

선형 검색 (linear search) 에서 검색 키는 배열의 각 요소와 선형적으로 비교됩니다. 일치하는 항목이 있으면 일치하는 요소의 인덱스를 반환하고, 그렇지 않으면 -1 을 반환합니다. 선형 검색의 복잡도는 O(n) 입니다.

/* Search an array for the given key using Linear Search */
#include <iostream>
using namespace std;

int linearSearch(const int a[], int size, int key);

int main() {
   const int SIZE = 8;
   int a1[SIZE] = {8, 4, 5, 3, 2, 9, 4, 1};

   cout << linearSearch(a1, SIZE, 8) << endl;  // 0
   cout << linearSearch(a1, SIZE, 4) << endl;  // 1
   cout << linearSearch(a1, SIZE, 99) << endl; // 8 (not found)
}

// Search the array for the given key
// If found, return array index [0, size-1]; otherwise, return size
int linearSearch(const int a[], int size, int key) {
   for (int i = 0; i < size; ++i) {
      if (a[i] == key) return i;
   }
   // a[0] = 1;
   // It will result in an error,because a[] is const, means read only
   return -1;
}

출력:

0
1
-1

image desc

'참조' 매개변수를 통한 참조 전달

&로 표시되는 *참조 매개변수 (reference parameter)*를 통해 기본 유형 매개변수를 참조로 전달할 수 있습니다.

/* Test Pass-by-reference for fundamental-type parameter
   via reference declaration */
#include <iostream>
using namespace std;

int squareByValue (int number);        // Pass-by-value
void squareByReference (int &number); // Pass-by-reference

int main() {
   int n1 = 8;
   cout << "Before call, value is " << n1 << endl;  // 8
   cout << squareByValue(n1) << endl;  // no side-effect
   cout << "After call, value is " << n1 << endl;   // 8

   int n2 = 9;
   cout << "Before call, value is " << n2 << endl;  // 9
   squareByReference(n2);  // side-effect
   cout << "After call, value is " << n2 << endl;   // 81
}

// Pass parameter by value - no side effect
int squareByValue (int number) {
   return number * number;
}

// Pass parameter by reference by declaring as reference (&)
// - with side effect to the caller
void squareByReference (int &number) {
   number = number * number;
}

출력:

Before call, value is 8
64
After call, value is 8
Before call, value is 9
After call, value is 81

image desc

2.8 수학 함수

C++ 는 <cmath> 라이브러리에서 많은 일반적인 수학 함수를 제공합니다.

sin(x), cos(x), tan(x), asin(x), acos(x), atan(x):
   인수 유형을 사용하고 float, double, long double 유형을 반환합니다.
sinh(x), cosh(x), tanh(x):
   쌍곡선 삼각 함수.
pow(x, y), sqrt(x):
   거듭제곱 및 제곱근.
ceil(x), floor(x):
   부동 소수점 숫자의 천장 및 바닥 정수를 반환합니다.
fabs(x), fmod(x, y):
   부동 소수점 절대값 및 모듈러스.
exp(x), log(x), log10(x):
   지수 및 로그 함수.

cstdlib 헤더 (C 의 stdlib.h에서 포팅됨) 는 0 과 RAND_MAX (포함) 사이의 의사 난수 정수를 생성하는 함수 rand()를 제공합니다.

/* Test Random Number Generation */
#include <iostream>
#include <cstdlib>  // for rand(), srand()
#include <ctime>    // for time()
using namespace std;

int main() {
   // rand() generate a random number in [0, RAND_MAX]
   cout << "RAND_MAX is " << RAND_MAX << endl;   // 32767

   // Generate 10 pseudo-random numbers between 0 and 99
   //   without seeding the generator.
   // You will get the same sequence, every time you run this program
   for (int i = 0; i < 10; ++i) {
      cout << rand() % 100 << " ";   // need <cstdlib> header
   }
   cout << endl;

   // Seed the random number generator with current time
   srand(time(0));   // need <cstdlib> and <ctime> header
   // Generate 10 pseudo-random numbers
   // You will get different sequence on different run,
   //   because the current time is different
   for (int i = 0; i < 10; ++i) {
      cout << rand() % 100 << " ";   // need <cstdlib> header
   }
   cout << endl;
}

출력:

RAND_MAX is 2147483647
83 86 77 15 93 35 86 92 49 21
29 0 83 60 22 55 97 80 68 87

image desc

- name: check if keyword exist
  script: |
    #!/bin/bash
    grep -i 'rand' /home/labex/Code/test.cpp
  error: Oops! We find that you didn't use "rand()" method in "test.cpp".
  timeout: 3

요약

함수를 사용하는 이점은 다음과 같습니다.

  1. 분할 정복 (Divide and conquer): 간단하고 작은 조각 또는 구성 요소로 프로그램을 구성합니다. 프로그램을 자체 포함된 작업으로 모듈화합니다.
  2. 코드 반복 방지: 복사하여 붙여넣는 것은 쉽지만, 모든 복사본을 유지 관리하고 동기화하는 것은 어렵습니다.
  3. 소프트웨어 재사용: 함수를 라이브러리 코드로 패키징하여 다른 프로그램에서 재사용할 수 있습니다.