inline 함수 지정자

inline 함수 지정자 (inline function specifier)

함수 호출에는 스택에 인자를 넣고 결과를 되돌려받는 부담이 있어요. 호출이 아주 잦은 작은 함수라면 이 부담이 쌓일 수 있는데, inline 지정자는 컴파일러가 이런 호출을 최적화하도록 힌트를 주는 기능이에요.

출처: cppreference

본문

문법

inline function_declaration (C99부터)

동작 방식

inline 지정자의 의도는 컴파일러가 함수 인라인(function inlining) 같은 최적화를 수행하도록 힌트를 주는 거예요. 이런 최적화는 보통 함수의 정의가 호출 지점에서 보여야 하죠. 컴파일러는 최적화 목적으로 inline 지정자의 유무를 무시할 수도 있고, 보통은 실제로 무시해요.

컴파일러가 함수 인라인을 수행하면, 그 함수 호출을 함수 본문으로 치환해서 함수 호출의 오버헤드(스택에 데이터를 넣고 결과를 가져오는 일)를 피해요. 다만 함수 코드가 여러 번 반복되므로 실행 파일이 더 커질 수 있어요. 결과는 함수형 매크로와 비슷하지만, 함수에서 사용된 식별자·매크로는 호출 지점이 아니라 정의 지점에서 보이는 정의를 참조한다는 차이가 있어요.

인라인이 실제로 일어나든 아니든, inline 함수의 다음 의미는 보장돼요:

  • 내부 연결(internal linkage)을 가진 함수는 다른 제약 없이 static inline으로 선언할 수 있어요.
  • 비정적(non-static) inline 함수는 비-const 함수 지역 static을 정의할 수 없고, 파일 스코프 static을 참조할 수도 없어요.
static int x;

inline void f(void)
{
    static int n = 1; // error: non-const static in a non-static inline function
    int k = x; // error: non-static inline function accesses a static variable
}

비정적 함수가 inline으로 선언되면 같은 번역 단위 안에서 정의돼야 해요. extern을 쓰지 않는 inline 정의는 외부에서 보이지 않고, 다른 번역 단위가 같은 함수를 정의하는 걸 막지도 않아요. 이 덕분에 inline 키워드는 같은 프로그램의 여러 번역 단위에 포함될 수 있는 헤더 파일 안에서 함수를 정의할 때 static의 대안이 돼요.

한 함수가 어떤 번역 단위에서 inline으로 선언됐다고 해서 모든 곳에서 inline으로 선언돼야 하는 건 아니에요. 최대 한 개의 번역 단위만 일반(비-inline 비정적) 함수나 extern inline으로 선언된 함수를 제공할 수 있는데, 이 번역 단위가 **외부 정의(external definition)**를 제공한다고 해요. 외부 연결을 가진 함수의 이름이 표현식에서 사용되면 프로그램에 외부 정의가 하나 반드시 존재해야 해요. 그렇지 않으면 정의되지 않은 동작이 되니까요(one definition rule 참고).

외부 연결을 가진 inline 함수의 주소는 항상 외부 정의의 주소예요. 하지만 이 주소로 함수 호출을 만들 때, (그 번역 단위에 있으면) inline 정의가 호출될지 외부 정의가 호출될지는 **불특정(unspecified)**이에요. inline 정의 안에 정의된 static 객체는 외부 정의 안에 정의된 static 객체와 서로 구별돼요:

inline const char *saddr(void) // the inline definition for use in this file
{
    static const char name[] = "saddr";
    return name;
}

int compare_name(void)
{
    return saddr() == saddr(); // unspecified behavior, one call could be external
}

extern const char *saddr(void); // an external definition is generated, too

C 프로그램은 함수의 inline 버전이 호출될지 외부 버전이 호출될지에 의존하면 안 돼요. 그렇지 않으면 동작이 불특정이 돼요.

키워드

inline

참고

inline 키워드는 C++에서 가져온 거예요. 하지만 C++에서는 함수가 inline으로 선언되면 모든 번역 단위에서 inline으로 선언해야 하고, inline 함수의 모든 정의가 정확히 같아야 해요(C에서는 정의가 달라도 되고, 차이에 따라 불특정 동작이 될 뿐). 반면 C++은 비-const 함수 지역 static을 허용하고, inline 함수의 서로 다른 정의에서 나온 함수 지역 static이 C++에서는 같지만 C에서는 서로 달라요.

예제

헤더 파일에 inline 정의를 두고, 한 소스 파일에서 extern inline으로 외부 정의를 제공하는 전형적인 구조를 확인해 볼게요.

헤더 파일 "test.h"
```c
#ifndef TEST_H_INCLUDED
#define TEST_H_INCLUDED
inline int sum(int a, int b)
{
return a + b;

} #endif

| 소스 파일 "sum.c" |
| ```c
#include "test.h"
extern inline int sum(int a, int b); // provides external definition
``` |
| 소스 파일 "test1.c" |
| ```c
#include <stdio.h>
#include "test.h"
extern int f(void);
int main(void)
{
    printf("%d\n", sum(1, 2) + f());
}
``` |
| 소스 파일 "test2.c" |
| ```c
#include "test.h"
int f(void)
{
    return sum(3, 4);
}
``` |
| 출력 |
| ```
10
``` |

## 더 알아보기

- `inline`으로 헤더에 함수를 정의하는 것과 대비되는, `static`으로 헤더에 함수를 정의하는 방식도 있어요. 차이를 보려면 [`extern`](https://en.cppreference.com/w/c/language/extern) 문서를 참고해요.
- 함수형 매크로는 `inline`과 비슷한 효과를 주지만 전처리기 단계에서 일어난다는 차이가 있어요.