C++ 에서 함수 정의 및 사용하기

C++Beginner
지금 연습하기

소개

이 랩에서는 C++ 의 함수 세계를 깊이 탐구합니다. 함수는 코드를 관리 가능하고 재사용 가능한 블록으로 구성할 수 있게 해주는 프로그래밍의 초석입니다. 이 랩에서는 다양한 반환 타입을 가진 함수 생성부터 재귀 및 함수 오버로딩과 같은 고급 개념 활용까지 광범위한 함수 관련 주제를 다룹니다. 함수 정의 방법, 값에 의한 매개변수 전달 및 참조에 의한 매개변수 전달, 기본 매개변수 값 설정, 함수 프로토타입 이해에 대해 배우게 됩니다. 또한 구조체를 사용하여 여러 값을 반환하는 방법을 발견하게 됩니다. 이 랩이 끝나면 모듈화되고 효율적이며 잘 구조화된 C++ 코드를 작성하는 데 탄탄한 기반을 갖추게 될 것입니다.

정수, 부동 소수점 숫자, 문자 및 문자열과 같은 다양한 반환 타입을 가진 함수를 탐색하는 것으로 시작하겠습니다. 그런 다음 값에 의한 매개변수 전달과 참조에 의한 매개변수 전달의 중요한 차이점과 기본 매개변수 값이 함수에 유연성을 더하는 방법을 배우게 됩니다. 또한 동일한 이름을 가지지만 매개변수 목록이 다른 여러 함수를 정의할 수 있게 해주는 함수 오버로딩도 다룰 것입니다. 또한, 재귀 함수, 즉 자신을 호출하여 문제를 해결하는 강력한 도구를 탐색하게 됩니다. 더 체계적인 코드를 만들기 위해 헤더 파일에서 함수 프로토타입을 사용하는 방법을 보게 될 것입니다. 마지막으로 구조체를 사용하여 함수에서 여러 값을 반환하는 방법을 발견하게 됩니다.

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

다양한 반환 타입을 가진 함수 생성하기

함수는 프로그래밍의 기본적인 개념입니다. 함수를 사용하면 큰 프로그램을 더 작고 관리하기 쉬운 조각으로 나눌 수 있어 코드를 더 쉽게 작성, 이해 및 유지 관리할 수 있습니다. 함수는 값을 반환할 수도 있어 데이터를 프로그램의 호출 부분으로 다시 전달할 수 있습니다.

C++ 에서 함수의 기본 구문은 다음과 같습니다.

return_type function_name(parameters) {
    // Function body
    return value;
}
  • return_type: 함수가 반환할 값의 데이터 타입을 지정합니다. 함수가 값을 반환하지 않으면 void 키워드를 사용합니다.
  • function_name: 함수를 호출하는 데 사용할 이름입니다. 함수의 목적을 명확하게 설명하는 이름을 선택하십시오.
  • parameters: 함수에 전달하는 입력 값입니다. 선택 사항이며 0 개 이상의 매개변수를 가질 수 있습니다.
  • value: 함수가 반환하는 값입니다. 함수의 return_type과 일치해야 합니다. return_typevoid인 경우 return 문을 생략하거나 값 없이 return;만 사용합니다.

이 단계에서는 다른 데이터 타입을 반환하는 함수를 만드는 방법을 배우게 됩니다. 이 능력은 다재다능하고 기능적인 프로그램을 구축하는 데 중요합니다.

WebIDE 를 열고 ~/project 디렉토리에 function_types.cpp라는 새 파일을 만듭니다.

touch ~/project/function_types.cpp

에디터에서 function_types.cpp 파일을 열고 다음 코드를 추가합니다.

#include <iostream>
#include <string>

// 정수를 반환하는 함수
int addNumbers(int a, int b) {
    return a + b;
}

// double (부동 소수점 숫자) 을 반환하는 함수
double calculateAverage(double x, double y) {
    return (x + y) / 2.0;
}

// 문자를 반환하는 함수
char getGrade(int score) {
    if (score >= 90) return 'A';
    if (score >= 80) return 'B';
    if (score >= 70) return 'C';
    if (score >= 60) return 'D';
    return 'F';
}

// 문자열을 반환하는 함수
std::string getStudentStatus(bool isEnrolled) {
    return isEnrolled ? "Active" : "Inactive";
}

int main() {
    // 다양한 반환 타입 시연
    int sum = addNumbers(5, 3);
    std::cout << "Sum: " << sum << std::endl;

    double average = calculateAverage(10.5, 20.5);
    std::cout << "Average: " << average << std::endl;

    char grade = getGrade(85);
    std::cout << "Grade: " << grade << std::endl;

    std::string status = getStudentStatus(true);
    std::cout << "Student Status: " << status << std::endl;

    return 0;
}

프로그램을 컴파일하고 실행합니다.

g++ function_types.cpp -o function_types
./function_types

예시 출력:

Sum: 8
Average: 15.5
Grade: B
Student Status: Active

함수 반환 타입에 대한 주요 사항:

  • 함수는 다양한 데이터 타입을 반환할 수 있어 코드에 유연성을 제공합니다.
  • return_type은 함수 이름 앞에 지정되어 함수가 어떤 종류의 데이터를 다시 보낼지 나타냅니다.
  • return 문은 선언된 return_type과 일치해야 하며, 그렇지 않으면 컴파일러가 오류를 보고합니다.
  • void 함수는 값을 반환하지 않으며, void 함수의 끝에 있는 경우 return;을 사용하거나 반환 문을 생략할 수 있습니다.
  • 기본 타입 (int, double, char), 문자열, 심지어 사용자 정의 타입까지 반환할 수 있습니다.
  • 함수 반환 타입을 다양한 종류의 컨테이너라고 생각하십시오. 다양한 항목을 운반하기 위해 상자, 가방 또는 바구니를 사용하는 것처럼 함수는 다양한 반환 타입을 사용하여 다양한 종류의 데이터를 다시 보냅니다.

값으로 함수에 매개변수 전달하기

이 단계에서는 C++ 에서 값에 의한 매개변수 전달에 대해 배우게 됩니다. 값에 의한 매개변수를 전달할 때 원본 인수의 복사본이 생성되며, 이 복사본이 함수 내에서 사용됩니다. 함수 내에서 매개변수에 대한 모든 변경은 함수 외부의 원본 변수에 영향을 미치지 않습니다.

WebIDE 를 열고 ~/project 디렉토리에 pass_by_value.cpp라는 새 파일을 만듭니다.

touch ~/project/pass_by_value.cpp

pass_by_value.cpp 파일을 에디터에서 열고 다음 코드를 추가합니다.

#include <iostream>

// 값에 의한 전달을 시연하는 함수
void modifyValue(int x) {
    // 이 수정은 원본 변수가 아닌 로컬 복사본에만 영향을 미칩니다.
    x = x * 2;
    std::cout << "Inside function - Value of x: " << x << std::endl;
}

int main() {
    // 원본 변수
    int number = 10;

    // 원본 값 출력
    std::cout << "Before function call - Original value: " << number << std::endl;

    // 원본 변수로 함수 호출
    modifyValue(number);

    // 원본 변수는 변경되지 않음
    std::cout << "After function call - Original value: " << number << std::endl;

    return 0;
}

프로그램을 컴파일하고 실행합니다.

g++ pass_by_value.cpp -o pass_by_value
./pass_by_value

예시 출력:

Before function call - Original value: 10
Inside function - Value of x: 20
After function call - Original value: 10

값에 의한 전달을 더 자세히 설명하기 위해 다른 예제를 만들어 보겠습니다.

touch ~/project/swap_values.cpp

swap_values.cpp에 다음 코드를 추가합니다.

#include <iostream>
#include <string>

// 값 교환 함수 (값에 의한 전달으로는 예상대로 작동하지 않음)
void swapValues(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    std::cout << "Inside function - a: " << a << ", b: " << b << std::endl;
}

int main() {
    int first = 5;
    int second = 10;

    std::cout << "Before swap - first: " << first << ", second: " << second << std::endl;

    // swap 함수 호출
    swapValues(first, second);

    // 원본 변수는 변경되지 않음
    std::cout << "After swap - first: " << first << ", second: " << second << std::endl;

    return 0;
}

두 번째 프로그램을 컴파일하고 실행합니다.

g++ swap_values.cpp -o swap_values
./swap_values

예시 출력:

Before swap - first: 5, second: 10
Inside function - a: 10, b: 5
After swap - first: 5, second: 10

값에 의한 전달에 대한 주요 사항:

  • 인수의 복사본이 함수에 전달되어 원본 값을 보존합니다.
  • 함수 내에서 매개변수에 대한 변경은 함수 외부의 원본 변수에 영향을 미치지 않습니다.
  • 값에 의한 전달은 원본 값을 수정할 필요가 없을 때 int, char, float 등과 같은 간단한 데이터 타입에 적합합니다.
  • 큰 객체를 값에 의한 전달으로 전달할 때 복사는 더 많은 메모리와 시간을 소비할 수 있어 참조에 의한 전달에 비해 효율성이 떨어집니다.

값에 의한 전달을 문서의 복사본을 만드는 것에 비유할 수 있습니다. 원본은 변경되지 않고 복사본을 수정해도 원본에 영향을 주지 않습니다.

& 연산자를 사용한 참조 전달 구현

이 단계에서는 C++ 에서 & 연산자를 사용하여 함수에 매개변수를 참조로 전달하는 방법을 배우게 됩니다. 참조 전달은 함수에 원본 변수에 대한 직접적인 액세스를 제공하므로 함수 내에서 매개변수에 대한 모든 변경은 함수 외부의 원본 변수를 직접 수정합니다.

WebIDE 를 열고 ~/project 디렉토리에 pass_by_reference.cpp라는 새 파일을 만듭니다.

touch ~/project/pass_by_reference.cpp

pass_by_reference.cpp 파일을 에디터에서 열고 다음 코드를 추가합니다.

#include <iostream>

// 참조 전달을 시연하는 함수
void swapValues(int& a, int& b) {
    // 원본 변수를 직접 수정합니다.
    int temp = a;
    a = b;
    b = temp;
}

// 참조를 사용하여 값을 수정하는 함수
void incrementValue(int& x) {
    // 원본 변수를 직접 증가시킵니다.
    x++;
}

int main() {
    // Swap 예제
    int first = 5;
    int second = 10;

    std::cout << "Before swap - first: " << first << ", second: " << second << std::endl;

    // 참조로 swap 함수 호출
    swapValues(first, second);

    std::cout << "After swap - first: " << first << ", second: " << second << std::endl;

    // Increment 예제
    int number = 7;

    std::cout << "Before increment: " << number << std::endl;

    // 참조로 increment 함수 호출
    incrementValue(number);

    std::cout << "After increment: " << number << std::endl;

    return 0;
}

프로그램을 컴파일하고 실행합니다.

g++ pass_by_reference.cpp -o pass_by_reference
./pass_by_reference

예시 출력:

Before swap - first: 5, second: 10
After swap - first: 10, second: 5
Before increment: 7
After increment: 8

참조 전달의 강력함을 보여주기 위해 다른 예제를 만들어 보겠습니다.

touch ~/project/string_reference.cpp

string_reference.cpp에 다음 코드를 추가합니다.

#include <iostream>
#include <string>

// 참조를 사용하여 문자열을 수정하는 함수
void appendText(std::string& text) {
    text += " - Modified";
}

int main() {
    std::string message = "Hello, World!";

    std::cout << "Original message: " << message << std::endl;

    // 참조를 사용하여 문자열 직접 수정
    appendText(message);

    std::cout << "Modified message: " << message << std::endl;

    return 0;
}

두 번째 프로그램을 컴파일하고 실행합니다.

g++ string_reference.cpp -o string_reference
./string_reference

예시 출력:

Original message: Hello, World!
Modified message: Hello, World! - Modified

참조 전달에 대한 주요 사항:

  • 참조 매개변수를 생성하려면 타입 뒤에 &를 사용합니다 (예: int& a).
  • 복사본을 사용하는 값에 의한 전달과 달리 원본 변수를 직접 수정합니다.
  • 큰 객체의 복사 오버헤드를 피하여 복잡한 데이터 타입에 더 효율적입니다.
  • 원본 값을 수정하거나 복사 없이 복잡한 데이터 타입으로 작업해야 할 때 유용합니다.

참조 전달을 복사본이 아닌 원본 문서를 누군가에게 주는 것에 비유할 수 있습니다. 그들이 하는 모든 변경은 원본에 직접적인 영향을 미칩니다.

함수 매개변수에 기본값 설정하기

이 단계에서는 C++ 에서 함수 매개변수에 대한 기본값을 설정하는 방법을 배우게 됩니다. 기본 매개변수를 사용하면 함수 인수에 기본값을 할당할 수 있어 함수를 더 유연하고 사용하기 쉽게 만들 수 있습니다. 호출자가 기본값이 있는 매개변수를 생략하면 기본값이 자동으로 사용됩니다.

WebIDE 를 열고 ~/project 디렉토리에 default_parameters.cpp라는 새 파일을 만듭니다.

touch ~/project/default_parameters.cpp

default_parameters.cpp 파일을 에디터에서 열고 다음 코드를 추가합니다.

#include <iostream>
#include <string>

// 기본 매개변수가 있는 함수
void greetUser(std::string name = "Guest", int age = 0) {
    std::cout << "Hello, " << name << "!" << std::endl;

    if (age > 0) {
        std::cout << "You are " << age << " years old." << std::endl;
    }
}

// 계산을 위한 기본 매개변수가 있는 또 다른 예제
double calculateArea(double length = 1.0, double width = 1.0) {
    return length * width;
}

int main() {
    // 인수가 없는 함수 호출 (기본값 사용)
    greetUser();

    // 부분 인수로 함수 호출
    greetUser("Alice");

    // 모든 인수로 함수 호출
    greetUser("Bob", 25);

    // 기본 매개변수를 사용한 면적 계산 시연
    std::cout << "\nArea calculations:" << std::endl;

    // 기본 면적 (1x1)
    std::cout << "Default area: " << calculateArea() << std::endl;

    // 매개변수 하나로 면적 계산
    std::cout << "Area with length 5: " << calculateArea(5.0) << std::endl;

    // 두 매개변수로 면적 계산
    std::cout << "Area with length 5 and width 3: " << calculateArea(5.0, 3.0) << std::endl;

    return 0;
}

프로그램을 컴파일하고 실행합니다.

g++ default_parameters.cpp -o default_parameters
./default_parameters

예시 출력:

Hello, Guest!
Hello, Alice!
Hello, Bob!
You are 25 years old.

Area calculations:
Default area: 1
Area with length 5: 5
Area with length 5 and width 3: 15

기본 매개변수에 대한 주요 사항:

  • 기본값은 함수 선언에서 매개변수 타입 바로 뒤에 지정됩니다 (예: int age = 0).
  • 기본값이 있는 매개변수는 매개변수 목록의 끝에 있어야 합니다. 기본 매개변수 뒤에 기본값이 아닌 매개변수를 둘 수 없습니다.
  • 더 적은 수의 인수로 함수를 호출할 수 있으며, 누락된 인수는 기본값을 사용합니다.
  • 기본값은 유연성을 제공하고 약간 다른 입력으로 동일한 작업을 수행하는 여러 함수 오버로딩의 필요성을 줄입니다.

더 복잡한 기본 매개변수를 시연하기 위해 다른 예제를 만들어 보겠습니다.

touch ~/project/student_info.cpp

student_info.cpp에 다음 코드를 추가합니다.

#include <iostream>
#include <string>

// 여러 기본 매개변수가 있는 함수
void printStudentInfo(
    std::string name = "Unknown",
    int age = 0,
    std::string grade = "N/A",
    bool isEnrolled = false
) {
    std::cout << "Name: " << name << std::endl;
    std::cout << "Age: " << age << std::endl;
    std::cout << "Grade: " << grade << std::endl;
    std::cout << "Enrollment Status: " << (isEnrolled ? "Enrolled" : "Not Enrolled") << std::endl;
}

int main() {
    // 함수를 호출하는 다양한 방법
    printStudentInfo();  // 모든 기본값
    std::cout << "\n";
    printStudentInfo("John");  // 이름만
    std::cout << "\n";
    printStudentInfo("Alice", 20);  // 이름과 나이
    std::cout << "\n";
    printStudentInfo("Bob", 22, "A", true);  // 모든 매개변수

    return 0;
}

두 번째 프로그램을 컴파일하고 실행합니다.

g++ student_info.cpp -o student_info
./student_info

예시 출력:

Name: Unknown
Age: 0
Grade: N/A
Enrollment Status: Not Enrolled

Name: John
Age: 0
Grade: N/A
Enrollment Status: Not Enrolled

Name: Alice
Age: 20
Grade: N/A
Enrollment Status: Not Enrolled

Name: Bob
Age: 22
Grade: A
Enrollment Status: Enrolled

기본 매개변수를 뷔페에 비유할 수 있습니다. 모든 항목을 가져가거나 몇 개만 가져갈 수 있으며, 지정하지 않으면 일부 항목이 자동으로 채워집니다.

다른 매개변수로 함수 오버로드 생성하기

이 단계에서는 C++ 의 함수 오버로드에 대해 배우게 됩니다. 함수 오버로드는 매개변수 목록 (매개변수의 수 또는 매개변수의 타입) 이 다르면 동일한 이름을 가진 여러 함수를 생성할 수 있는 기능입니다. 이 기능을 사용하면 동일한 함수 이름을 사용하여 다른 데이터 타입에 대해 유사한 작업을 수행하거나 다양한 수의 입력을 처리할 수 있어 코드를 더 유연하고 읽기 쉽게 만들 수 있습니다.

WebIDE 를 열고 ~/project 디렉토리에 function_overloads.cpp라는 새 파일을 만듭니다.

touch ~/project/function_overloads.cpp

function_overloads.cpp 파일을 에디터에서 열고 다음 코드를 추가합니다.

#include <iostream>
#include <string>

// 다른 타입의 숫자를 더하는 함수 오버로드
int add(int a, int b) {
    std::cout << "Adding two integers" << std::endl;
    return a + b;
}

double add(double a, double b) {
    std::cout << "Adding two doubles" << std::endl;
    return a + b;
}

// 매개변수 수가 다른 함수 오버로드
int add(int a, int b, int c) {
    std::cout << "Adding three integers" << std::endl;
    return a + b + c;
}

// 매개변수 타입이 다른 함수 오버로드
std::string add(std::string a, std::string b) {
    std::cout << "Concatenating strings" << std::endl;
    return a + b;
}

int main() {
    // 다른 오버로드된 함수 호출
    std::cout << "Integer addition: " << add(5, 3) << std::endl;
    std::cout << "Double addition: " << add(5.5, 3.7) << std::endl;
    std::cout << "Three integer addition: " << add(1, 2, 3) << std::endl;
    std::cout << "String concatenation: " << add("Hello, ", "World!") << std::endl;

    return 0;
}

프로그램을 컴파일하고 실행합니다.

g++ function_overloads.cpp -o function_overloads
./function_overloads

예시 출력:

Adding two integers
Integer addition: 8
Adding two doubles
Double addition: 9.2
Adding three integers
Three integer addition: 6
Concatenating strings
String concatenation: Hello, World!

더 많은 함수 오버로드를 시연하기 위해 다른 예제를 만들어 보겠습니다.

touch ~/project/overloading_examples.cpp

overloading_examples.cpp에 다음 코드를 추가합니다.

#include <iostream>
#include <string>

// 오버로드된 print 함수
void print(int value) {
    std::cout << "Printing integer: " << value << std::endl;
}

void print(double value) {
    std::cout << "Printing double: " << value << std::endl;
}

void print(std::string value) {
    std::cout << "Printing string: " << value << std::endl;
}

// 오버로드된 calculate 함수
int calculate(int a, int b) {
    return a + b;
}

double calculate(double a, double b) {
    return a * b;
}

int main() {
    // 함수 오버로드 시연
    print(42);
    print(3.14);
    print("Hello, Overloading!");

    std::cout << "Integer calculation: " << calculate(5, 3) << std::endl;
    std::cout << "Double calculation: " << calculate(5.5, 3.2) << std::endl;

    return 0;
}

두 번째 프로그램을 컴파일하고 실행합니다.

g++ overloading_examples.cpp -o overloading_examples
./overloading_examples

예시 출력:

Printing integer: 42
Printing double: 3.14
Printing string: Hello, Overloading!
Integer calculation: 8
Double calculation: 17.6

함수 오버로드에 대한 주요 사항:

  • 함수는 매개변수 목록 (다른 타입 또는 다른 수의 매개변수) 이 다르면 동일한 이름을 가질 수 있습니다.
  • C++ 컴파일러는 함수 호출 시 제공된 인수의 타입과 수에 따라 어떤 오버로드된 함수를 호출할지 결정합니다.
  • 함수 오버로드는 유사한 동작에 이름을 재사용할 수 있도록 하여 더 읽기 쉽고 직관적인 코드를 만듭니다.
  • 오버로드된 함수는 반환 타입만 다를 경우 오버로드할 수 없습니다. 오버로드가 올바르게 작동하려면 매개변수가 달라야 합니다.

함수 오버로드를 다양한 종류의 장치와 작동하는 만능 리모컨에 비유할 수 있습니다. 동일한 버튼 (함수 이름) 이 제공하는 컨텍스트 (인수) 에 따라 다른 작업을 수행합니다.

팩토리얼 계산을 위한 재귀 함수 작성

이 단계에서는 팩토리얼 계산 프로그램을 구현하여 재귀 함수에 대해 배우게 됩니다. 재귀는 함수가 문제를 해결하기 위해 자신을 호출하는 프로그래밍 기법입니다. 무한 루프를 피하기 위해 재귀 함수는 재귀 호출을 중지하는 기본 사례 (base case) 와 기본 사례를 향해 계속 자신을 호출하는 재귀 사례 (recursive case) 를 가져야 합니다.

WebIDE 를 열고 ~/project 디렉토리에 factorial_recursive.cpp라는 새 파일을 만듭니다.

touch ~/project/factorial_recursive.cpp

factorial_recursive.cpp 파일을 에디터에서 열고 다음 코드를 추가합니다.

#include <iostream>

// 팩토리얼을 계산하는 재귀 함수
unsigned long long calculateFactorial(int n) {
    // 기본 사례: 0 또는 1 의 팩토리얼은 1 입니다.
    if (n == 0 || n == 1) {
        return 1;
    }

    // 재귀 사례: n! = n * (n-1)!
    return n * calculateFactorial(n - 1);
}

int main() {
    // 여러 숫자에 대한 팩토리얼 계산
    int numbers[] = {0, 1, 5, 7, 10};

    for (int num : numbers) {
        unsigned long long result = calculateFactorial(num);
        std::cout << "Factorial of " << num << " is: " << result << std::endl;
    }

    return 0;
}

프로그램을 컴파일하고 실행합니다.

g++ factorial_recursive.cpp -o factorial_recursive
./factorial_recursive

예시 출력:

Factorial of 0 is: 1
Factorial of 1 is: 1
Factorial of 5 is: 120
Factorial of 7 is: 5040
Factorial of 10 is: 3628800

더 상세한 재귀 함수로 다른 예제를 만들어 보겠습니다.

touch ~/project/factorial_steps.cpp

factorial_steps.cpp에 다음 코드를 추가합니다.

#include <iostream>

// 단계별 출력이 있는 재귀 함수
unsigned long long calculateFactorialWithSteps(int n, int depth = 0) {
    // 시각화를 위해 들여쓰기 추가
    std::string indent(depth * 2, ' ');

    // 기본 사례: 0 또는 1 의 팩토리얼은 1 입니다.
    if (n == 0 || n == 1) {
        std::cout << indent << "Base case: factorial(" << n << ") = 1" << std::endl;
        return 1;
    }

    // 시각화를 포함한 재귀 사례
    std::cout << indent << "Calculating factorial(" << n << ")" << std::endl;

    // 재귀 호출
    unsigned long long subResult = calculateFactorialWithSteps(n - 1, depth + 1);

    // 결과 결합
    unsigned long long result = n * subResult;

    std::cout << indent << "factorial(" << n << ") = "
              << n << " * factorial(" << n-1 << ") = "
              << result << std::endl;

    return result;
}

int main() {
    int number = 5;

    std::cout << "Factorial calculation steps for " << number << ":" << std::endl;
    unsigned long long result = calculateFactorialWithSteps(number);

    std::cout << "\nFinal result: " << number << "! = " << result << std::endl;

    return 0;
}

두 번째 프로그램을 컴파일하고 실행합니다.

g++ factorial_steps.cpp -o factorial_steps
./factorial_steps

예시 출력:

Factorial calculation steps for 5:
Calculating factorial(5)
  Calculating factorial(4)
    Calculating factorial(3)
      Calculating factorial(2)
        Calculating factorial(1)
        Base case: factorial(1) = 1
      factorial(2) = 2 * factorial(1) = 2
    factorial(3) = 3 * factorial(2) = 6
  factorial(4) = 4 * factorial(3) = 24
factorial(5) = 5 * factorial(4) = 120

Final result: 5! = 120

재귀 함수에 대한 주요 사항:

  • 재귀 함수는 자신을 호출합니다: 함수는 동일한 유형의 더 작은 하위 문제를 해결하기 위해 자신을 호출합니다.
  • 기본 사례 (Base Case): 재귀 함수는 재귀 호출을 중지하는 조건인 기본 사례를 가져야 합니다. 기본 사례가 없으면 함수는 무한히 자신을 호출하여 스택 오버플로 오류가 발생합니다.
  • 재귀 사례 (Recursive Case): 재귀 사례는 함수가 기본 사례를 향해 자신을 호출하는 곳입니다.
  • 문제 분해: 재귀는 복잡한 문제를 더 작고 간단한 하위 문제로 분해하여 관리하기 쉽게 만듭니다.
  • 스택 사용: 각 재귀 호출은 프로그램의 호출 스택에 공간을 차지합니다. 깊은 재귀는 스택 오버플로를 유발할 수 있으므로 호출 스택 제한에 유의해야 합니다.
  • 적합한 문제: 재귀는 트리 순회, 분할 정복 알고리즘, 프랙탈 생성과 같이 더 작고 유사한 하위 문제로 자연스럽게 정의될 수 있는 문제에 특히 적합합니다.

재귀를 러시아 인형 세트에 비유할 수 있습니다. 각 인형은 더 작은 버전의 자신을 포함하고 있으며, 가장 안쪽 인형은 중첩 과정을 중지하는 기본 사례를 나타냅니다.

헤더 파일에서 함수 프로토타입 사용하기

이 단계에서는 함수 프로토타입과 C++ 에서 헤더 파일을 사용하여 함수를 구성하고 선언하는 방법에 대해 배우게 됩니다. 함수 프로토타입은 함수의 구현 (함수의 본문) 을 제공하지 않고 함수의 이름, 반환 타입 및 매개변수를 선언합니다. 헤더 파일을 사용하면 함수의 인터페이스 (선언) 와 구현을 분리할 수 있습니다. 이러한 분리는 코드 구성을 개선하여 프로그램의 여러 부분이나 여러 프로그램에서 함수를 유지 관리하고 재사용하기 쉽게 만듭니다.

WebIDE 를 열고 ~/project 디렉토리에 세 개의 파일을 만듭니다.

먼저 math_functions.h를 만듭니다.

touch ~/project/math_functions.h

math_functions.h에 다음 코드를 추가합니다.

#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H

// 수학 연산을 위한 함수 프로토타입
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
double divide(double a, double b);

#endif // MATH_FUNCTIONS_H

.h 파일은 함수 프로토타입 및 기타 선언을 포함하는 선언에 사용되며 구현은 포함하지 않습니다. 이런 식으로 구현 없이 함수를 선언할 수 있습니다. #ifndef, #define, #endif 지시문은 include guard 라고 하며, 헤더 파일의 중복 포함을 방지하여 오류를 일으킬 수 있습니다.

다음으로 math_functions.cpp를 만듭니다.

touch ~/project/math_functions.cpp

math_functions.cpp에 다음 코드를 추가합니다.

#include "math_functions.h"

// 함수 구현
int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int multiply(int a, int b) {
    return a * b;
}

double divide(double a, double b) {
    // 0 으로 나누기 확인
    if (b == 0) {
        return 0;
    }
    return a / b;
}

.cpp 파일에는 헤더 파일에 선언된 함수의 실제 구현이 포함되어 있습니다.

마지막으로 main.cpp를 만듭니다.

touch ~/project/main.cpp

main.cpp에 다음 코드를 추가합니다.

#include <iostream>
#include "math_functions.h"

int main() {
    // 헤더 파일에서 함수 호출 시연
    int x = 10, y = 5;

    std::cout << "Addition: " << x << " + " << y << " = " << add(x, y) << std::endl;
    std::cout << "Subtraction: " << x << " - " << y << " = " << subtract(x, y) << std::endl;
    std::cout << "Multiplication: " << x << " * " << y << " = " << multiply(x, y) << std::endl;
    std::cout << "Division: " << x << " / " << y << " = " << divide(x, y) << std::endl;

    return 0;
}

main.cpp 파일은 math_functions.h 헤더 파일을 포함하여 함수 프로토타입을 사용할 수 있게 합니다. 그런 다음 math_functions.cpp에 구현된 함수를 사용할 수 있습니다.

여러 소스 파일을 사용하여 프로그램을 컴파일합니다.

g++ math_functions.cpp main.cpp -o math_operations
./math_operations

예시 출력:

Addition: 10 + 5 = 15
Subtraction: 10 - 5 = 5
Multiplication: 10 * 5 = 50
Division: 10 / 5 = 2

더 복잡한 헤더 파일로 다른 예제를 만들어 보겠습니다.

calculator.h를 만듭니다.

touch ~/project/calculator.h

calculator.h에 다음 코드를 추가합니다.

#ifndef CALCULATOR_H
#define CALCULATOR_H

class Calculator {
public:
    // 계산기 연산을 위한 함수 프로토타입
    int add(int a, int b);
    int subtract(int a, int b);
    int calculate(int a, int b, char operation);
};

#endif // CALCULATOR_H

이 헤더 파일은 Calculator라는 클래스와 해당 공개 메서드를 선언합니다.

calculator.cpp를 만듭니다.

touch ~/project/calculator.cpp

calculator.cpp에 다음 코드를 추가합니다.

#include "calculator.h"

int Calculator::add(int a, int b) {
    return a + b;
}

int Calculator::subtract(int a, int b) {
    return a - b;
}

int Calculator::calculate(int a, int b, char operation) {
    switch (operation) {
        case '+': return add(a, b);
        case '-': return subtract(a, b);
        default: return 0;
    }
}

calculator.cppcalculator.h 헤더 파일에 선언된 메서드에 대한 구현을 제공합니다.

calculator_main.cpp를 만듭니다.

touch ~/project/calculator_main.cpp

calculator_main.cpp에 다음 코드를 추가합니다.

#include <iostream>
#include "calculator.h"

int main() {
    Calculator calc;

    std::cout << "Calculator Operations:" << std::endl;
    std::cout << "10 + 5 = " << calc.calculate(10, 5, '+') << std::endl;
    std::cout << "10 - 5 = " << calc.calculate(10, 5, '-') << std::endl;

    return 0;
}

이 메인 파일은 Calculator 클래스를 사용하고 연산을 수행합니다.

계산기 프로그램을 컴파일합니다.

g++ calculator.cpp calculator_main.cpp -o calculator
./calculator

예시 출력:

Calculator Operations:
10 + 5 = 15
10 - 5 = 5

함수 프로토타입 및 헤더 파일에 대한 주요 사항:

  • **헤더 파일 (.h)**은 함수 프로토타입, 클래스 및 기타 선언을 선언합니다. 인터페이스 역할을 하여 어떤 함수를 사용할 수 있는지 개략적으로 설명합니다.
  • **소스 파일 (.cpp)**은 헤더 파일에 선언된 실제 함수를 구현합니다. 함수가 작동하는 방식에 대한 코드를 포함합니다.
  • #ifndef, #define, #endif (include guard) 는 동일한 헤더 파일의 중복 포함을 방지하여 잠재적인 컴파일 오류를 방지합니다.
  • 헤더 파일을 사용하면 모듈화 및 코드 재사용성을 높일 수 있습니다.
  • 헤더 파일을 사용하면 "무엇" (선언) 과 "어떻게" (구현) 를 분리할 수 있습니다.
  • 코드 구성을 용이하게 하여 유지 관리 및 이해를 쉽게 합니다.

헤더 파일을 레스토랑 메뉴에 비유할 수 있습니다. 메뉴 (헤더) 는 무엇을 사용할 수 있는지 나열하고, 주방 (소스 파일) 은 실제 요리를 준비합니다.

구조체를 사용하여 여러 값 반환하기

이 단계에서는 C++ 에서 함수에서 여러 값을 반환하기 위해 구조체를 사용하는 방법을 배우게 됩니다. 구조체는 사용자 정의 데이터 타입으로, 서로 다른 유형의 데이터를 단일 이름으로 그룹화할 수 있습니다. 구조체는 관련 여러 값을 구조화되고 체계적인 방식으로 함수에서 반환하는 데 이상적입니다.

WebIDE 를 열고 ~/project 디렉토리에서 student_info.cpp 파일을 업데이트합니다.

기존 코드를 다음 코드로 바꿉니다.

#include <iostream>
#include <string>

// 학생 정보를 담을 구조체 정의
struct StudentResult {
    std::string name;
    int score;
    bool passed;
};

// 구조체를 사용하여 여러 값을 반환하는 함수
StudentResult evaluateStudent(std::string name, int testScore) {
    StudentResult result;
    result.name = name;
    result.score = testScore;
    result.passed = (testScore >= 60);

    return result;
}

// 여러 통계 값을 계산하는 함수
struct MathStats {
    int sum;
    double average;
    int minimum;
    int maximum;
};

MathStats calculateArrayStats(int numbers[], int size) {
    MathStats stats;
    stats.sum = 0;
    stats.minimum = numbers[0];
    stats.maximum = numbers[0];

    for (int i = 0; i < size; i++) {
        stats.sum += numbers[i];

        if (numbers[i] < stats.minimum) {
            stats.minimum = numbers[i];
        }

        if (numbers[i] > stats.maximum) {
            stats.maximum = numbers[i];
        }
    }

    stats.average = static_cast<double>(stats.sum) / size;

    return stats;
}

int main() {
    // 학생 평가 시연
    StudentResult student1 = evaluateStudent("Alice", 75);
    StudentResult student2 = evaluateStudent("Bob", 45);

    std::cout << "Student Results:" << std::endl;
    std::cout << student1.name << " - Score: " << student1.score
              << ", Passed: " << (student1.passed ? "Yes" : "No") << std::endl;
    std::cout << student2.name << " - Score: " << student2.score
              << ", Passed: " << (student2.passed ? "Yes" : "No") << std::endl;

    // 배열 통계 시연
    int numbers[] = {5, 2, 9, 1, 7};
    int size = sizeof(numbers) / sizeof(numbers[0]);

    MathStats stats = calculateArrayStats(numbers, size);

    std::cout << "\nArray Statistics:" << std::endl;
    std::cout << "Sum: " << stats.sum << std::endl;
    std::cout << "Average: " << stats.average << std::endl;
    std::cout << "Minimum: " << stats.minimum << std::endl;
    std::cout << "Maximum: " << stats.maximum << std::endl;

    return 0;
}

프로그램을 컴파일하고 실행합니다.

g++ student_info.cpp -o student_info
./student_info

예시 출력:

Student Results:
Alice - Score: 75, Passed: Yes
Bob - Score: 45, Passed: No

Array Statistics:
Sum: 24
Average: 4.8
Minimum: 1
Maximum: 9

구조체 및 여러 값 반환에 대한 주요 사항:

  • 구조체는 관련 데이터를 함께 그룹화합니다: 서로 다른 유형의 변수 (예: string, int, bool) 를 단일 단위로 그룹화할 수 있으며, 관련 정보가 있을 때 유용할 수 있습니다.
  • 복잡한 데이터 타입 반환: 구조체를 사용하면 함수에서 복잡한 데이터 타입을 반환할 수 있어 관련 데이터 세트를 쉽게 관리하고 전달할 수 있습니다.
  • 정리된 반환 값: 구조체를 반환하면 여러 관련 반환 값을 정리하는 데 도움이 되어 코드를 더 깔끔하고 유지 관리하기 쉽게 만듭니다.
  • 구조화된 정보 전달을 위한 명확한 방법 제공: 구조체는 여러 관련 값을 반환하기 위한 명확하고 이름이 지정된 컨테이너를 제공하여 코드의 가독성과 이해도를 향상시킵니다.

구조체를 여러 종류의 음식을 별도의 칸에 담을 수 있는 칸막이 도시락에 비유할 수 있습니다.

요약

이 실습에서는 C++ 에서 함수를 정의하고 사용하는 방법에 대한 포괄적인 이해를 얻었습니다. 다양한 반환 타입을 가진 함수를 생성하고, 값으로 매개변수를 전달하고, 참조로 매개변수를 전달하고, 기본 매개변수 값을 설정하고, 함수 오버로딩을 활용하는 방법을 배웠습니다. 또한 재귀 함수, 헤더 파일에서 함수 프로토타입을 사용하는 방법, 구조체를 사용하여 여러 값을 반환하는 방법을 살펴보았습니다.

다루어진 주요 개념은 다음과 같습니다.

  • 함수 반환 타입: 함수가 다른 데이터 타입을 반환할 수 있으며 return 문은 선언된 반환 타입과 일치해야 한다는 것을 배웠습니다.
  • 값으로 전달 및 참조로 전달: 값으로 매개변수를 전달하는 것 (복사본 만들기) 과 참조로 전달하는 것 (원본 변수 사용) 의 차이를 이해했습니다.
  • 기본 매개변수: 함수 매개변수에 기본값을 할당하여 함수를 더 다재다능하게 만드는 방법을 배웠습니다.
  • 함수 오버로딩: 동일한 이름을 가지지만 매개변수 목록이 다른 여러 함수를 정의하는 방법을 보았으며, 코드를 더 읽기 쉽고 직관적으로 만들었습니다.
  • 재귀 함수: 함수가 더 작은 하위 문제를 해결하기 위해 자신을 호출하는 재귀 함수의 강력함을 탐구했으며, 무한 재귀를 방지하기 위한 기본 사례의 중요성도 함께 살펴보았습니다.
  • 함수 프로토타입 및 헤더 파일: 헤더 파일을 사용하여 함수 선언을 구성하는 방법을 배웠으며, 모듈화 및 코드 재사용성을 촉진했습니다.
  • 구조체를 사용한 여러 값 반환: 구조체를 사용하여 함수에서 여러 관련 값을 반환하는 방법을 발견했으며, 이는 데이터에 대한 정리된 컨테이너를 제공합니다.

이러한 개념을 숙달함으로써 이제 더 모듈화되고 효율적이며 잘 구조화된 C++ 코드를 작성할 수 있게 되었으며, 더 고급 프로그래밍 기술을 탐색할 수 있는 견고한 기반을 갖추게 되었습니다.