"정적"또는 "외부"가없는 "인라인"이 C99에서 유용합니까?


96

이 코드를 빌드하려고 할 때

inline void f() {}

int main()
{
    f();
}

명령 줄 사용

gcc -std=c99 -o a a.c

링커 오류 (에 대한 정의되지 않은 참조 f)가 발생합니다. 그냥를 사용 static inline하거나 extern inline대신 inline또는 컴파일 하면 오류가 사라집니다 -O(따라서 함수가 실제로 인라인됩니다).

이 동작은 C99 표준의 단락 6.7.4 (6)에 정의 된 것으로 보입니다.

번역 단위의 함수에 대한 모든 파일 범위 선언에이 inline없는 함수 지정자가 포함 된 경우 extern해당 번역 단위의 정의는 인라인 정의입니다. 인라인 정의는 함수에 대한 외부 정의를 제공하지 않으며 다른 번역 단위의 외부 정의를 금지하지 않습니다. 인라인 정의는 외부 정의에 대한 대안을 제공하며, 번역자가 동일한 번역 단위에서 함수에 대한 호출을 구현하는 데 사용할 수 있습니다. 함수 호출이 인라인 정의를 사용하는지 외부 정의를 사용하는지는 지정되지 않습니다.

이 모든 것을 올바르게 이해 inline하면 위의 예제에서 정의한 함수가있는 컴파일 단위 는 동일한 이름의 외부 함수도있을 때만 일관되게 컴파일되며 내 함수 또는 외부 함수가 호출되는지 알 수 없습니다.

이 행동은 완전히 멍청하지 않습니까? C99 inline없이 static또는 externC99에서 함수를 정의하는 것이 유용 합니까? 내가 뭔가를 놓치고 있습니까?

답변 요약

물론 나는 무언가를 놓치고 있었고 행동은 멍청하지 않았습니다. :)

Nemo가 설명 했듯이 아이디어는 함수 의 정의 를 넣는 것 입니다.

inline void f() {}

헤더 파일과 선언

extern inline void f();

해당 .c 파일에서. extern선언 만 외부에서 볼 수있는 이진 코드 생성을 트리거합니다. 그리고 실제로 inline.c 파일에서는를 사용하지 않습니다 . 헤더에서만 유용합니다.

는 AS 조나단의 답변에 인용 된 C99위원회의 근거 해명, inline호출의 사이트에서 볼 수 있도록 함수의 정의를 요구하는 컴파일러 최적화에 대한 모든 것입니다. 이것은 헤더에 정의를 넣어야 만 가능하며 물론 헤더의 정의는 컴파일러가 볼 때마다 코드를 내 보내지 않아야합니다. 그러나 컴파일러가 실제로 함수를 인라인하도록 강요하지 않기 때문에 외부 정의가 어딘가에 있어야합니다.




@Earlz : 링크 주셔서 감사합니다. 내 질문은 표준의 인용 된 단락 뒤에있는 이론적 근거에 관한 것이며 , 그러나 inline없이 static및에 대한 사용 사례가 있는지 여부 extern입니다. 불행히도 이러한 문제는 해당 질문에서 다루지 않습니다.
Sven Marnach 2011-06-10

@Nemo : 내 질문을 게시하기 전에 그 질문과 답변을 읽었습니다. 다시 말하지만 "어떻게 작동합니까?"라고 묻지 않습니다. 오히려 "이 행동의 배후에있는 아이디어는 무엇입니까?" 여기에 뭔가 빠졌다고 확신합니다.
Sven Marnach 2011 년

1
네, 그것이 제가 "경계선"이라고 말한 이유입니다. 나는 개인적으로이 완전히 다른 질문을 생각
Earlz

답변:


40

실제로이 훌륭한 답변은 귀하의 질문에 대한 답변도 제공합니다.

extern 인라인은 무엇을합니까?

아이디어는 "인라인"이 헤더 파일에서 사용 된 다음 .c 파일에서 "외부 인라인"으로 사용될 수 있다는 것입니다. "extern inline"은 생성 된 코드 (외부에서 볼 수있는)를 포함해야하는 객체 파일을 컴파일러에 지시하는 방법입니다.

[정교하게 업데이트]

.c 파일에서 "인라인"( "정적"또는 "외부"없이)에 대한 사용이 없다고 생각합니다. 그러나 헤더 파일에서는 의미가 있으며 실제로 독립 실행 형 코드를 생성하려면 일부 .c 파일에 해당 "extern inline"선언이 필요합니다.


1
감사합니다. 아이디어를 얻기 시작했습니다!
Sven Marnach 2011 년

8
네, 지금까지 직접 이해하지 못 했으니 물어봐 주셔서 감사합니다 :-). 비 외부 "인라인" 정의 가 헤더에 들어가는 반면 (반드시 코드 생성이 반드시 발생하지는 않음) "extern inline" 선언 은 .c 파일에 들어가 실제로 코드가 생성됩니다.
Nemo

3
이것은 내가 inline void f() {}헤더와 extern inline void f();.c 파일에 쓰는 것을 의미합니까 ? 따라서 실제 함수 정의는 헤더에 들어가고 .c 파일에는이 경우 일반적인 순서와 반대로 단순한 선언이 포함되어 있습니까?
스벤 Marnach

1
@endolith : 귀하의 질문을 이해하지 못합니다. 또는 inline 없이 논의하고 있습니다. 물론 괜찮습니다.하지만이 질문과 대답은 그게 아닙니다. staticexternstatic inline
Nemo

2
@MatthieuMoy -std=c99대신 -std=gnu89.
a3f

27

표준 (ISO / IEC 9899 : 1999) 자체에서 :

부록 J.2 정의되지 않은 동작

  • ...
  • 외부 연결이있는 inline함수 는 함수 지정자로 선언 되지만 동일한 변환 단위 (6.7.4)에서도 정의되지 않습니다.
  • ...

C99위원회는 근거를 작성했으며 다음 과 같이 말합니다.

6.7.4 함수 지정자

C99의 새로운 기능 :inline C ++에서 채택 된 키워드 는 함수 선언에서만 사용할 수 있는 함수 지정자 입니다. 호출 사이트에서 볼 수있는 함수 정의가 필요한 프로그램 최적화에 유용합니다. (표준은 이러한 최적화의 특성을 지정하지 않습니다.)

함수에 내부 연결이 있거나 외부 연결이 있고 호출이 외부 정의와 동일한 변환 단위에있는 경우 가시성이 보장됩니다. 이러한 경우 inline함수의 선언 또는 정의에 키워드 가 존재하는 것은 해당 함수의 호출이 inline키워드 없이 선언 된 다른 함수의 호출보다 우선적으로 최적화되어야한다는 기본 설정을 나타내는 것 이상의 효과가 없습니다 .

가시성은 호출이 함수의 정의와 다른 변환 단위에있는 외부 연결이있는 함수 호출의 문제입니다. 이 경우 inline키워드를 사용하면 호출이 포함 된 번역 단위가 함수의 로컬 또는 인라인 정의도 포함 할 수 있습니다.

프로그램에는 외부 정의가있는 번역 단위, 인라인 정의가있는 번역 단위 및 선언이 있지만 함수에 대한 정의가없는 번역 단위가 포함될 수 있습니다. 후자의 번역 단위의 호출은 평소와 같이 외부 정의를 사용합니다.

함수의 인라인 정의는 외부 정의와 다른 정의로 ​​간주됩니다. func인라인 정의가 표시되는 곳에서 외부 연결이있는 일부 함수에 대한 호출이 발생하면 __func내부 연결을 사용 하여 다른 함수 (예 :)를 호출 한 것과 동일한 동작이 수행됩니다 . 준수 프로그램은 호출되는 함수에 의존해서는 안됩니다. 이것은 표준의 인라인 모델입니다.

준수 프로그램은 인라인 정의를 사용하는 구현에 의존해서는 안되며 외부 정의를 사용하는 구현에 의존해서는 안됩니다. 함수의 주소는 항상 외부 정의에 해당하는 주소이지만이 주소가 함수를 호출하는 데 사용되는 경우 인라인 정의가 사용될 수 있습니다. 따라서 다음 예제는 예상대로 작동하지 않을 수 있습니다.

inline const char *saddr(void)
{
    static const char name[] = "saddr";
    return name;
}
int compare_name(void)
{
    return saddr() == saddr(); // unspecified behavior
}

구현시 호출 중 하나에 인라인 정의를 saddr사용하고 다른 호출에 외부 정의를 사용할 수 있으므로 같음 연산이 1 (true)로 평가된다는 보장은 없습니다. 이는 인라인 정의 내에서 정의 된 정적 개체가 외부 정의의 해당 개체와 구별됨을 보여줍니다. 이로 인해 const이러한 유형 의 비 객체를 정의하는 것에 대한 제약 이있었습니다.

인라이닝은 기존 링커 기술로 구현할 수있는 방식으로 표준에 추가되었으며 C99 인라인의 하위 집합은 C ++와 호환됩니다. 이는 인라인 함수의 정의를 포함하는 정확히 하나의 번역 단위를 함수에 대한 외부 정의를 제공하는 것으로 지정하도록 요구함으로써 달성되었습니다. 해당 사양은 inline키워드가 없거나 inline및 둘 다 포함 하는 선언으로 만 구성되기 때문에 externC ++ 번역기에서도 허용됩니다.

C99의 인라인은 두 가지 방법으로 C ++ 사양을 확장합니다. 첫째, 함수가 inline하나의 번역 단위에서 선언 inline된 경우 다른 모든 번역 단위에서 선언 될 필요가 없습니다 . 예를 들어 라이브러리 내에서 인라인되지만 다른 곳에서 외부 정의를 통해서만 사용할 수있는 라이브러리 함수를 허용합니다. 외부 함수에 래퍼 함수를 ​​사용하는 대신 추가 이름이 필요합니다. 번역자가 실제로 인라인 대체를 수행하지 않으면 성능에 악영향을 미칠 수도 있습니다.

둘째, 인라인 함수의 모든 정의가 "정확히 동일"해야한다는 요구 사항은 호출이 가시적 인 인라인 정의 또는 외부 정의로 구현되는지 여부에 따라 프로그램의 동작이 의존해서는 안된다는 요구 사항으로 대체됩니다. 함수. 이를 통해 특정 번역 단위 내에서 사용하도록 인라인 정의를 전문화 할 수 있습니다. 예를 들어, 라이브러리 함수의 외부 정의에는 동일한 라이브러리의 다른 함수에서 수행 된 호출에 필요하지 않은 일부 인수 유효성 검사가 포함될 수 있습니다. 이러한 확장은 몇 가지 장점을 제공합니다. 호환성에 관심이있는 프로그래머는 더 엄격한 C ++ 규칙을 따를 수 있습니다.

참고가되어 있지 구현 표준 헤더에 표준 라이브러리 함수의 인라인 정의를 제공하기 위해이 일부 레거시 코드를 깰 수 있기 때문에 적절한 그 자신의 헤더를 포함한 후 redeclares는 표준 라이브러리 함수. 이 inline키워드는 사용자에게 함수 인라인을 제안 할 수있는 이식 가능한 방법을 제공하기위한 것입니다. 표준 헤더는 이식 할 필요가 없기 때문에 구현에는 다음과 같은 다른 옵션이 있습니다.

#define abs(x) __builtin_abs(x)

또는 표준 라이브러리 함수를 인라인하기위한 기타 이식 불가능한 메커니즘.


감사합니다. 이것은 매우 상세하며 표준 자체만큼 가독성이 높습니다. :-) 나는 내가 뭔가를 놓치고 있어야한다는 것을 알았다. 이걸 어디서 얻었는지 참고해 주시겠습니까?
Sven Marnach 2011 년

Google을 사용하여 링크를 찾을 수 있다는 생각이 들었습니다
Sven Marnach 2011 년

@Sven : 검색에서 URL을 차용하여 답변에 넣었습니다. 내가 사용한 문서는 이전에 저장 한 근거 (2005 년)의 사본 이었지만 V5.10이기도했습니다.
Jonathan Leffler 2011 년

4
다시 한 번 감사드립니다. 대답에서 얻은 가장 귀중한 통찰력은 C99 표준의 근거가 포함 된 문서가 있다는 것입니다 (그리고 아마도 다른 표준에 대한 문서도있을 것입니다). Nemo의 대답은 나에게 물고기를 주었지만 이것은 나에게 물고기를 가르쳐주었습니다.
Sven Marnach 2011 년

0

> 링커 오류가 발생합니다 (에 대한 정의되지 않은 참조 f).

여기에서 작동합니다 : Linux x86-64, GCC 4.1.2. 컴파일러의 버그 일 수 있습니다. 나는 주어진 프로그램을 금지하는 표준에서 인용 된 단락에서 아무것도 보지 못합니다. iff 보다는 if 의 사용에주의하십시오 .

인라인 정의는 외부 정의에 대한 대안을 제공하며 , 번역자 동일한 번역 단위에서 함수에 대한 호출을 구현하는 데 사용할 있습니다.

따라서 함수의 동작을 알고 f있고 엄격한 루프에서 호출하려는 경우 함수 호출을 방지하기 위해 해당 정의를 모듈에 복사하여 붙여 넣을 수 있습니다. 또는 , 현재 모듈의 목적에 따라 동등한 정의를 제공 할 수 있습니다 (그러나 입력 유효성 검사 또는 상상할 수있는 최적화를 건너 뜁니다). 그러나 컴파일러 작성자는 대신 프로그램 크기를 최적화하는 옵션이 있습니다.


2
표준에 따르면 컴파일러는 항상 같은 이름의 외부 함수가 있다고 가정하고 인라인 버전 대신 호출 할 수 있으므로 컴파일러 버그라고 생각하지 않습니다. gcc 4.3, 4.4 및 4.5로 시도했지만 모두 링커 오류가 발생합니다.
Sven Marnach 2011 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.