인라인 함수의 정적 변수


85

헤더 파일에 선언되고 정의 된 함수가 있습니다. 이것은 그 자체로 문제입니다. 해당 함수가 인라인되지 않으면 해당 헤더를 사용하는 모든 번역 단위가 함수의 사본을 가져오고 함께 연결될 때 중복됩니다. 함수를 인라인으로 만들어서 "고정"했지만, "인라인"키워드를 지정하더라도 컴파일러가 인라인을 보장하지 않기 때문에 이것이 깨지기 쉬운 솔루션이라는 것이 두렵습니다. 이것이 사실이 아닌 경우 저를 수정하십시오.

어쨌든, 진짜 질문은이 함수 내부의 정적 변수는 어떻게 되나요? 몇 개의 사본으로 끝나나요?

답변:


106

여기에 뭔가 빠진 것 같아요.

정적 기능?

함수를 static으로 선언하면 컴파일 단위에서 "숨겨집니다".

네임 스페이스 범위 (3.3.6)가있는 이름은 다음과 같은 경우 내부 연결이 있습니다.

— 명시 적으로 정적으로 선언 된 변수, 함수 또는 함수 템플릿

3.5 / 3-C ++ 14 (n3797)

이름에 내부 연결이있는 경우 해당 엔터티는 동일한 번역 단위에있는 다른 범위의 이름으로 참조 될 수 있습니다.

3.5 / 2-C ++ 14 (n3797)

이 정적 함수를 헤더에 선언하면이 헤더를 포함한 모든 컴파일 단위에 자체 함수 사본이 있습니다.

문제는 해당 함수 내에 정적 변수가있는 경우이 헤더를 포함하는 각 컴파일 단위도 고유 한 개인 버전을 갖게됩니다.

인라인 기능?

인라인으로 선언하면 인라인 후보가됩니다 (요즘 C ++에서는 컴파일러가 인라인 여부에 관계없이 키워드 인라인이 존재하거나 없다는 사실을 무시하기 때문에 많은 의미가 없습니다).

인라인 지정자가있는 함수 선언 (8.3.5, 9.3, 11.3)은 인라인 함수를 선언합니다. 인라인 지정자는 호출 지점에서 함수 본문의 인라인 대체가 일반적인 함수 호출 메커니즘보다 선호됨을 구현에 나타냅니다. 호출 시점에서이 인라인 대체를 수행하기 위해 구현이 필요하지 않습니다. 그러나이 인라인 대체가 생략 되더라도 7.1.2에 정의 된 인라인 함수에 대한 다른 규칙은 여전히 ​​준수됩니다.

7.1.2 / 2-C ++ 14 (n3797)

헤더에는 흥미로운 부작용이 있습니다. 인라인 함수는 동일한 모듈에서 여러 번 정의 될 수 있으며 링커는 단순히 "그들"을 하나로 결합합니다 (컴파일러의 이유로 인라인되지 않은 경우).

내부에 선언 된 정적 변수의 경우 표준은 특별히 거기에 하나만 명시합니다.

extern 인라인 함수의 정적 지역 변수는 항상 동일한 객체를 참조합니다.

7.1.2 / 4-C ++ 98 / C ++ 14 (n3797)

(함수는 기본적으로 extern이므로 함수를 정적으로 표시하지 않는 한 해당 함수에 적용됩니다)

이것은 결함이없는 "정적"(즉, 헤더에 정의 될 수 있음)의 장점이 있습니다 (인라인되지 않은 경우 최대 한 번만 존재 함).

정적 지역 변수?

정적 지역 변수에는 연결이 없지만 (범위 밖의 이름으로 참조 할 수 없음) 정적 저장 기간이 있습니다 (즉, 전역 적이지만 구성 및 파괴는 특정 규칙을 따릅니다).

정적 + 인라인?

인라인과 정적을 혼합하면 설명한 결과가 나타납니다 (함수가 인라인 된 경우에도 내부 정적 변수는 그렇지 않으며 정적 함수의 정의를 포함하는 컴파일 단위가있는만큼의 정적 변수로 끝납니다. ).

저자의 추가 질문에 대한 답변

질문을 작성한 이후로 Visual Studio 2008에서 시도해 보았습니다. VS가 표준에 따라 작동하도록하는 모든 옵션을 설정하려고했지만 일부를 놓쳤을 가능성이 있습니다. 결과는 다음과 같습니다.

함수가 단순히 "인라인"인 경우 정적 변수의 복사본이 하나만 있습니다.

함수가 "정적 인라인"이면 번역 단위 수만큼 복사본이 있습니다.

진짜 문제는 이제 일이 이런 식으로 이루어져야하는지 아니면 이것이 Microsoft C ++ 컴파일러의 특이한 것인지 여부입니다.

그래서 나는 당신이 다음과 같은 것을 가지고 있다고 가정합니다.

void doSomething()
{
   static int value ;
}

함수 내부의 정적 변수, 간단히 말해서 함수의 범위를 제외한 모든 영역에 숨겨진 전역 변수를 인식해야합니다. 즉, 내부에서 선언 된 함수 만 도달 할 수 있음을 의미합니다.

함수를 인라인해도 아무것도 변경되지 않습니다.

inline void doSomething()
{
   static int value ;
}

숨겨진 전역 변수는 하나만 있습니다. 컴파일러가 코드를 인라인하려고한다고해서 전역 숨겨진 변수가 하나뿐이라는 사실이 바뀌지는 않습니다.

이제 함수가 정적으로 선언 된 경우 :

static void doSomething()
{
   static int value ;
}

그런 다음 각 컴파일 단위에 대해 "비공개"입니다. 즉, 정적 함수가 선언 된 헤더를 포함하는 모든 CPP 파일에는 전역 숨겨진 변수의 자체 사본을 포함하여 함수의 자체 사본이 있으므로 다음과 같은 많은 변수가 있습니다. 헤더를 포함한 컴파일 단위가 있습니다.

내부에 "정적"변수가있는 "정적"함수에 "인라인"추가 :

inline static void doSomething()
{
   static int value ;
}

내부의 정적 변수에 관한 한이 "인라인"키워드를 추가하지 않는 것과 동일한 결과를 가져옵니다.

따라서 VC ++의 동작은 정확하고 "인라인"과 "정적"의 실제 의미를 잘못 이해하고 있습니다.


링크 단계에서 인라인 함수에서 선언 된 모든 정적 변수가 하나로 해결된다는 점에서 언급해야 할 중요한 점이 누락되었다고 생각합니다. 내가 틀렸나 요?
user14416 2016

1
아니요, 각 정적 변수는 자체 별도의 함수 안에 있기 때문입니다. 함수가 동일한 이름을 가지고 있음에도 불구하고 내부 연결이 있으므로 번역 단위간에 공유되지 않습니다.
paercebal

1
@paercebal in inline void doSomething() { static int value ; }, 함수는 외부 연결이 있습니다. 두 개의 다른 단위에서 포함 된 헤더에 표시되는 경우 ODR 위반입니다
MM

@MM 무슨 뜻이야? 귀하의 기능은 inline입니다. ODR을 위반할 수 없습니다.
Ruslan

@Ruslan 그 비 sequitur
MM

39

컴파일러가 변수의 많은 복사본을 생성한다고 생각하지만 링커는 하나를 선택하고 다른 모든 복사본이이를 참조하도록합니다. 다른 버전의 인라인 함수를 만드는 실험을 시도했을 때 비슷한 결과를 얻었습니다. 함수가 실제로 인라인되지 않은 경우 (디버그 모드) 모든 호출은 호출 된 소스 파일에 관계없이 동일한 함수로 이동합니다.

잠시 컴파일러처럼 생각하십시오. 그렇지 않으면 어떻게 될까요? 각 컴파일 단위 (소스 파일)는 서로 독립적이며 별도로 컴파일 할 수 있습니다. 그러므로 각각은 변수의 복사본을 만들어야하며 그것이 유일한 것이라고 생각해야합니다. 링커는 이러한 경계를 넘어서 변수와 함수에 대한 참조를 조정할 수 있습니다.


2
AFAICT, 당신은 여기서 말하는 것이 완벽하게 맞습니다. 사람들이이 답변에 반대하는 이유를 이해할 수 없습니다. 내 유일한 추측은 그들이 "변수의 많은 복사본"까지 읽은 다음 중지하는 것입니다! :( 어쨌든 나에게서 토큰 (+1).
Richard Corden

3
사람들이 컴파일러가 무엇을 의미하는지 물어볼 때 컴파일러 + 링커를 의미합니다. 객체 파일을 실행할 수 없기 때문입니다. 따라서이 대답은 정확하지만 완전히 의미가 없습니다.
Evan Dark

1
왜냐하면 사람들은 무지하기 때문입니다. 이것은 좀 더 진보 된 질문이며 모두가 토론에서 구별을해야합니다.
Sogartar

13

Mark Ransom의 답변이 유용하다는 것을 알았습니다. 컴파일러는 정적 변수의 많은 복사본을 생성하지만 링커는 하나를 선택하여 모든 번역 단위에 적용합니다.

다른 곳에서 나는 이것을 발견했습니다.

[dcl.fct.spec] / 4 참조

[..] 외부 연결이있는 인라인 함수는 모든 번역 단위에서 동일한 주소를 가져야합니다. extern 인라인 함수의 정적 지역 변수는 항상 동일한 객체를 참조합니다. extern 인라인 함수의 문자열 리터럴은 다른 번역 단위의 동일한 객체입니다.

확인할 표준 사본이 없지만 VS Express 2008에서 어셈블리를 검토 한 경험과 일치합니다.


5

이런 식으로되어 있습니다. "정적"은 함수가 컴파일 단위에 로컬이되도록 컴파일러에 알려줍니다. 따라서 컴파일 단위당 복사본 하나와 함수 인스턴스 당 정적 변수 복사본 하나를 원합니다.

"inline"은 함수가 인라인되기를 원하는 컴파일러를 알리는 데 사용됩니다. 요즘에는 "코드의 복사본이 여러 개 있으면 괜찮습니다. 동일한 기능인지 확인하십시오"라고 생각합니다. 그래서 모두가 정적 변수를 공유합니다.

참고 :이 답변은 원래 포스터가 자신에게 게시 한 답변에 대한 응답으로 작성되었습니다.


1
그는 정적 함수의 변수가 아니라 '인라인 함수'의 '정적 변수'에 대해 질문하고 있습니다.
Richard Corden

우리는 그것에 동의하지만 당신의 말이 맞습니다. 답을 맥락에 맞추기 위해서는 편집이 필요합니다.
Raphaël Saint-Pierre

나도 이것을 발견 했다. 그래서 둘 중 어느 것입니까? inline함수가 인라인되도록하거나 여러 복사본이 있어도 괜찮습니까?
Vassilis

@Vassilis는 둘 다 맞지만 인라인을 유발inline 하지는 않지만 단지 제안하고 둘 이상의 정의를 허용합니다 (동일한 컴파일 단위에 포함되지 않음).
Raphaël Saint-Pierre

3

질문을 작성한 이후로 Visual Studio 2008에서 시도해 보았습니다. VS가 표준에 따라 작동하도록하는 모든 옵션을 설정하려고했지만 일부를 놓쳤을 가능성이 있습니다. 결과는 다음과 같습니다.

함수가 단순히 "인라인"인 경우 정적 변수의 복사본이 하나만 있습니다.

함수가 "정적 인라인"이면 번역 단위 수만큼 복사본이 있습니다.

진짜 문제는 이제 일이 이런 식으로 이루어져야하는지 아니면 이것이 Microsoft C ++ 컴파일러의 이데 오동 기성인지 여부입니다.


1
"기능이"정적 인라인 "일 때"-원래 게시물에 그렇게하는 것에 대해 아무 말도하지 않았습니다. 함수의 정적은 변수의 정적과 다른 의미를 갖기 때문에 다른 결과를 기대해야합니다. 함수의 정적은 다른 번역 단위가이 정의를 볼 수 없음을 의미합니다.
Windows 프로그래머

설정이 확실하지 않지만이 경우 컴파일러가 올바르게 작동합니다. 그러나 길을 따라 일부 비준수 컴파일러가 발생할 경우를 대비하여 단위 테스트를 포함 할 수 있습니다.
Robert Gould

-1

인라인이란 실행 코드 (명령)가 호출 함수의 코드에 인라인된다는 것을 의미합니다. 컴파일러는 요청 여부에 관계없이이를 수행하도록 선택할 수 있습니다. 이는 함수에 선언 된 변수 (데이터)에 영향을주지 않습니다.


-2

나는 당신이 번역 단위 당 하나로 끝날 것이라고 믿습니다. 해당 함수 (및 선언 된 정적 변수)의 많은 버전이 헤더를 포함하는 모든 번역 단위에 대해 효과적으로 있습니다.


-2

이 모든 것이 암시 할 수있는 모든 디자인 문제 외에도 이미 그것에 집착했기 때문에이 경우에는 인라인이 아닌 정적을 사용해야합니다. 그렇게하면 모든 사람이 동일한 변수를 공유합니다. (정적 기능)


-2

정적이란 하나의 복사본이 프로그램 전체에 배포된다는 것을 의미하지만 인라인은 동일한 프로그램에서 여러 번 동일한 코드가 필요하다는 것을 의미하므로 인라인 함수 내에서 변수를 정적으로 만들 수 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.