C++ 함수 기본 사항

C++Beginner
지금 연습하기

소개

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

이것은 가이드 실험입니다. 학습과 실습을 돕기 위한 단계별 지침을 제공합니다.각 단계를 완료하고 실무 경험을 쌓기 위해 지침을 주의 깊게 따르세요. 과거 데이터에 따르면, 이것은 중급 레벨의 실험이며 완료율은 52%입니다.학습자들로부터 75%의 긍정적인 리뷰율을 받았습니다.

콘텐츠 미리보기

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

함수를 사용하는 데는 두 당사자가 관련됩니다: 함수를 호출하는 호출자 (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. 소프트웨어 재사용: 함수를 라이브러리 코드로 패키징하여 다른 프로그램에서 재사용할 수 있습니다.