devseop08 님의 블로그

[Basic] 함수와 스코프 본문

Language/C++

[Basic] 함수와 스코프

devseop08 2025. 7. 16. 21:57

1. 스코프

블록

  • 복합 명령문이라고도 불리는 블록은 마치 한 명령문같이 보이는 명령문의 그룹이다.
  • 블록은 { 기호로 시작하고, } 기호로 끝나며, 기호 사이에 실행할 명령문을 배치한다.
  • 블록은 단일 명령문이 허용되는 모든 위치에서 사용할 수 있고, 블록 끝에는 세미콜론(;)이 필요하지 않다.
int add(int x, int y)
{ // 시작 블록 (start a block)
    return x + y;
} // 끝 블록 (end a block)

int main()
{ // start a block

    // 여러 명령문 (multiple statements)
    int value(0);
    add(3, 4);

    return 0;

} // end a block (no semicolon)
  • 블록은 다른 블록 내부에 중첩될 수 있다.
#include <iostream>

int main()
{
    std::cout << "Enter an integer: ";
    int value;
    std::cin >> value;

    if (value >= 0)
    { // start of nested block
        std::cout << value << " is a positive integer (or zero)" << std::endl;
        std::cout << "Double this number is " << value * 2 << std::endl;
    } // end of nested block
    else
    { // start of another nested block
        std::cout << value << " is a negative integer" << std::endl;
        std::cout << "The positive of this number is " << -value << std::endl;
    } // end of another nested block

    return 0;
}
  • 중첩된 블록(nested block) 수에는 제한이 없다.
int main()
{
    std::cout << "Enter an integer: ";
    int value;
    std::cin >> value;

    if (value >  0)
    {
        if ((value % 2) == 0)
        {
            std::cout << value << " is positive and even" << std::endl;
        }
        else
        {
            std::cout << value << " is positive and odd" << std::endl;
        }
    }

    return 0;
}

지역변수 스코프와 주기

  • 변수의 스코프(scope)에 따라 변수에 접근할 수 있는 위치가 결정된다.
  • 변수의 주기(duration)는 변수가 생성되고 소멸하는 위치를 결정한다.
  • 이 두 개념은 밀접하게 관련돼있다.
  • 함수 내부에 정의된 변수를 지역 변수라고 한다.
    • 지역 변수는 자동 주기(auto duration)을 가지고 있다.
    • 정의되는 시점에 생성되고 초기화되며, 정의된 블록이 끝나는 지점에서 소멸한다.
    • 지역 변수를 지역 스코프(블록 스코프)를 갖는다.
    • 지역 변수의 지역 스코프(블록 스코프) 내에서만 해당 지역 변수에 접근할 수 없다.
int main()
{
    int i(5);      // i created and initialized here
    double d(4.0); // d created and initialized here

    return 0;

} // i and d go out of scope and are destroyed here
  • 변수 i와 d는 main() 함수 내부에서 정의되었기 때문에 main() 함수의 실행이 완료되면 모두 소멸한다.
  • 중첩된 블록(nested block) 내부에 정의된 변수는 중첩된 블록이 끝나는 즉시 소멸한다.
int main() // outer block
{
    int n(5); // n created and initialized here

    { // begin nested block
        double d(4.0); // d created and initialized here
    } // d goes out of scope and is destroyed here

    // d can not be used here because it was already destroyed!

    return 0;
} // n goes out of scope and is destroyed here
  • 블록 내부에 정의된 변수는 해당 블록에서만 접근할 수 있다. 각 함수는 자체 블록을 가지고 있으므로 한 함수의 변수를 다른 함수에서 볼 수 없다.
void someFunction()
{
    int value(4); // value defined here

    // value can be seen and used here

} // value goes out of scope and is destroyed here

int main()
{
    // value can not be seen or used inside this function.

    someFunction();

    // value still can not be seen or used inside this function.

    return 0;
}
  • 중첩된 블록은 외부 블록의 일부로 간주한다. 따라서 외부 블록에서 정의된 변수를 중첩된 블록 내부에서 접근할 수 있다.
#include <iostream>

int main()
{ // start outer block

    int x(5);

    { // start nested block
        int y(7);
        // we can see both x and y from here
        std::cout << x << " + " << y << " = " << x + y;
    } // y destroyed here

    // y can not be used here because it was already destroyed!

    return 0;
} // x is destroyed here
쉐도잉
  • 중첩된 블록 내부의 변수는 외부 블록과 같은 이름을 가질 수 있다. 이 경우 외부 블록의 같은 이름의 변수는 잠시 가려지는데 이것을 쉐도잉(shadowing) 또는 숨기기(hide)라고 부른다.
#include <iostream>

int main()
{ // outer block
    int apples(5); // here's the outer block apples

    if (apples >= 5) // refers to outer block apples
    { // nested block
        int apples; // hides previous variable named apples

        // apples now refers to the nested block apples
        // the outer block apples is temporarily hidden

        apples = 10; // this assigns value 10 to nested block apples, not outer 
        block apples

        std::cout << apples << '\n'; // print value of nested block apples
    } // nested block apples destroyed

    // apples now refers to the outer block apples again

    std::cout << apples << '\n'; // prints value of outer block apples

    return 0;
} // outer block apples destroyed

If you run this program, it prints:

10
5
  • 위 프로그램에서 중첩된 블록 내부의 변수 apple은 외부 블록 apple을 숨긴다. 중첩된 블록이 끝나면 중첩된 블록 내부 변수 apple 은 소멸하고 다시 외부 블록 apple에 접근할 수 있게 된다.
  • 만약 중첩된 블록 내부에서 변수 apple을 정의하지 않으면 외부 블록 변수 apple에 접근하여 값 10을 할당한다.
#include <iostream>

int main()
{ // outer block
    int apples(5); // here's the outer block apples

    if (apples >= 5) // refers to outer block apples
    { // nested block
        // no inner block apples defined

        apples = 10; // this now applies to outer block apples, even though we're 
                     // in an inner block

        std::cout << apples << '\n'; // print value of outer block apples
    } // outer block apples retains its value even after we leave the nested block

    std::cout << apples << '\n'; // prints value of outer block apples

    return 0;
} // outer block apples destroyed

If you run this program, it prints:

10
10
  • 변수는 가능한 한 제한된 스코프(=범위) 안에서 정의되는게 좋다. 예를 들어, 변수가 중첩된 블록 내에서만 사용되는 경우, 해당 중첩된 블록 내에서 정의되도록 하자.
함수 매개 변수
  • 함수 매개 변수는 함수가 속하는 블록 내에서 정의되지는 않았지만 대부분 블록 스코프를 가진 것으로 간주할 수 있다.
int max(int x, int y) // 변수 x와 y는 함수 매개 변수에서 정의되었다.
{
    // 매개변수는 블록 스코프를 가진것으로 간주되므로 블록 내부에서 접근가능하다.
    // x와 y중 더 큰 값을 변수 max에 할당한다.
    int max = (x > y) ? x : y;
    return max;
} // 변수 x와 y는 여기서 소멸된다.

전역 변수와 링크

  • 함수 내부에서 정의된 변수를 지역 변수라고 한다. 지역 변수는 블록 스코프(정의된 블록 내에서만 접근 가능)를 갖고 자동 주기(정의 시점에서 생성, 블록이 종료되면 소멸)를 갖는다.
  • 함수 외부에서 선언된 변수를 전역 변수라고 한다. 전역 변수는 정적 주기(static duration)로, 프로그램이 시작할 때 생성되고 프로그램이 종료될 때 파괴된다.
  • 전역 변수는 파일 스코프(전역 스코프)를 갖는다.
  • 전역 변수는 정의된 시점부터 소스 파일의 끝까지 접근 가능하다는 것이다.
  • 전역 변수 정의
#include <iostream>

// 함수 외부에서 정의된 변수는 전역 변수(global variable)다.
int g_x;          // global variable g_x
const int g_y(2); // global variable g_y

void doSomething()
{
    // 전역 변수가 정의된 이후, 프로그램 어디에서든지 접근 가능하다.
    std::cout << g_y << "\n";
}

int main()
{
    doSomething();

    // 전역 변수가 정의된 이후, 프로그램 어디에서든지 접근 가능하다.
    g_x = 5;
    std::cout << g_y << "\n";

    return 0;
}
  • 중첩된 블록(nested block)이 이름이 같은 외부 블록의 변수를 숨기는 것처럼, 전역 변수와 같은 이름을 가진 지역 변수는 전역 변수를 숨긴다.
  • 그러나 전역 범위 연산자(: :)를 사용하면 컴파일러는 지역 변수 대신 전역 변수를 사용한다.
#include <iostream>
int value(5); // 전역 변수

int main()
{
    int value = 7; // 전역 변수를 숨긴다. (shadowing or hide)
    value++;   // 지역 변수를 증가시킨다.
    ::value--; // 전역 변수를 감소시킨다.

    std::cout << "global value: " << ::value << "\n";
    std::cout << "local value: " << value << "\n";
    return 0;
} // 지역 변수 value는 소멸된다.

This code prints:

global value: 4
local value: 8
  • 그러나 전역 변수와 같은 지역 변수를 정의하는 건 피해야 한다. 관습에 따라 일반적으로 전역 변수를 정의할 때는 g_ 접두사를 붙인다.
  • 이 방법은 전역 변수를 식별하는데 편리할 뿐만 아니라 지역 변수와 충돌을 방지하는 데 도움이 된다.
static과 extern 키워드를 이용한 내부/외부 링크
  • 변수는 스코프와 주기 외에도 링크라는 속성을 갖는다.
  • 링크는 같은 이름의 여러 식별자가 같은 식별자를 참조하는지를 결정한다.
// Uninitialized definition:
int g_x;        // defines uninitialized global variable (external linkage)
static int g_x; // defines uninitialized static variable (internal linkage)
const int g_x;  // not allowed: const variables must be initialized

// Forward declaration via extern keyword:
extern int g_z;       // forward declaration for global variable defined elsewhere
extern const int g_z; // forward declaration for const global variable defined elsewhere

// Initialized definition:
int g_y(1);        // defines initialized global variable (external linkage)
static int g_y(1); // defines initialized static variable (internal linkage)
const int g_y(1);  // defines initialized static variable (internal linkage)

// Initialized definition w/extern keyword:
extern int g_w(1);       // defines initialized global variable (external linkage, extern keyword is redundant in this case)
extern const int g_w(1); // defines initialized const global variable (external linkage)
  • 링크가 없는 변수는 정의된 제한된 범위에서만 참조할 수 있다.
  • 지역 변수가 링크가 없는 변수의 예이다.
  • 이름은 같지만 다른 함수(블록)에서 정의된 지역 변수는 링크가 없다.
  • 각 지역 변수는 독립적이다.
  • 내부 링크가 있는 변수를 static 변수라고 한다.
  • static 변수는 변수가 정의된 소스 파일 내에서 어디서나 접근할 수 있지만, 소스 파일 외부에선 참조할 수 없다.
static int g_x; // g_x is static, and can only be used within this file

int main()
{
    return 0;
}
  • 외부 링크가 있는 변수를 extern 변수라고 한다. extern 변수는 정의된 소스 파일과 다른 소스 파일 모두에서 접근할 수 있다.
extern double g_y(9.8); // g_y is external, and can be used by other files

// Note: those other files will need to use a `forward declaration` to access this // external variable
// We'll discuss this in the next section

int main()
{
    return 0;
}
  • 기본적으로 전역 변수는 extern 변수로 간주한다. 그러나 상수(const) 전역 변수는 static 변수로 간주한다.
extern 키워드를 통한 변수 전방 선언
  • 다른 소스 파일에서 선언된 외부 전역 변수를 사용하려면 '변수 전방 선언(variable forward declarations)'을 해야 한다.
  • extern 키워드는 두 가지 다른 의미가 있다. 어떤 상황에서는 extern 키워드가 외부 링크가 있는 변수를 의미하고 다른 상황에서는 다른 어딘가에서 정의된 변수에 대한 전방 선언을 의미한다.
// global.cpp 

// 두 개의 전역 변수를 정의한다.
// non-const globals have external linkage by default
int g_x;           // external linkage by default
extern int g_y(2); // external linkage by default, so this extern is redundant and 
                   // ignored

// in this file, g_x and g_y can be used anywhere beyond this point
  • 만약 변수 전방 선언이 함수 외부에서 선언되면 소스 파일 전체에 적용된다. 함수 내에서 선언되면 해당 블록 내에서만 적용된다.
// main.cpp
#include <iostream>
#include "global.cpp"

extern int g_x; // forward declaration for g_x (defined in global.cpp) -- g_x can 
                // now be used beyond this point in this file
int main()
{
    extern int g_y; // forward declaration for g_y (defined in global.cpp) -- g_y 
                    // can be used beyond this point in main() only
    g_x = 5;
    std::cout << g_y; // should print 2

    return 0;
}
  • 변수가 static으로 선언된 경우, 이에 접근하기 위해 변수 전방 선언을 해도 적용되지 않는다.
//constant.cpp
static const double g_gravity(9.8);
// main.cpp
#include <iostream>
#include "constant.cpp"

extern const double g_gravity; // This will satisfy the compiler that g_gravity 
                               // exists

int main()
{
    std:: cout << g_gravity; // This will cause a linker error because the only 
                             // definition of g_gravity is inaccessible from here
    return 0;
}
함수 링크
  • 함수는 변수와 같은 링크 속성을 갖는다.
  • 함수는 항상 외부 링크로 기본 설정(extern이 default)되지만 static 키워드를 통해 내부 링크로 설정할 수도 있다.
// This function is declared as static, and can now be used only within this file
// Attempts to access it via a function prototype will fail
static int add(int x, int y)
{
    return x + y;
}
  • 함수 전방 선언에는 extern 키워드가 필요하지 않다. 컴파일러는 함수 몸체인지 원형인지 알아서 판단한다.
전역 변수가 나쁜 이유
  • non-const 전역 변수가 나쁜 이유: 호출되는 함수에 의해서 값이 예상치 못한 값으로 변경될 수 있기 때문이다.
// 전역 변수 선언
int g_mode;

void doSomething()
{
    g_mode = 2; // 전역 변수 g_mode의 값을 2로 설정한다.
}

int main()
{
    g_mode = 1; // 지역 변수 g_mode가 없기 때문에 전역 변수 g_mode의 값을 1로 설정한다.

    doSomething();

    // 프로그래머는 전역 변수 g_mode의 값이 1이라고 생각할 수 있다.
    // 그러나 doSomething() 함수가 g_mode 값을 2로 바꾸었다.

    if (g_mode == 1)
        std::cout << "No threat detected.\n";
    else
        std::cout << "Launching nuclear missiles...\n";

    return 0;
}

정적 변수(static)

  • static 키워드는 전역 변수 뿐만 아니라 블록 내에 선언된 지역 변수에서도 사용할 수 있다.
  • 지역 변수는 '자동 주기(auto duration)'를 가지며, 정의되는 시점에서 생성되고 초기화되며, 정의된 블록이 끝나는 지점에서 소멸한다.
  • static 키워드를 사용한 지역 변수는 전혀 다른 의미를 갖는다.
  • static 키워드를 사용한 지역 변수는 '자동 주기'에서 '정적 주기'를 갖는다.
  • 이것을 정적 변수(static variable)라고도 부르는데, 생성된 스코프(=범위)가 종료한 이후에도 해당 값을 유지하는 변수다.
  • 스코프가 종료된 이후에도 값은 유지되지만, 그렇다고 해서 스코프 밖에서 접근 가능한 것은 아니다. 왜냐하면 정적 변수도 결국엔 지역 변수이기 때문이다.
  • 또한, 정적 변수는 한 번만 초기화되며 프로그램 수명 내내 지속된다.
  • 지역 변수와 정적 변수의 차이 예제
#include <iostream>

void incrementAndPrint()
{
    int value = 1; // 기본적으로 '자동 생명 주기'
    ++value;
    std::cout << value << '\n';
} // value는 여기서 소멸된다.

int main()
{
    incrementAndPrint();
    incrementAndPrint();
    incrementAndPrint();
}

program outputs:

2
2
2
#include <iostream>

void incrementAndPrint()
{
    static int s_value = 1; // 'static' 키워드를 사용한 '정적 생명 주기', 이 줄은 한번
                            //  만 실행된다.
    ++s_value;
    std::cout << s_value << '\n';
} // s_value가 여기서 소멸되지는 않지만 이 스코프 밖에서 
  // s_value에 접근할 수 있는 것은 아니다.

int main()
{
    incrementAndPrint();
    incrementAndPrint();
    incrementAndPrint();
}

program outputs:

2
3
4

스코프, 주기, 링크 요약

변수의 스코프, 주기, 링크

변수와 함수 전방 선언

2. 함수

함수의 선언과 정의

함수의 형식
ReturnType functionName(fParameterList) // 머리부(header)
{   // 몸체 블록(body block)
    Type1 localVar1; // 지역변수 선언
    Type2 localVar2; 
    ...
    statement1;      // 처리할 작업을 수행하는 문장
    statement2;     
    ...
    return returnExpression; // 결괏값을 반환
}
  • fParameterList는 함수를 호출하는 루틴이 전달하는 값(인수)들을 받을 매개변수들의 자료형과 이름을 나열해놓은 것으로, 이것을 형식 매개변수라고 한다.
  • 함수 안에 선언한 변수를 지역변수라고 한다.
  • 형식 매개변수와 함수 안에 지역변수는 그 함수 내에서만 사용할 수 있다.
  • 지역변수는 함수의 첫머리에 선언하는 것이 일반적이지만 C++에서는 사용되는 곳의 앞이면 함수의 어느 위치에서든 선언하여도 된다.
  • 함수 바깥에 선언한 변수는 프로그램의 모든 영역에서 사용할 수 있다. => 전역변수
  • 반환할 값의 자료형을 함수의 첫머리에 지정하여야 한다.
  • returnExpression은 ReturnType으로 암사적 형 변환이 가능한 자료형의 수식을 사용해야 한다.
  • return 명령은 함수의 처리를 마치려고 할 때 언제든 사용할 수 있으며, 한 함수 내에서 여러 개의 return 명령을 사용할 수 있다.
  • 값을 반환할 필요가 없는 함수를 만들 때는 ReturnType을 void형으로 지정, 함수의 마지막에 return 문을 써주지 않아도 되며, 필요하다면 return 명령으로 언제든 함수를 종료하고 복귀할 수 있다. 이런 경우 return 명령은 returnExpression 없이 사용된다.
함수의 위치
  • 함수는 호출되는 영역보다 먼저 앞의 위치에서 선언 및 정의돼 있어야 한다.
#include <iostream>

int add(int x, int y)
{
    return x + y;
}

int main()
{
    std::cout << "The sum of 3 and 4 is: " << add(3, 4) << std::endl;
    return 0;
}
함수의 전방 선언
  • 전방 선언(forward declaration)은 실제로 식별자를 정의하기 전제 식별자의 존재를 컴파일러에 미리 알리는 것이다.
  • 함수의 경우, 함수의 몸체를 정의하기 전에 함수의 존재에 대해 컴파일러에 미리 알리는 것을 말한다.
  • 이 방법으로 컴파일러가 함수를 호출할 때 함수가 정의된 방법이나 위치를 아직 모를 때에도 함수를 호출한다는 것을 이해시킬 수 있다.
  • 함수의 전방 선언을 하려면 함수 원형(prototype)이라고 하는 선언문(declaration statement)을 사용해야 한다.
  • 함수가 전방 선언되면 함수 몸체의 정의는 해당 함수가 호출되는 영역 이후에 위치할 수 있게 된다.
  • 함수 원형은 리턴 타입, 이름 그리고 매개 변수로 구성되지만, 함수 몸체({} 사이의 부분)는 포함하지 않는다. 함수 원형은 명령문(statement)이므로 세미콜론(;)으로 끝나야 한다.
  • 함수 원형 형식
ReturnType functionName(fParameterList);
  • ,함수 원형(선언문)을 이용한 함수 전방 선언
#include <iostream>

int add(int x, int y); // add() 함수의 전방 선언(함수 원형)

int main()
{
    std::cout << "The sum of 3 and 4 is: " << add(3, 4) << std::endl;
    return 0;
}

int add(int x, int y)
{
    return x + y;
}

// The sum of 3 and 4 is: 7
  • 함수 원형에서 매개 변수의 이름을 지정하지 않고 선언할 수 있다.
int add(int, int); // 매개변수의 이름을 생략 가능하다.
선언 vs 정의
  • 선언(declaration)은 식별자(변수 또는 함수 이름) 및 해당 타입의 존재를 컴파일러에 알려주는 명령문(statement)이다.
// 컴파일러에게 두 개의 int 매개 변수를 사용하고 
// int를 반환하는 "add"라는 이름의 함수를 알려준다.
int add(int x, int y);

int x; // x라는 정수형 변수를 컴파일러에게 알려준다.
  • 선언하지 않고 식별자를 사용하면 컾파일러에서 오류가 발생한다.
  • 정의는 식별자를 실제로 구현하거나, 인스턴스화 한다.
  • 링커를 만족하기 위해서는 정의가 필요하다.
int add(int x, int y) // add() 함수를 구현한다.
{
    return x + y;
}

int x = 10; // x라는 정수형 변수를 인스턴스화(메모리 할당)

함수의 호출

1. functionName(aParameterList);
2. varName = funtionName(aParameterList);

함수 사용의 장점과 단점

  • 장점
      1. 의미있는 작업 단위로 모듈화하여 프로그램을 분할 작성함으로써, 간결하고 이해하기 쉬운 프로그램을 만들 수 있다.
      1. 여러 곳에서 반복 사용되는 기능을 함수로 정의하면, 동일 코드를 중복 작성하지 않아도 된다.
      1. 잘 설계하면 만든 함수는 그 기능이 필요한 여러 가지 다른 응용에서 재사용하기 좋다
  • 단점
    • 함수를 사용하면 함수 호출과 복귀 과정에서 처리 시간이 추가된다.(inline 함수로 보완 가능)

인수의 전달

값 호출
  • 값 호출은 실 매개변수의 값을 형식 매개변수에 복사하여 주는 방식이다.
  • 함수 내에서 형식 매개변수의 값을 변경하더라도 실 매개변수의 값은 영향받지 않는다.
참조 호출
  • 값 호출과 달리 참조 호출은 실 매개변수의 참조, 즉 주소를 전달한다.
  • 이에 따라 형식 매개 변수는 전달되는 실 매개변수의 별명처럼 사용된다.
  • C++ 언어에서는 포인터 대신 참조형을 사용할 수 있다.
#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);
    cout << "큰 수 = " << a << " 작은 수 = " << b << endl;
    return 0; 
}

// 함수 정의
void SwapValues(int &x, int &y) {
    int temp = x;
    x = y;
    y = temp;
}
두 수를 입력하시요 : 50 100
큰 수 = 100 작은 수 = 50
  • 참조변수는 포인터와 같이 다른 대상을 참조하는 것이므로 자료형이 일치해야 한다.
const 매개변수
  • 참조 호출을 하거나 매개변수가 포인터인 경우 함수 내에서 호출 루틴의 실 매개변수를 수정할 수 있다.
  • 그러나 이러한 방식의 호출을 했다고 해도 실 매개변수의 값을 보호하고 싶을 때에는 해당 형식의 매개 변수를 const로 지정하면 된다.
  • const로 지정한 매개변수는 데이터의 크기가 큰 인수를 전달할 때 유용하게 사용할 수 있다.
  • 만약 크기가 큰 인수를 값 호출 방식으로 전달하면 이를 복사하기 위해 많은 시간이 소비되며 메모리도 많이 소비된다.
  • 이런 경우에는 참조 호출을 이용하는 것이 바람직하다.
  • 그러나 참조 호출을 하면 호출한 루틴에서 전달한 인수가 함수를 실행한 후 원래의 값을 유지하고 있다는 것을 보장받지 못한다.
  • 이 때 실 매개변수의 값이 변경되지 않을 보장하기 위해 함수의 매개변수에 const를 지정할 수 있다.
#include <iostream>
using namespace std;

struct SalesRec {
    char pID[10];
    int dYear, dMonth, dDate;
    char deliverAddr[40];
};

void PrSalesRec(const SalesRec &srec);

int main()
{
    SalesRec s = {"C25TV001", 2019, 11, 15, "서울시 종로구 대학로 86"};
}

void PrSalesRec(const SalesRec &srec) {
    cout << "품목코드: " << srec.pID << endl;
    cout << "배달일자: " << srec.dYear << "년 ";
    cout << srec.dMonth << "월 ";
    cout << srec.dDate << "일 " << endl;
    cout << "배달주소 : " << srec.deliverAddr << endl;
}
  • 반대로 const가 지정된 변수를 const가 지정되지 않은 형식 매개변수에 참조 호출로 전달하는 것은 허용되지 않는다.
  • const_cast<>()는 const 한정어를 해제함으로써 const 객체를 const가 아닌 참조형 매개변수에 인수로 전달할 수 있게 한다.
void RtnA(int x);
void RtnB(int &x);

const int a = 10;

RtnA(a); // OK
RtnB(a);  // Error - const 변수를 const가 아닌 참조 인수에 전달
RtnB(const_cast<int&>(a)); // OK - const 한정어를 해제
디폴트 인수
  • 함수의 인수 중 특정 인수는 일반적으로 사용하는 디폴트 값이 있고, 특별한 경우에만 다른 값을 사용하는 경우가 있다. 이런 경우 디폴트 인수를 사용할 수 있다.
#include <iostream>
#include <cmath>

using namespace std;
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
}
  • 디폴트 인수의 위치
    • 디폴트 인수는 반드시 인수 리스트의 끝에 있어야 한다.
    • 2개 이상의 디폴트 인수가 있을 때, 뒤의 인수는 디폴트 값이 아닌 다른 값을 전달하고 싶다면
    • 앞의 인수는 디폴트 값을 사용한다 할지라도 값을 명시적으로 지정해줘야 한다.
void f(int x, int y = 10, int z = 20);
void g(int x, int y = 10, int z); // Error - 디폴트 인수는 반드시 인수 리스트의 끝에

f(5);
f(5, 100);
f(5, 100, 200);
f(5, , 300); // Error
f(5, 10, 300); // OK

함수의 다중 정의

  • 다중정의(overloading)란 동일한 이름에 대하여 여러가지 의미를 부여하는 것이다.
  • 동일한 이름을 갖는 함수를 여러 개 정의할 수 있다.
  • 함수를 다중정의하는 것은 동일한 개념의 처리를 다양한 데이터나 객체에 대해 해야 할 경우 쓰인다.
  • 함수를 다중정의한 경우, 사용되는 함수가 같은 이름을 가지고 있는 여러 함수 중에서 어느 함수를 의미하는가를 구분할 수 있어야 한다.
  • 구분의 기준은 인수의 개수 및 인수의 자료형이다.
함수의 다중 정의는 명확하게 구분하여 호출할 수 있게 해야 한다.
int g(int a)
{...}
int g(int a, int b = 100)
{...}

g(10) // 어떤 함수 g를 호출하는 것인지 불분명

int h(int a)
{...}
int h(float a)
{...}

h(10.0) // 어떤 함수 h를 호출하는 것인지 불분명

inline 함수

  • 함수를 호출하는 과정에는 인수를 전달하고, 함수의 위치로 분기하며, 결과를 반환하고, 호출한 위치로 돌아오는 과정이 수반된다.
  • 이에 따른 처리시간 및 코드의 증가는 비록 미미한 것이지만, 때로는 매우 빠른 처리가 필요하여 불필요한 시간 지연을 피하고 싶을 때가 있다.
  • 특히 모듈의 크기가 매우 작아 모듈을 실행하는 시간이나 모듈 크기가 함수 호출에 따른 부수적인 시간 및 코드의 양과 큰 차이가 없고, 이 모듈을 매우 빈번히 호출한다면 함수를 사용하지 않는 것이 더 바람직할 수 있다.
  • inline 함수는 함수가 가지는 모듈화의 장점을 살리면서 이러한 불필요한 실행 효율 저하를 막기위해 사용할 수 있다.
  • inline 함수를 작성하는 것은 inline 키워드를 사용하는 것외에는 일반 함수와 동일하다.
  • 그러나 컴파일러가 번역할 때에는 일반 함수와는 달리 함수의 처리 문장이 호출되는 위치에 직접 삽입된다.
  • 이렇게 번역하면 함수 호출 명령이 생략되어 호출에 따른 부수적 동작이 생략된다.\
  • inline 함수를 사용하면 원시 프로그램은 함수를 사용한 간결하고 이해하기 쉬운 형태를 그대로 유지하면서 함수 호출에 따른 실행 효율 저하는 발생하지 않는다.
#include <iostream>
using namespace std;

inline void SwapValues(int &x, int &y) {
    int temp = x;
    x = y;
    y = temp;
}

int main() {
    int a, b;

    cout << "두 수를 입력하시오 : ";
    cin >> a >> b;
    if(a < b) SwapValues(a, b);
    cout << "큰 수 = " << a << " 작은 수 = " << b << endl;
    return 0; 
}
  • inline으로 함수를 선언하더라도 그 함수가 반드시 inline으로 번역된다는 것은 아니다.
  • 컴파일러가 inline을 무시하고 일반 함수와 같이 번역하는 경우가 있다.
    • 함수가 너무 큰 경우
    • 순환 호출을 하는 경우(어떤 함수가 직간접적으로 자기 자신을 호출하는 경우)
    • 프로그램 내에서 inline으로 선언한 함수에 대한 포인터를 사용하는 경우

'Language > C++' 카테고리의 다른 글

[OOP] 연산자 다중정의  (5) 2025.07.21
[OOP] 상속과 다형성  (1) 2025.07.16
[Basic] 연산자와 흐름 제어 구문  (6) 2025.07.16
[Basic] 구조체와 열거형  (1) 2025.07.15
[OOP] 상속과 다형성  (0) 2025.07.15