The Essentials of C++, David hunter

Chapter 5. Funtions

5.1 Function Structures 함수 구조

  • A typical C++ function definition is shown below.

  • 전형적인 C++ 함수 정의는 아래와 같이 나타납니다.

int power(int base, int exponent)
{
    int product = 1;
    for (int i = 1; i <= exponent; ++i) {
        product = product * base;
    }
    return product;
}
  • A function may contain any number of return statements. A function terminates with the value specified as soon as any return is encountered. Local variables may be declared anywhere within the function provided they are declared before they are referenced.

  • 함수는 return 문을 몇개든 포함할 수 있습니다. 함수는 어떤 return 문을 만나면 지정된 값으로 종료됩니다. 지역 변수는 함수 내에서 어디서든 선언할 수 있으며, 참조되기 전에 선언되어야 합니다.

  • C++ does not make a distinction between “procedures” and “functions”. All subroutines in C++ are considered functions. However, procedures may be emulated by declaring a function to be of void type. Such functions need not include return statements. Functions for which no type is specified (not even void) are considered to be of type int.

  • C++에서는 “프로시저(procedures)”와 “함수(functions)” 사이의 구분을 지정하지 않습니다. C++의 모든 하위 루틴은 함수로 간주됩니다. 그러나 프로시저는 반환 유형을 void로 선언함으로써 에뮬레이트될 수 있습니다. 이러한 함수는 반환 문을 포함할 필요가 없습니다. 반환 유형이 지정되지 않은 함수들 (심지어 void도 아닌 경우)은 int 유형으로 간주됩니다.

추가 해설
return 값이 없는 하위 루틴은 procedure라고 부르는데, Pascal, SQL, COBOL 등에서 사용한다. (ex. stored procedure)
C++에서는 엄격하게 구분하지 않고, 반환유형을 void라고 지정하면 된다.

  • Functions that do not have parameters may be specified using either the notation type f() or type f(void). When parameterless functions are called, the notation f() must be specified.

  • 매개변수가 없는 함수들은 type f() 또는 type f(void)와 같은 표기법을 사용하여 지정할 수 있습니다. 매개변수가 없는 함수를 호출할 때는 f()와 같이 표기해야 합니다.

5.2 Parameter Passing Mechanisms 매개변수 전달 방식

추가해설
C++에서 지원하는 매개변수 전달 방식 참고 블로그

void func(int x); // Pass by value
void func(int& x); // Pass by Reference
void func(int* x); // Pass by Pointer
void func(const int& x); // Pass by Const Reference
// Pass by Value Result
void func(int&& x); // Pass by Rvalue Reference

예시 Pass by Value
매개변수 값을 복사하여 함수에 전달하는 방식.
함수 내에서 매개변수 값을 변경하더라도 호출자에게는 영향을 미치지 않음
Call by value

#include <iostream>

void modifyValue(int x) {
    x = x * 2;
}

int main() {
    int originalValue = 10;
    modifyValue(originalValue);
    std::cout << "originalValue: " << originalValue << std::endl; // 10
    return 0;
}

예시 Pass by Reference
매개변수에 대한 참조(주소)를 전달하여 함수에서 직접 해당 매개변수를 조작할 수 있는 방식.
매개변수 값을 변경하면 호출자에게도 영향을 미친다
Call by reference

#include <iostream>

void modifyValue(int& x) {
    x = x * 2;
}

int main() {
    int originalValue = 10;
    modifyValue(originalValue);
    std::cout << "originalValue: " << originalValue << std::endl; // 20
    return 0;
}

예시 Pass by Pointer
매개변수에 대한 포인터를 전달하여 함수에서 포인터를 통해 해당 매개변수를 조작할 수 있는 방식
Pass by Reference와 유사하지만 포인터의 활용이 가능

#include <iostream>

void modifyValue(int* x) {
    *x = *x * 2;
}

int main() {
    int originalValue = 10;
    modifyValue(&originalValue);
    std::cout << "originalValue: " << originalValue << std::endl; // 20
    return 0;
}

예시 Pass by Const Reference
함수에 인자로 변수의 참조를 전달하되, 해당 변수를 수정하지 않고 읽기만 하는 경우

#include <iostream>

void modifyValue(const int& x) {
    // 아래 코드는 에러를 발생시킬 것이므로 주석 처리
    // x = x * 2;
}

int main() {
    int originalValue = 10;
    modifyValue(originalValue);
    std::cout << "originalValue: " << originalValue << std::endl;
    return 0;
}
#include <iostream>

// 상수 참조를 사용하여 두 수의 합을 계산하는 함수
int add(const int& a, const int& b) {
    return a + b;
}

int main() {
    int num1 = 5;
    int num2 = 7;

    // add 함수를 호출하고 결과를 출력
    int sum = add(num1, num2);

    std::cout << "Sum: " << sum << std::endl;

    // 원래 변수는 수정되지 않음
    std::cout << "num1: " << num1 << std::endl;
    std::cout << "num2: " << num2 << std::endl;

    return 0;
}

예시 Pass by Value Result
C++에서는 직접 “pass by value result”를 지원하지는 않으나 “pass by value”와 유사한 동작을 “pass by reference”나 “pass by pointer”를 사용하여 구현할 수 있음.

// Refer to source code

예시 Pass by Rvalue Reference
C++11 이후에 도입된 개념으로, 이동 가능한 우측값(임시 객체)을 전달할 때 사용
임시 객체나 임시 결과 값을 효율적으로 처리하려는 경우, 이동 의미론(Move Semantics)을 활용하여 성능을 향상시키려는 경우, 오버로딩된 함수 중에서 좌측값 참조와 구분하여 호출하고 싶은 경우 등에서 활용한다.

#include <iostream>

// 함수 정의에서 우측값 참조를 사용
void processValue(int&& value) {
    std::cout << "Received: " << value << std::endl;
    // 여기에서 value를 처리하는 작업을 수행
}

int main() {
    // 우측값을 함수에 전달
    processValue(42);

    // 변수를 우측값으로 캐스팅하여 전달
    int x = 100;
    processValue(std::move(x)); // std::move()를 사용하여 우측값으로 변환

    return 0;
}
  • C++ provides two fundamental mechanisms for passing parameters to functions: pass by value and pass by reference. With the exception of array parameters, which are discussed later, pass by value is the default mechanism. When this mechanism is used, the parameter values in the function call (“actual parameters”) are copied into the parameters declared in the function definition (“formal parameters”). Modifying a formal parameter passed by value has no effect on its corresponding actual parameter.

  • C++은 함수에 매개변수를 전달하는 두 가지 기본 메커니즘을 제공합니다: 값에 의한 전달(pass by value)과 참조에 의한 전달(pass by reference). 나중에 논의할 배열 매개변수를 제외하고는 값에 의한 전달이 기본 메커니즘입니다. 이 메커니즘을 사용할 때 함수 호출에서의 매개변수 값(“실제 매개변수”)이 함수 정의에서 선언된 매개변수(“형식 매개변수”)로 복사됩니다. 값에 의해 전달된 형식 매개변수를 수정하더라도 해당하는 실제 매개변수에는 영향을 미치지 않습니다.

  • When the pass by reference method is used, a formal parameter becomes an alias for its corresponding actual parameter. Changes made to the formal parameter are immediately reflected in the actual parameter. The “&” symbol is used to indicate that a parameter should be implemented using the reference method.

  • 참조에 의한 전달 방법을 사용할 때, 형식 매개변수는 해당하는 실제 매개변수의 별칭(alias)이 됩니다. 형식 매개변수에 대한 변경 사항은 즉시 해당하는 실제 매개변수에 반영됩니다. “&” 기호는 매개변수가 참조 방법을 사용하여 구현되어야 함을 나타내는 데 사용됩니다.

  • The overhead involved in copying values from actual to formal parameters can make the pass by value method inappropriate for structures. Consequently, many programmers prefer to pass structures (and objects, as seen later) by reference and pass scalars (integers, reals, and so forth) by value.

  • 실제 매개변수에서 형식 매개변수로 값 복사하는 데 관련된 오버헤드는 구조체에 대한 값에 의한 전달 방법을 부적절하게 만들 수 있습니다. 따라서 많은 프로그래머들은 구조체(그리고 나중에 볼 것과 같이 객체)를 참조에 의해 전달하고 스칼라 값(정수, 실수 등)을 값에 의해 전달하는 것을 선호합니다.

  • Both types of parameters may be prefaced with the keyword const to indicate that they are read only parameters. Such parameters can have their values accessed, but cannot be assigned to.

  • 두 유형의 매개변수 모두 읽기 전용 매개변수임을 나타내기 위해 키워드 “const”로 시작될 수 있습니다. 이러한 매개변수의 값은 접근할 수 있지만 할당할 수는 없습니다.

  • When arrays are used as actual parameters, the corresponding formal parameter is treated as a pointer to the first element of the array. This is illustrated in the example below.

  • 배열이 실제 매개변수로 사용될 때, 해당하는 형식 매개변수는 배열의 첫 번째 요소를 가리키는 포인터로 처리됩니다. 이는 아래의 예제에서 설명되어 있습니다.

#include <iostream>
using namespace std;

void read_array(int *a, int& size)
{
    cout << "How many data points?";
    cin >> size;
    cout << "Enter data: \n ";
    for (int i = 0; i < size; ++i)
        cin >> a[i];
}

int sum(const int *a, const int size)
{
    int i, temp = 0;
    for (int i = 0; i < size; ++i)
        temp = temp + a[i];
    return temp;
}

int main()
{
    int data[100], n;
    read_array(data, n); // data and n now have values
    cout << "Sum is: " << sum(data, n);
    return 0;
}
  • One could write int a[] in place of int *a in the two function definitions. The effect is the same. While the former is more readable, the latter has been used to more clrealy indicate the semantics of array paramter passing. The const qualification of the parameters in sum is not necessary for correctness, but since both parameters are used in a read only manner they have been so marked.

  • 두 개의 함수 정의에서 int *a 대신에 int a[]를 사용할 수 있습니다. 두 방법의 효과는 동일합니다. 전자는 더 읽기 쉬우며, 후자는 배열 매개변수 전달의 의미론을 더 명확하게 나타내기 위해 사용됩니다. sum 함수에서 매개변수에 대한 const 자격 부여는 정확성에 필수적이지는 않지만, 두 매개변수가 읽기 전용 방식으로 사용되므로 const로 표시되었습니다.

5.3 Function Return Types

  • A function may return a value of any type including structured types, pointers, and references. Examples of the former two types are shown below.

  • 함수는 구조화된 형식(structured types), 포인터(pointer), 참조(reference)를 포함한 모든 형식의 값을 반환할 수 있습니다. 아래에서는 구조화된 형식과 포인터의 예제를 보여줍니다.

#include <iostream>
using namespace std;

struct student_record {
    int id;
    char classification;
    double gpa;
};

student_record read_record_method1()
// return a student_record 
{
    student_record r;
    cin >> r.id >> r.classification >> r.gpa;
    return r;
}

student_record* read_record_method2()
// return a pointer to a student_record 
{
    student_record* r = new student_record;
    cin >> r->id >> r->classification >> r->gpa;
    return r;
}

int main()
{
    student_record test1, *test2;
    test1 = read_record_method1();
    test2 = read_record_method2();
    cout << test1.id << test2->id;

    delete test2;

    return 0;
}
  • Functions that return reference types should be thought of not as returning values but aliases for storage locations. The function call becomes an alias for the storage location returned by the function. For example, the function below returns an alias for the largest of its two formal parameters.

  • 참조 형식을 반환하는 함수는 값을 반환하는 것이 아니라 저장 위치(storage location)에 대한 별칭(alias)으로 생각해야 합니다. 함수 호출은 함수가 반환한 저장 위치에 대한 별칭이 됩니다. 예를 들어, 아래 함수는 두 개의 형식 인자 중에서 가장 큰 값에 대한 별칭을 반환합니다.

#include <iostream>
using namespace std;

int& max(int& a, int& b) { // 참조 형식 반환, int& max{}
    return (a > b) ? a : b;
}

int main() {
    int q = 5, r = 10, s = 15, t = 20;
    // q: 5, r: 10, s: 15, t: 20

    ++max(q, r); // r을 증가시킴, max(q, r)는 r의 별칭(alias)이다.
    // q: 5, r: 11, s: 15, t: 20

    max(s, t) = 3; // t를 3으로 설정함, max(s, t)는 t의 별칭(alias)이다.
    // q: 5, r: 11, s: 15, t: 3

    int x = max(q, s); // 함수 호출과 같이 동작하여 x를 15로 설정함.
    // q: 5, r: 11, s: 15, t: 3, x: 15

    return 0;
}

추가 해설
참조 형식을 반환 int& max{}하면, 함수 호출은 반환된 값의 별칭Aliasing이다.
반환 값은 새 복사본이 아니라, 기본 변수, 데이터의 참조 = 저장 위치(메모리 공간)에 대한 별칭
예를 들어, 주어진 코드에서 max(q, r)는 q와 r 중에서 큰 값을 반환합니다. 이 반환 값은 r 변수에 대한 참조이므로 ++max(q, r)를 통해 r 변수를 증가시키게 됩니다.
사용하는 이유:
- 반환 값이 복사가 아니라 원본 데이터에 대한 참조로 처리되므로, 큰 데이터 구조나 객체를 반환할 때 복사 비용을 피할 수 있다.
- Function Chaining
- Builder Pattern
- Operator Overloading
- Library 설계
- 코드 가독성, 유지 보수
- 예시: 큰 데이터 집합에서 최댓값을 찾는 함수에서, 최대 값의 인덱스를 반환하고, 이 인덱스를 사용하여 데이터를 수정할 수 있다. 참조 형식을 반환하는 함수가 유용하다.

5.4 Function Prototypes

  • A function prototype specifies the name of a function and the type and number of its parameters, as illustrated below.

  • “함수 프로토타입”은 함수의 이름과 매개변수의 유형 및 개수를 지정하는 것으로, 아래와 같이 나타낼 수 있습니다.

int power(int, int);
  • Function prototypes allow a programmer to reference a function before its implementation is specified. They also allow sets of related function definitions to be grouped into “header” files as discussed in the next chapter.

  • 함수 프로토타입은 프로그래머가 해당 함수의 구현이 명시되기 전에 함수를 참조할 수 있게 합니다. 또한 다음 장에서 다루게 될 것처럼 관련된 함수 정의 세트를 ‘헤더’ 파일로 그룹화할 수 있게 합니다

추가 예시

  • header file: ‘power.h’
#ifndef POWER_H
#define POWER_H

int power(int base, int exponent); // 함수 프로토타입 선언

#endif
  • main file: ‘main.cpp’
#include <iostream>
#include "power.h" // 헤더 파일 포함

int main() {
    int base = 2;
    int exponent = 3;
    
    // power 함수 호출
    int result = power(base, exponent);

    std::cout << base << "의 " << exponent << " 제곱은 " << result << "입니다." << std::endl;

    return 0;
}
  • function implementation file: ‘power.cpp’
#include "power.h"

// 함수 정의
int power(int base, int exponent) {
    int result = 1;
    for (int i = 0; i < exponent; ++i) {
        result *= base;
    }
    return result;
}

5.5 Optional Parameters 선택적 매개변수

  • C++ allows function parameters to be given default values, essentially making them optional parameters. The technique is illustrated below.

  • C++는 함수 매개변수에 기본값을 지정할 수 있어서 이를 선택적 매개변수로 만들 수 있습니다. 이 기술은 아래에서 설명되어 있습니다.

#include <iostream>
using namespace std;

int volume(int width, int length = 2, int height = 1)
{
    return width * length * height;
}

int main()
{
    int test;
    test = volume(3, 2, 2); // test is 12
    test = volume(3, 2);    // test is 12 (default height = 1)
    test = volume(2);       // test is 4 (default length = 2, default height = 1)
    
    // ...
    
    return 0; // main 함수의 반환 값
}
  • Since no value was specified for height in the second call to volume, it was assigned a default value of 1, and in the third call to volume both height and width were assigned their default values.

  • volume” 함수의 두 번째 호출에서 높이(height)에 대한 값을 지정하지 않았으므로, 이 값은 기본값 1로 할당되었고, “volume” 함수의 세 번째 호출에서는 높이(height)와 너비(width) 모두 기본값으로 할당되었습니다.

  • Once a formal parameter is specified as having a default value all subsequent formal parameters must also be given default values. Thus the function below is not permissible.

한 번 형식 매개변수가 기본값을 가진다고 지정되면 그 이후의 모든 형식 매개변수에도 기본값을 지정해야 합니다. 따라서 아래의 함수는 허용되지 않습니다.

int volume (int width, int length = 2, int height) // NOT ALLOWED!
{
    return width * length * height;
}
  • Similarly, it is not possible to leave “holes” in either the formal or actual parameter lists. Thus, it would not be possible to call volume specifying only width and height.

  • 마찬가지로 형식 매개변수 목록 또는 실제 매개변수 목록에서 “빈 칸”을 남기는 것도 불가능합니다. 따라서 너비(width)와 높이(height)만 지정하여 “volume” 함수를 호출하는 것은 불가능합니다.

  • Default parameter values for a function can be specified only once. They cannot be specified both in a funtion’s prototype and its implementation. If function prototypes are used, it is recommended that default paramters be specified there, as illustrated below.

  • 함수의 기본 매개변수 값은 한 번만 지정할 수 있습니다. 함수의 프로토타입과 구현부에서 동시에 지정할 수 없습니다. 함수의 프로토타입을 사용하는 경우 기본 매개변수는 프로토타입에서 지정하는 것이 권장됩니다. 그것은 아래에 기술되어 있습니다.

// specify defaults in prototype
int volumen (int, int =2, int = 1); // ...

// do not specify them here
int volumen (int width, int length, int height)
{
    return width * length * height;
}

5.6 Overloaded Functions 오버로딩된 함수

  • It is possible in C++ to define several functions with identical names, provided that they differ in their return types or their formal parameter lists. This is illustrated below.

  • C++에서는 반환 유형이나 형식 매개변수 목록에서 차이가 있는 여러 함수를 정의할 수 있습니다. 이것은 아래에서 설명되어 있습니다.

struct fraction {
    int numerator, denominator;
};

// int 배열을 출력하는 함수
void write(int a[], int size)
{
    for (int i = 0; i <= size-1; ++i)
    cout << a[i] << '\n';
}

// char 배열을 출력하는 함수
void write(char a[], int size)
{
    for (int i = 0; i <= size-1; ++i)
    cout << a[i] << '\n';
}

// fraction 구조체를 출력하는 함수
void write(fraction f)
{
    cout << f.numerator << '/' << f.denominator;
}
  • The complier is able to decipher any call to write by checking the types of the actual parameters.

  • 컴파일러는 실제 매개변수의 유형을 확인하여 “write” 함수 호출을 해석할 수 있습니다.

  • When several definitions are associated with the same name, that name is said to be overloaded. Thus, the example above illustrates function overloading.

  • 동일한 이름과 여러 정의가 연관되어 있을 때, 그 이름을 “오버로드”되었다고 합니다. 따라서 위의 예제는 함수 오버로딩을 보여줍니다.

5.7 Operator Overloading 연산자 오버로딩

  • One may overload operators as well as functions. In the example below, the multiplication operator is “told” how to multiply two fraction structures.

  • 함수뿐만 아니라 연산자도 오버로딩할 수 있습니다. 아래의 예제에서는 곱셈 연산자가 두 개의 fraction 구조체를 어떻게 곱셈할지를 정의하고 있습니다.

#include <iostream>
using namespace std;

struct fraction {
    int numerator, denominator;
};

fraction operator*(fraction f1, fraction f2)
{
    fraction result;
    result.numerator = f1.numerator * f2.numerator;
    result.denominator = f1.denominator * f2.denominator;
    return result;
}

int main()
{
    fraction a = {2, 3}, b = {3, 5}, c;
    c = a * b; // c is 6/15
    cout << "c.numerator: " << c.numerator << " c.denominator: " << c.denominator << endl;
    return 0;
}
  • The first parameter in a binary operator definition is the left operand and the second is the right operand.

  • 이진 연산자 정의에서 첫 번째 매개변수는 왼쪽 피연산자이고 두 번째 매개변수는 오른쪽 피연산자입니다.

추가 해석
연산자 *를 오버로딩 할 때, 수식 c = a * b에서, 첫 번째 매개변수는 a, 두 번째 매개변수는 b라는 말임.

  • One may only overload existing C++ operators. It is not possible to create new operator symbols, and it is not possible to change the associatively and precedence of an operator.

  • 기존의 C++ 연산자만 오버로딩할 수 있습니다. 새로운 연산자 기호를 만들거나 연산자의 연관성과 우선순위를 변경하는 것은 불가능합니다.

5.8 Inline Functions

  • The run-time overhead associated with function calls makes it inefficient to call functions that contain only a few lines of code since the time spent in the function may be significantly less than the time required to pass the parameters, transfer control, return the value of the function, and so forth. Consequently, C++ allows programmers to specify that a function is to be implemented “inline”.

  • 실행 시간에 함수 호출과 관련된 오버헤드는 코드가 몇 줄밖에 없는 함수를 호출하는 것이 비효율적으로 만들 수 있습니다. 왜냐하면 함수 내에서 소요되는 시간이 매개변수를 전달하고 제어를 전환하고 함수의 반환 값을 반환하는 등의 작업에 필요한 시간보다 현저히 적을 수 있기 때문입니다. 그 결과로 C++은 프로그래머가 함수를 “인라인”으로 구현할 수 있도록 지정할 수 있게 합니다.

  • When the compiler encounters a call to a function marked as inline, it does not generate function call but instead subsitutes the body of the function in the place of the function call. An inline function definition is shown below.

  • 컴파일러가 “인라인”으로 표시된 함수 호출을 만날 때, 함수 호출을 생성하지 않고 대신 함수 호출의 위치에 함수의 본문을 대체합니다. 인라인 함수의 정의는 아래와 같이 나타낼 수 있습니다.

inline int doubleValue(int x)
{
    return 2 * x;
}

5.9 Controlling Scope and Lifetime 범위와 수명 제어

5.9.1 Scope 범위

  • Scope refers to the range of statements over which a variable can be referenced. Generally, the scope of a variable in C++ proceeds from the point at which it is declared to the end of the program unit (function or file) in which it is declared. Later, it will be shown that the scope of a variable defined within a class structure is more tightly restricted than this.

  • 범위(scope)란 변수가 참조될 수 있는 문장의 범위를 나타냅니다. 일반적으로 C++에서 변수의 범위는 해당 변수가 선언된 지점부터 선언된 프로그램 단위(함수 또는 파일)의 끝까지입니다. 나중에 클래스 구조 내에서 정의된 변수의 범위는 이것보다 더 엄격하게 제한됨을 보여줄 것입니다.

  • The scope resolution operator “::” can be used to reference variables which would otherwise not be accessible. This is illustrated below.

  • 범위 해결 연산자 ::는 그렇지 않으면 접근할 수 없는 변수를 참조하는 데 사용할 수 있습니다. 이것은 아래에서 설명되어 있습니다.

int i; // variable is accessable from this point onward
// ...
float fn()
{
    int i; // References to i within fn refer to this variable. The previously declared i is now "blocked" and can only be accessed by using the :: operator.
    // ...
    cout << i; // Write out value of local i
    cout << ::i; // Write out value of externally declared i.
}

추가 해설
std::cout에서 :: 연산자를 사용하여 cout을 std 네임스페이스에서 가져올 수 있다.

std::cout << "Hello World" << std::endl;

추가 해설
using namespace std를 통해 std 네임스페이스 내의 요소를 현재 범위(scope)에서 사용할 수 있게 된다

using namespace std;
cout << "Hello World" << endl;

5.9.2 Lifetime

  • The lifetime of a variable refers to the time during which storage for the variable has been allocated. Unless otherwise stated, storage for a variable is allocated when that variable’s declaration statement is elaborated and deallocated when the unit in which it is declared terminates. Thus, variables declared within functions are newly created every time the function is activated and destroyed when the function terminates. This is not always desirable, however, and can be overridden in C++ by declaring a variable to be static.

  • 변수의 수명(lifetime)은 해당 변수의 저장 공간이 할당된 시간을 나타냅니다. 특별히 언급되지 않는 한, 변수의 저장 공간은 해당 변수의 선언문이 실행될 때 할당되며, 선언이 있는 단위가 종료될 때 해제됩니다. 따라서 함수 내에서 선언된 변수는 함수가 활성화될 때마다 새로 생성되고 함수가 종료될 때 파괴됩니다. 그러나 이는 항상 원하는 동작이 아닐 수 있으며, C++에서는 변수를 정적(static)으로 선언함으로써 이를 무시할 수 있습니다.

  • Storage for a static variable is allocated the first time its declaration statement is encountered and is not deallocated until the entire C++ program terminates. For example, the function below writes 1 the first time it is called, 2 the second time, 3 the third time, and so forth. If a were not declared static, it would print 1 each time it was called.

  • 정적(static) 변수의 저장 공간은 해당 선언문이 처음으로 만날 때 할당되며, C++ 프로그램 전체가 종료될 때까지 해제되지 않습니다. 예를 들어, 아래의 함수는 처음 호출될 때 1을 출력하고, 두 번째 호출 때 2를 출력하며, 세 번째 호출 때 3을 출력하고 이어갑니다. 만약 변수 a가 정적(static)으로 선언되지 않았다면, 매번 호출될 때마다 1을 출력했을 것입니다.

void fn()
{
    static int a = 0; // 한번만 초기화된다.
    a = a + 1;
    cout << a;
}