인라인 함수
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
인라인 함수는 C++, C99, GNU C 등에서 지원되며, 컴파일러가 함수 호출을 해당 함수 본문으로 대체하여 성능을 향상시키는 데 사용된다. C++에서는 클래스 멤버 함수를 인라인으로 정의할 수 있으며, 템플릿, 특수 멤버 함수, constexpr 함수도 암묵적으로 인라인이다. C99, C++에서는 inline 키워드를 사용하여 인라인 함수를 표준으로 지원하며, C99에서는 extern inline으로 정의된 함수는 항상 외부에서 볼 수 있는 함수를 생성한다. 인라인 함수는 매크로에 비해 형식 검사, 부작용 방지, 디버깅 용이성 등 여러 장점을 제공한다. 하지만, 코드 크기 증가, 컴파일 시간 증가, 링커 오류 발생 가능성, 특정 컴파일러 제약 등의 문제점도 존재한다.
C++, C99, GNU C 등 여러 프로그래밍 언어에서 인라인 함수를 지원한다.[9][10] 컴파일러마다 인라인 처리 방식은 다르지만, 마이크로소프트 비주얼 C++나 GCC와 같은 주류 C++ 컴파일러들은 인라인 함수로 명시되지 않은 함수도 자동으로 인라인하는 옵션을 제공한다.
2. 언어 지원
inline int max(int a, int b)
{
return (a > b) ? a : b;
}
위 코드는 다음과 같이 변환될 수 있다.
a = (x > y) ? x : y;
C++(C++) 및 C99 이후의 C 언어는 `inline` 키워드를 사용하여 인라인 함수를 표준으로 지원한다. Microsoft Visual C++는 C 언어 모드에서 `inline` 키워드를 지원하지 않지만, `__inline` 키워드로 지원한다.[9][10] Ada에서는 `pragma`를 인라인 함수로 사용할 수 있다. Delphi (Object Pascal)는 2005년 이후 인라인 함수 지원을 추가했다. Java나 JavaScript는 언어 사양에는 없지만, JIT 컴파일러가 최적화의 일환으로 인라인 전개를 수행하기도 한다. Oracle의 Java SE 컴파일러는 인라인 디렉티브 옵션을 지원한다.[11] 함수형 프로그래밍 언어 등 다른 많은 언어는 인라인 함수를 지원하지 않지만, 인라인 전개를 적극적으로 수행하는 경우가 많다.
C++에서는 클래스 및 구조체의 인라인 멤버 함수를 정의할 수도 있으며, 클래스 및 구조체의 타입 정의 내에 직접 구현을 기술한 멤버 함수는 암묵적으로 `inline`이 된다.[12] C++에서는 함수 템플릿, 컴파일러가 암묵적으로 선언과 정의를 자동 생성한 특수 멤버 함수, C++11 이후의 `constexpr` 함수[15]도 암묵적으로 `inline`이다.
일부 구현체는 컴파일러가 함수를 강제로 인라인하도록 하는 수단을 제공한다.
| 컴파일러 | 강제 인라인 지정자 |
|---|---|
| 마이크로소프트 Visual C++ | `__forceinline` |
| gcc 또는 clang | `__attribute__((always_inline))` 또는 `__attribute__((__always_inline__))` |
2. 1. C/C++
C++, C99, GNU C는 각각 인라인 함수를 지원한다. 함수의 인라인 처리 방식은 컴파일러마다 다르다. 마이크로소프트 비주얼 C++와 GCC 같은 주류 C++ 컴파일러들은 인라인 함수로 표시되지 않은 것이 있더라도 적절한 함수를 자동으로 인라인하는 옵션을 지원한다.C++와 C99는 K&R C와 C89와는 달리 `inline` 함수를 지원하지만, 의미는 서로 다르다. 두 경우 모두에서 `inline`은 인라인을 강제하지 않으며, 컴파일러는 함수를 전혀 인라인하지 않거나 일부 경우에만 인라인하는 것을 자유롭게 선택할 수 있다.
C++에서 `inline`으로 정의된 함수는 필요에 따라 여러 번역 단위 간에 공유되는 함수를 생성하며, 일반적으로 필요한 객체 파일의 공통 섹션에 배치한다. 이 함수는 항상 `inline` 한정자를 사용하여 모든 곳에서 동일한 정의를 가져야 한다. C++에서 `extern inline`은 `inline`과 동일하다. C++ 접근 방식은 프로그래머에게 가장 편리한 방법이다.
`inline` 한정자는 클래스 정의의 일부로 정의된 함수에 자동으로 추가된다.
C90 모드의 armcc는 C++와 동일한 의미를 가진 `extern inline`과 `inline`을 제공한다. 이러한 정의는 필요할 경우 여러 번역 단위 간에 공유되는 함수를 생성한다. C99 모드에서 `extern inline`은 항상 함수를 생성하지만, C++와 마찬가지로 여러 번역 단위 간에 공유된다. 따라서 동일한 함수를 여러 번역 단위에서 `extern inline`으로 정의할 수 있다.[8]
인라인 전개는 함수 호출에 드는 오버헤드를 없앨 목적으로 수행된다.
C++(C++)에서는 인라인 함수를 모듈 단위로 정의해야 한다 (일반적인 함수는 하나의 모듈에서 정의하면 된다). 이를 통해 모듈 단위로 독립적인 컴파일이 가능하게 된다.
C++(C++) 및 C99 이후의 C 언어는 `inline` 키워드를 사용하여 인라인 함수를 표준으로 지원한다. Microsoft Visual C++는 버전 2017에서도 C99를 완벽하게 지원하지 않아 C 언어 모드에서는 `inline` 키워드를 지원하지 않지만, 대신 인라인 함수는 자체 확장된 `__inline` 키워드로 지원한다.[9][10] Microsoft Visual C++나 g++ 등은 인라인 함수로 지정되지 않아도 인라인 전개해야 하는 함수를 자동으로 전개하는 옵션을 제공한다.
2. 1. 1. C99
C99와 C++는 이전 버전인 K&R C와 C89와는 달리 `inline` 함수를 지원하지만, 그 의미는 서로 다르다. 두 경우 모두에서 `inline`은 인라인을 강제하지 않으며, 컴파일러는 함수를 전혀 인라인하지 않거나 일부 경우에만 인라인하는 것을 자유롭게 선택할 수 있다. 컴파일러마다 인라인할 수 있는 함수의 복잡성이 다르다. 마이크로소프트 비주얼 C++ 및 GCC와 같은 주류 C++ 컴파일러는 컴파일러가 `inline` 함수로 표시되지 않은 함수까지 포함하여 적절한 모든 함수를 자동으로 인라인하도록 하는 옵션을 지원한다.[3] 그러나 컴파일러가 모든 인라인 결정을 내리도록 하기 위해 `inline` 키워드를 생략하는 것은 불가능하다. 왜냐하면 링커가 다른 번역 단위에서 중복 정의에 대해 문제를 제기하기 때문이다. 이는 `inline`이 컴파일러에게 함수를 인라인해야 한다는 힌트를 제공할 뿐만 아니라, 컴파일러가 호출 가능한 아웃오브라인(out-of-line) 함수의 복사본을 생성할지 여부에도 영향을 미치기 때문이다 ( inline 함수의 저장 클래스 참조).C99에서 `inline`으로 정의된 함수는 외부에서 볼 수 있는 함수를 절대 생성하지 않으며, `extern inline`으로 정의된 함수는 항상 외부에서 볼 수 있는 함수를 생성한다.[3] C++와 달리, 여러 번역 단위 간에 공유되는 외부에서 볼 수 있는 함수가 필요한 경우에만 생성되도록 요청할 방법이 없다.
`inline` 선언이 `extern inline` 선언 또는 한정되지 않은 선언(즉, `inline` 한정자 또는 저장 클래스가 없는 선언)과 혼합된 경우, 번역 단위는 정의(한정되지 않음, `inline` 또는 `extern inline` 여부에 관계없이)를 포함해야 하며, 외부에서 볼 수 있는 함수가 생성된다.[3]
`inline`으로 정의된 함수는 프로그램 어딘가에 정확히 하나의 해당 이름을 가진 함수가 필요하며, 이 함수는 `extern inline`으로 정의되거나 한정자 없이 정의된다.[3] 전체 프로그램에서 이러한 정의가 둘 이상 제공되면 링커가 중복 기호에 대해 문제를 제기한다. 그러나 이것이 부족한 경우, 모든 사용이 인라인될 수 있다면 필요하지 않기 때문에 링커가 반드시 문제를 제기하지는 않는다. 하지만 컴파일러가 항상 `inline` 한정자를 무시하고 대신 함수 호출을 생성할 수 있으므로, 일반적으로 최적화 없이 코드를 컴파일하는 경우 발생하므로 문제를 제기할 수 있다. (함수가 모든 수단으로 어디에서나 인라인되도록 하고, 그렇지 않은 경우 오류가 생성되어야 하는 경우 원하는 동작일 수 있습니다.) 편리한 방법은 `inline` 함수를 헤더 파일에 정의하고, 각각 `extern inline` 선언을 포함하고 정의가 있는 해당 헤더 파일을 포함하는 함수당 하나의 .c 파일을 만드는 것이다. 선언이 포함 전에 있든 후에 있든 상관 없다.
함수의 모든 사용이 인라인된 경우 최종 실행 파일에 도달할 수 없는 코드가 추가되는 것을 방지하기 위해, 모든 `extern inline` 함수가 있는 모든 .c 파일의 개체 파일을 정적 라이브러리 파일에 배치하고, 일반적으로 `ar rcs`를 사용하여 해당 라이브러리를 개별 개체 파일 대신 링크하는 것이 좋다.[3] 이렇게 하면 실제로 필요한 개체 파일만 링크되므로, 개체 파일을 직접 링크하여 실행 파일에 항상 포함되도록 하는 것과는 대조적이다. 그러나 라이브러리 파일은 라이브러리 파일 이후에 지정된 개체 파일에서 함수에 대한 호출이 링커에 의해 고려되지 않으므로 링커 명령줄의 다른 모든 개체 파일 뒤에 지정해야 한다. `inline` 함수에서 다른 `inline` 함수에 대한 호출은 링커에 의해 자동으로 해결된다( `ar rcs`의 `s` 옵션은 이를 보장한다).
대안적인 해결책은 라이브러리 대신 링크 시간 최적화를 사용하는 것이다. gcc는 모든 함수가 사용되지 않는 섹션을 생략하는 `-Wl,--gc-sections` 플래그를 제공한다. 이것은 사용되지 않은 단일 `extern inline` 함수의 코드를 포함하는 개체 파일의 경우이다. 그러나 사용되지 않은 `extern inline` 함수와 관련된 개체 파일뿐만 아니라 다른 모든 개체 파일에서 다른 모든 사용되지 않은 섹션도 제거한다. (예를 들어 프로그램 자체보다는 디버거에서 프로그래머가 호출할 함수를 실행 파일에 링크하여 프로그램의 내부 상태를 검사하는 것이 좋다.) 이 접근 방식을 사용하면 함수당 하나의 .c 파일 대신 모든 `extern inline` 함수가 있는 단일 .c 파일을 사용할 수도 있다. 그런 다음 `-fdata-sections -ffunction-sections`으로 파일을 컴파일해야 한다. 그러나 gcc 매뉴얼 페이지는 "이렇게 함으로써 상당한 이점이 있는 경우에만 이러한 옵션을 사용하십시오"라고 경고한다.
일부는 헤더 파일에서 함수를 `inline` 대신 `static inline`으로 정의하는 완전히 다른 접근 방식을 권장한다.[2] 그러면 도달할 수 없는 코드가 생성되지 않는다. 그러나 이 접근 방식은 반대의 경우에 단점이 있다. 함수가 둘 이상의 번역 단위에서 인라인될 수 없는 경우 중복 코드가 생성된다. 방출된 함수 코드는 서로 다른 주소를 가져야 하므로 번역 단위 간에 공유할 수 없다. 이것은 또 다른 단점이다. 헤더 파일에서 `static inline`으로 정의된 이러한 함수의 주소를 사용하면 서로 다른 번역 단위에서 서로 다른 값이 생성된다. 따라서 `static inline` 함수는 하나의 번역 단위에서만 사용되는 경우에만 사용해야 하며, 이는 해당 .c 파일로만 이동해야 하고 헤더 파일로 이동해서는 안 됨을 의미한다.
2. 1. 2. gnu89
gcc는 버전 4.2까지 `-std=c99`가 명시적으로 지정된 경우에도 gnu89 `inline` 의미를 사용했다.[6] 버전 5부터,[5] gcc는 gnu89에서 gnu11 방언으로 전환하여 기본적으로 C99 `inline` 의미를 활성화했다. 대신 gnu89 의미를 사용하려면 `-std=gnu89` 또는 인라인에만 영향을 미치도록 `-fgnu89-inline`을 사용하거나, 모든 `inline` 선언에 `gnu_inline` 속성을 추가하여 명시적으로 활성화해야 한다. C99 의미를 보장하려면 `-std=c99`, `-std=c11`, `-std=gnu99` 또는 `-std=gnu11`(`-fgnu89-inline` 없이)을 사용할 수 있다.[3]`inline` 및 `extern inline`의 gnu89 의미는 C99와 정반대이며,[4] gnu89는 `extern inline` 함수를 자격이 없는 함수로 재정의하는 것을 허용하지만, C99 `inline`은 그렇지 않다는 점을 제외하면 거의 동일하다.[5] 따라서 gnu89 `extern inline`은 재정의되지 않은 경우 C99 `inline`과 같고, gnu89 `inline`은 C99 `extern inline`과 같다. 즉, gnu89에서 `inline`으로 정의된 함수는 항상 외부에서 볼 수 있는 함수를 내보내지 않으며, `extern inline`으로 정의된 함수는 절대 내보내지 않는다. 이는 `extern`으로 정의하면 저장 공간이 절대 예약되지 않고, 정의하지 않으면 항상 예약되는 변수와 일치하기 때문이다. 반면에 C99는 `inline`을 사용하면 함수의 인라인되지 않은 버전을 항상 내보내는 부작용이 발생하여 이름이 시사하는 바와 반대되는 경우, 즉 놀라운 일이기 때문에 이러한 방식을 채택했다.
인라인 함수에 대해 정확히 하나의 외부에서 볼 수 있는 함수 인스턴스를 제공해야 한다는 C99의 설명과 그로 인한 도달할 수 없는 코드 문제는 gnu89에도 마찬가지로 적용된다.
2. 1. 3. C++
C++와 C99, GNU C는 각각 인라인 함수에 대한 지원을 갖추고 있다. 함수의 인라인 처리 방식은 컴파일러마다 다르다. 마이크로소프트 비주얼 C++와 GCC와 같은 주류 C++ 컴파일러들은 인라인 함수로 표시되지 않은 것이 있다고 할지라도 적절한 함수를 자동으로 인라인하는 옵션을 지원한다.[7]C99이나 C++로 작성된 인라인 함수는 다음과 같다:
```c
inline int max(int a, int b)
{
return (a > b) ? a : b;
}
```
여기서 다음과 같은 구문은
```c
a = max(x, y);
```
더 직접적인 계산으로 변환될 수 있다:
```c
a = (x > y) ? x : y;
```
C++와 C99는 이전 버전인 K&R C와 C89와는 달리 `inline` 함수를 지원하지만, 의미는 서로 다르다. 두 경우 모두에서 `inline`은 인라인을 강제하지 않으며, 컴파일러는 함수를 전혀 인라인하지 않거나 일부 경우에만 인라인하는 것을 자유롭게 선택할 수 있다. 컴파일러마다 인라인할 수 있는 함수의 복잡성이 다르다. 마이크로소프트 비주얼 C++ 및 GCC와 같은 주류 C++ 컴파일러는 컴파일러가 `inline` 함수로 표시되지 않은 함수까지 포함하여 적절한 모든 함수를 자동으로 인라인하도록 하는 옵션을 지원한다. 그러나 컴파일러가 모든 인라인 결정을 내리도록 하기 위해 `inline` 키워드를 생략하는 것은 불가능하다. 왜냐하면 링커가 다른 번역 단위에서 중복 정의에 대해 불만을 제기하기 때문이다. 이는 `inline`이 컴파일러에게 함수를 인라인해야 한다는 힌트를 제공할 뿐만 아니라, 컴파일러가 호출 가능한 아웃오브라인(out-of-line) 함수의 복사본을 생성할지 여부에도 영향을 미치기 때문이다.
C++에서 `inline`으로 정의된 함수는 필요에 따라 여러 번역 단위 간에 공유되는 함수를 생성하며, 일반적으로 필요한 객체 파일의 공통 섹션에 배치한다. 이 함수는 항상 `inline` 한정자를 사용하여 모든 곳에서 동일한 정의를 가져야 한다. C++에서 `extern inline`은 `inline`과 동일하다. C++ 접근 방식의 근거는 프로그래머에게 가장 편리한 방법이라는 것이다. 도달할 수 없는 코드 제거를 위한 특별한 예방 조치를 취할 필요가 없고, 일반 함수와 마찬가지로 `extern`을 지정하든 아니든 차이가 없기 때문이다.
`inline` 한정자는 클래스 정의의 일부로 정의된 함수에 자동으로 추가된다.
C90 모드의 armcc는 C++와 동일한 의미를 가진 `extern inline`과 `inline`을 제공한다. 이러한 정의는 필요할 경우 여러 번역 단위 간에 공유되는 함수를 생성한다. C99 모드에서 `extern inline`은 항상 함수를 생성하지만, C++와 마찬가지로 여러 번역 단위 간에 공유된다. 따라서 동일한 함수를 여러 번역 단위에서 `extern inline`으로 정의할 수 있다.[8]
인라인 전개는 함수 호출에 드는 오버헤드를 없앨 목적으로 수행된다.
C++(C++)에서는 인라인 함수를 모듈 단위로 정의해야 한다 (일반적인 함수는 하나의 모듈에서 정의하면 된다). 이를 통해 모듈 단위로 독립적인 컴파일이 가능하게 된다.
C++(C++) 및 C99 이후의 C 언어는 `inline` 키워드를 사용하여 인라인 함수를 표준으로 지원한다. Microsoft Visual C++는 버전 2017에서도 C99를 완벽하게 지원하지 않아 C 언어 모드에서는 `inline` 키워드를 지원하지 않지만, 대신 인라인 함수는 자체 확장된 `__inline` 키워드로 지원한다.[9][10] Microsoft Visual C++나 g++ 등은 인라인 함수로 지정되지 않아도 인라인 전개해야 하는 함수를 자동으로 전개하는 옵션을 제공한다.
C99/C++에서의 인라인 함수 정의 및 사용 예는 다음과 같다.
```c
inline int max(int a, int b) {
if (a > b)
return a;
else
return b;
}
...
int x = 1, y = -2;
int z = max(x--, y); // 1
```
C++에서는 클래스 및 구조체의 인라인 멤버 함수를 정의할 수도 있다. 클래스 및 구조체의 타입 정의 내에 직접 구현을 기술한 멤버 함수는 암묵적으로 `inline`이 된다.[12]
```c++
class MyClass {
int m_number1;
int m_number2;
public:
explicit MyClass(int number1, int number2) : m_number1(number1), m_number2(number2) {}
inline int getNumber1() const { return this->m_number1; }
int getNumber2() const { return this->m_number2; } // 암묵적으로 inline 이 된다.
int getSum() const;
};
inline int MyClass::getSum() const { return this->m_number1 + this->m_number2; }
```
C++에서는 함수 템플릿, 컴파일러가 암묵적으로 선언과 정의를 자동 생성한 특수 멤버 함수, C++11 이후의 `constexpr` 함수[15]도 암묵적으로 `inline`이다.
2. 1. 4. 예제
C99이나 C++로 작성된 인라인 함수 예시는 다음과 같다:inline int max(int a, int b)
{
return (a > b) ? a : b;
}
위 코드는 아래와 같이 더 직접적인 계산으로 변환될 수 있다:
a = (x > y) ? x : y;
C 또는 C++에서 `inline` 함수는 다음과 같이 작성할 수 있다:
inline void swap(int *m, int *n)
{
int tmp = *m;
- m = *n;
- n = tmp;
}
다음과 같은 문장이 있다고 가정했을 때:
swap(&x, &y);
컴파일러가 인라인을 수행하기로 결정하면 (일반적으로 최적화가 활성화되어야 함) 다음과 같이 된다:
int tmp = x;
x = y;
y = tmp;
이는 많은 스왑을 수행하는 정렬 알고리즘을 구현할 때 실행 속도를 증가시킬 수 있다.
C99/C++에서의 인라인 함수 정의 및 사용 예시는 다음과 같다:
inline int max(int a, int b) {
if (a > b)
return a;
else
return b;
}
...
int x = 1, y = -2;
int z = max(x--, y); // 1
C++에서는 클래스 및 구조체의 인라인 멤버 함수를 정의할 수도 있다. 클래스 및 구조체의 타입 정의 내에 직접 구현을 기술한 멤버 함수는 암묵적으로 `inline`이 된다.[12]
class MyClass {
int m_number1;
int m_number2;
public:
explicit MyClass(int number1, int number2) : m_number1(number1), m_number2(number2) {}
inline int getNumber1() const { return this->m_number1; }
int getNumber2() const { return this->m_number2; } // 암묵적으로 inline 이 된다.
int getSum() const;
};
inline int MyClass::getSum() const { return this->m_number1 + this->m_number2; }
C++에서는 함수 템플릿, 컴파일러가 암묵적으로 선언과 정의를 자동 생성한 특수 멤버 함수, C++11 이후의 `constexpr` 함수[15]도 암묵적으로 `inline`이다.
2. 2. 비표준 확장
GNU C는 gnu89 방언의 일부로, C89에 대한 확장으로 `inline`을 지원하지만, 그 의미는 C++ 및 C99와는 다르다. C90 모드의 armcc 역시 비표준 확장으로 `inline`을 제공하며, gnu89 및 C99와 다른 의미를 가진다.[9]일부 구현체는 컴파일러가 함수를 강제로 인라인하도록 하는 수단을 제공하는데, 일반적으로 구현체별 선언 지정자를 사용한다.
| 컴파일러 | 강제 인라인 지정자 |
|---|---|
| 마이크로소프트 Visual C++ | `__forceinline` |
| gcc 또는 clang | `__attribute__((always_inline))` 또는 `__attribute__((__always_inline__))` |
`__attribute__((__always_inline__))`는 `always_inline`이라는 사용자 정의 매크로와의 충돌을 피하는 데 유용하다.
무분별한 인라인 강제 사용은 코드 크기 증가(실행 파일 비대화), 성능 향상 미미 또는 부재, 심지어 성능 저하를 초래할 수 있다. 컴파일러가 모든 상황에서 함수를 인라인할 수 있는 것은 아니며, 이 경우 gcc와 마이크로소프트 Visual C++ 모두 경고를 생성한다.
인라인 강제는 다음과 같은 경우에 유용하다.
- `inline`이 컴파일러에 의해 무시되는 경우 (컴파일러의 비용/이점 분석기에 의해)
- 성능 향상을 위해 인라인 결과가 반드시 필요한 경우
코드 이식성을 위해 다음과 같은 전처리기 지시문을 사용할 수 있다.
```cpp
#ifdef _MSC_VER
#define forceinline __forceinline
#elif defined(__GNUC__)
#define forceinline inline __attribute__((__always_inline__))
#elif defined(__CLANG__)
#if __has_attribute(__always_inline__)
#define forceinline inline __attribute__((__always_inline__))
#else
#define forceinline inline
#endif
#else
#define forceinline inline
#endif
```
C++ 및 C99 이후의 C 언어는 `inline` 키워드를 사용하여 인라인 함수를 표준으로 지원한다. Microsoft Visual C++는 C99를 완벽하게 지원하지 않아 C 언어 모드에서는 `inline` 키워드를 지원하지 않지만, 자체 확장된 `__inline` 키워드로 지원한다.[10] 마이크로소프트 비주얼 C++나 g++ 등은 인라인 함수로 지정되지 않아도 인라인 전개해야 하는 함수를 자동으로 전개하는 옵션을 제공한다.
2. 3. 인라인 함수의 저장 클래스
C++와 C99는 K&R C 및 C89와 달리 `inline` 함수를 지원하지만, 그 의미는 서로 다르다.[2] 두 경우 모두 `inline`은 인라인을 강제하지 않으며, 컴파일러는 함수를 전혀 인라인하지 않거나 일부 경우에만 인라인하는 것을 자유롭게 선택할 수 있다. 컴파일러마다 인라인할 수 있는 함수의 복잡성이 다르다. 마이크로소프트 비주얼 C++ 및 GCC와 같은 주류 C++ 컴파일러는 컴파일러가 `inline` 함수로 표시되지 않은 함수까지 포함하여 적절한 모든 함수를 자동으로 인라인하도록 하는 옵션을 지원한다.`static inline`은 모든 C 방언과 C++에서 동일한 효과를 갖는다. 필요하다면 지역적으로 보이는 (함수의 외부 복사본) 함수를 생성한다. 저장 클래스에 관계없이, 컴파일러는 모든 C 방언과 C++에서 `inline` 한정자를 무시하고 함수 호출을 생성할 수 있다.
`inline` 함수에 `extern` 저장 클래스를 적용하거나 적용하지 않을 때의 효과는 C 방언[2]과 C++ 사이에서 다르다.[3]
- C99에서 `inline`으로 정의된 함수는 외부에서 볼 수 있는 함수를 절대 생성하지 않으며, `extern inline`으로 정의된 함수는 항상 외부에서 볼 수 있는 함수를 생성한다.
- C++에서 `inline`으로 정의된 함수는 필요에 따라 여러 번역 단위 간에 공유되는 함수를 생성하며, 일반적으로 필요한 객체 파일의 공통 섹션에 배치한다. 이 함수는 항상 `inline` 한정자를 사용하여 모든 곳에서 동일한 정의를 가져야 한다. C++에서 `extern inline`은 `inline`과 동일하다.
C++ 접근 방식의 근거는 프로그래머에게 가장 편리한 방법이라는 것이다. 도달할 수 없는 코드 제거를 위한 특별한 예방 조치를 취할 필요가 없고, 일반 함수와 마찬가지로 `extern`을 지정하든 아니든 차이가 없기 때문이다.
`inline` 한정자는 클래스 정의의 일부로 정의된 함수에 자동으로 추가된다.
3. 매크로와의 비교
오래된 C 언어(C89) 등에서는 인라인 전개를 소스 레벨의 인수 있는 매크로로 실현해 왔다. 인라인 함수는 매크로에 비해 다음과 같은 이점이 있다.
- 매크로 호출은 형식 검사를 하지 않고, 인수가 올바른 형식인지도 검사하지 않는다. 반면 인라인 함수 호출에서는 이것들이 검사된다.
- C 언어의 매크로는 단순한 문자열 치환이어서 예상치 못한 부작용이나 인수의 평가를 여러 번 하는 것으로 인한 폐해가 발생할 수 있다. 인라인 함수는 그러한 부작용을 일으키지 않는다.
- 매크로 내부에서의 컴파일 오류는 매크로 전개 후의 코드에서 발생하기 때문에 프로그래머가 이해하기 어렵고 디버깅에 시간이 걸릴 수 있다. 인라인 함수는 컴파일러가 처리하므로 컴파일 오류 발생 위치 및 원인 특정도 쉽다.
- 매크로 내에서는 구문이 제한되어 통상과는 다른 작성을 요구받는다. 인라인 함수는 통상의 함수와 완전히 같으며, 인라인화할지 여부도 자유롭게 결정할 수 있다.
- 인라인화된 코드의 디버깅 정보는 매크로를 전개한 코드보다 다루기 쉽다. 브레이크 포인트 작성도 가능하다.
- 많은 컴파일러에서 일종의 재귀 호출 함수도 인라인 전개할 수 있다. 재귀적 매크로는 일반적으로 부적절하다.
이러한 이점은 매크로를 사용한 제네릭 프로그래밍에 대한 C++의 함수 템플릿의 장점과 마찬가지이다.
C++의 설계자 비야네 스트롭스트루프는 매크로보다 인라인 함수를 사용해야 한다고 주장하고 있다.
4. 제약 사항
`inline` 함수는 컴파일러에게 해당 함수를 호출 위치에 직접 삽입하도록 지시하는 힌트를 제공하지만, 모든 경우에 인라인이 가능한 것은 아니다.
C99에서 `inline` 또는 `extern inline` 함수는 `static` 전역 변수에 접근하거나 비`const` `static` 지역 변수를 정의할 수 없다. `const static` 지역 변수는 함수가 인라인되었는지 여부에 따라 서로 다른 번역 단위에서 서로 다른 객체일 수도 있고 아닐 수도 있다. `static inline` 정의만 내부 연결을 가진 식별자를 제한 없이 참조할 수 있으며, 이러한 식별자는 각 번역 단위에서 서로 다른 객체가 된다. 반면 C++에서는 `const` 및 비`const` `static` 지역 변수 모두 허용되며, 모든 번역 단위에서 동일한 객체를 참조한다.[3]
`inline` 함수의 주소를 가져오면 해당 함수의 비인라인 복사본을 위한 코드가 생성되어야 한다.[3]
GNU 컴파일러 모음와 MS Visual C++는 다음과 같은 경우 함수를 인라인 할 수 없다.
4. 1. GCC
GCC는 GNU C의 gnu89 방언의 일부로서 C89에 대한 확장으로 `inline`을 지원한다. 하지만 그 의미는 C++ 및 C99와는 다르다. C90 모드의 armcc 또한 비표준 확장으로 `inline`을 제공하며, gnu89 및 C99와 다른 의미를 갖는다.[3]일부 구현체는 컴파일러가 함수를 강제로 인라인하도록 하는 수단을 제공하는데, 일반적으로 구현체별 선언 지정자를 사용한다. gcc 또는 clang의 경우 `__attribute__((always_inline))` 또는 `__attribute__((__always_inline__))`를 사용한다. 후자는 `always_inline`이라는 사용자 정의 매크로와의 충돌을 피하는 데 유용하다.[3]
코드 이식성을 위해 다음과 같은 전처리기 지시문을 사용할 수 있다.
```cpp
#ifdef _MSC_VER
#define forceinline __forceinline
#elif defined(__GNUC__)
#define forceinline inline __attribute__((__always_inline__))
#elif defined(__CLANG__)
#if __has_attribute(__always_inline__)
#define forceinline inline __attribute__((__always_inline__))
#else
#define forceinline inline
#endif
#else
#define forceinline inline
#endif
```
`inline` 함수의 주소를 가져오는 것은 어떠한 경우에도 해당 함수의 비인라인 복사본을 위한 코드를 생성해야 한다.
C99에서 `inline` 또는 `extern inline` 함수는 `static` 전역 변수에 접근하거나 비`const` `static` 지역 변수를 정의해서는 안 된다. `const static` 지역 변수는 함수가 인라인되었는지 또는 호출이 이루어졌는지에 따라, 서로 다른 번역 단위에서 서로 다른 객체일 수도 있고 아닐 수도 있다. `static inline` 정의만 제한 없이 내부 연결을 가진 식별자를 참조할 수 있으며, 이러한 식별자는 각 번역 단위에서 서로 다른 객체가 된다. C++에서는 `const` 및 비`const` `static` 지역 변수가 모두 허용되며, 모든 번역 단위에서 동일한 객체를 참조한다.
GCC는 다음과 같은 경우 함수를 인라인할 수 없다.[3]
| 인라인 불가 조건 |
|---|
| 가변 인자인 경우 |
| `alloca`를 사용하는 경우 |
| 계산된 `goto`를 사용하는 경우 |
| 비지역 `goto`를 사용하는 경우 |
| 중첩 함수를 사용하는 경우 |
| `setjmp`를 사용하는 경우 |
| `__builtin_longjmp`를 사용하는 경우 |
| `__builtin_return`을 사용하는 경우 |
| `__builtin_apply_args`를 사용하는 경우 |
4. 2. MS Visual C++
마이크로소프트 비주얼 C++는 자체 확장 키워드인 `__inline`을 통해 인라인 함수를 지원한다.[9][10] 2017년 버전에서도 C99를 완전히 지원하지 않아 C 언어 모드에서는 `inline` 키워드를 지원하지 않는다. 또한, `__forceinline` 키워드를 사용하면 컴파일러가 함수를 강제로 인라인하도록 할 수 있다.MSDN의 Microsoft 사양에 따르면, MS Visual C++는 `__forceinline`을 사용하더라도 다음과 같은 경우에는 함수를 인라인할 수 없다.
| 경우 |
|---|
| 함수 또는 해당 호출자가 /Ob0 (디버그 빌드의 기본 옵션)으로 컴파일된 경우 |
| 함수와 호출자가 서로 다른 유형의 예외 처리를 사용하는 경우 (C++ 예외 처리, 구조적 예외 처리) |
| 함수에 가변 인수 목록이 있는 경우 |
| 함수가 인라인 어셈블리를 사용하는 경우 (단, /Og, /Ox, /O1 또는 /O2로 컴파일된 경우는 제외) |
| 함수가 재귀적이며 `#pragma inline_recursion(on)`이 없는 경우 (프라그마를 사용하면 재귀 함수는 기본 깊이 16 호출까지 인라인되며, `inline_depth` 프라그마를 사용하여 인라인 깊이를 줄일 수 있음) |
| 함수가 가상이며 가상으로 호출되는 경우 (가상 함수에 대한 직접 호출은 인라인될 수 있음) |
| 프로그램이 함수의 주소를 가져오고 함수 포인터를 통해 호출하는 경우 (주소가 사용된 함수의 직접 호출은 인라인될 수 있음) |
| 함수가 naked `__declspec` 수정자로 표시된 경우 |
C++에서는 클래스 및 구조체의 타입 정의 내에 직접 구현을 작성한 멤버 함수는 암묵적으로 `inline`이 된다.[12]
5. 문제점
일반적으로 인라인 확장의 문제점 외에도, `inline` 함수는 여러 가지 이유로 보이는 만큼 가치가 없을 수 있다.
- 컴파일러는 특정 함수를 인라인할지 여부를 결정하는 데 인간보다 더 나은 위치에 있는 경우가 많다. 때로는 컴파일러가 프로그래머가 지정한 만큼 많은 함수를 인라인하지 못할 수도 있다.
- 주목해야 할 중요한 점은 ( `inline` 함수의) 코드가 클라이언트(호출 함수)에 노출된다는 것이다.
- 함수가 발전함에 따라 이전에는 인라인하기에 적합하지 않았던 함수가 인라인에 적합해지거나, 이전에는 인라인에 적합했던 함수가 더 이상 적합하지 않을 수 있다. 함수를 인라인하거나 인라인 해제하는 것은 매크로로 변환하고 변환하는 것보다 쉽지만, 일반적으로 상대적으로 적은 이점을 제공하는 추가적인 유지 관리가 필요하다.
- 네이티브 C 기반 컴파일 시스템에서 널리 사용되는 인라인 함수는 컴파일 시간을 늘릴 수 있다. 이는 해당 함수의 중간 표현이 각 호출 사이트에 복사되기 때문이다.
- C99의 `inline` 사양은 어딘가에서 사용되는 경우 해당 함수의 외부 정의를 정확히 하나만 요구한다. 프로그래머가 이러한 정의를 제공하지 않은 경우, 이는 쉽게 링커 오류로 이어질 수 있다. 이는 일반적으로 인라인을 방지하는 최적화를 끄면 발생할 수 있다. 반면에 정의를 추가하면, 프로그래머가 라이브러리에 넣어 링크하거나, 링크 시간 최적화를 사용하거나, `static inline`을 사용하여 주의 깊게 피하지 않으면 도달할 수 없는 코드가 발생할 수 있다.
- C++에서는 `inline` 함수를 사용하는 모든 모듈(번역 단위)에서 해당 함수를 정의해야 하는 반면, 일반 함수는 단일 모듈에서만 정의해야 한다. 그렇지 않으면 다른 모든 모듈과 독립적으로 단일 모듈을 컴파일할 수 없다. 컴파일러에 따라, 이는 인라인될 수 없는 일부 사용 사례가 있는 각 모듈에 대해 해당 함수 코드의 복사본을 각 해당 객체 파일에 포함시킬 수 있다.
- 임베디드 소프트웨어에서는, 종종 특정 컴파일러 지시어(예: "pragma" 문)를 사용하여 특정 함수를 특정 코드 섹션에 배치해야 한다. 때로는 한 메모리 세그먼트의 함수가 다른 메모리 세그먼트의 함수를 호출해야 하며, 호출된 함수의 인라인이 발생하는 경우 호출된 함수의 코드가 원하지 않는 세그먼트에 있을 수 있다. 예를 들어, 고성능 메모리 세그먼트는 코드 공간이 매우 제한적일 수 있으며, 이러한 공간에 속하는 함수가 고성능 섹션에 속하지 않는 다른 큰 함수를 호출하고 호출된 함수가 부적절하게 인라인되면 고성능 메모리 세그먼트의 코드 공간이 부족해질 수 있다. 이러한 이유로, 때로는 함수가 인라인되지 않도록 보장해야 한다.
인라인 함수와 관련된 문제뿐만 아니라, 인라인 함수는 언어 기능으로 적극적으로 사용되지 않는 측면이 있다. 그 이유는 다음과 같다.
- 대부분의 경우, 사람이 인라인화해야 할 함수를 결정하는 것보다 컴파일러가 결정하는 것이 더 좋은 결과를 얻는다. 사람이 인라인화하고 싶어하는 함수보다 컴파일러가 인라인화할 수 있다고 판단하는 함수의 수가 적은 경우에는 특히 그렇다.
- 프로그램 수정으로 인해, 이전에는 인라인화해야 했던 함수가 인라인화하지 않아야 하는 함수가 되거나, 그 반대의 변화가 일어난다. 이는 매크로의 경우도 마찬가지이지만, 코드 유지보수라는 관점에서 보면 인라인 함수는 별로 장점이 없다.
- C언어에서 인라인 함수를 많이 사용하면 컴파일 시간이 늘어나는 경향이 있다. 이는 함수의 실체가 호출하는 각 위치에 삽입되어 중간 표현을 형성하기 때문이다. 코드 크기의 증가는 컴파일 시간 증가도 초래한다.
인라인 전개 자체의 문제에 대해서는 인라인 전개의 단점을 참조하라.
참조
[1]
간행물
The New C: Inline Functions
http://www.drdobbs.c[...]
2002-07-01
[2]
웹사이트
Inline Functions in C
http://www.greenend.[...]
[3]
웹사이트
Using the GNU Compiler Collection (GCC): Inline
https://gcc.gnu.org/[...]
[4]
웹사이트
Josef "Jeff" Sipek » GNU inline vs. C99 inline
http://blahg.josefsi[...]
[5]
웹사이트
Porting to GCC 5 - GNU Project
https://gcc.gnu.org/[...]
[6]
웹사이트
Ian Lance Taylor - Clean up extern inline
https://gcc.gnu.org/[...]
[7]
웹사이트
Documentation – Arm Developer
http://infocenter.ar[...]
[8]
문서
gcc manual page, description of -fno-common
[9]
Microsoft Docs
Inline Functions (C++) | Microsoft Docs
https://docs.microso[...]
[10]
Microsoft Docs
Inline Functions | Microsoft Docs
https://docs.microso[...]
[11]
Oracle Java SE 11 Help Center
Java仮想マシン・ガイド §ディレクティブの記述 | Oracle Java SE 11 Help Center
https://docs.oracle.[...]
[12]
cppreference.com
inline 指定子 - cppreference.com
https://ja.cpprefere[...]
[13]
cppreference.com
デフォルトコンストラクタ - cppreference.com
https://ja.cpprefere[...]
[14]
cppreference.com
デストラクタ - cppreference.com
https://ja.cpprefere[...]
[15]
cppreference.com
constexpr 指定子 (C++11以上) - cppreference.com
https://ja.cpprefere[...]
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com