문제

일부를 리팩토링할 때 #defines C++ 헤더 파일에서 다음과 유사한 선언을 발견했습니다.

static const unsigned int VAL = 42;
const unsigned int ANOTHER_VAL = 37;

문제는 정적이 어떤 차이를 만들 것인가 하는 것입니다.클래식으로 인해 헤더를 여러 개 포함할 수 없습니다. #ifndef HEADER #define HEADER #endif 트릭(중요하다면).

정적은 단 하나의 복사본만 의미합니까? VAL 헤더가 둘 이상의 소스 파일에 포함된 경우 생성됩니까?

도움이 되었습니까?

해결책

그만큼 static 사본이 1개 있다는 뜻입니다. VAL 포함된 각 소스 파일에 대해 생성됩니다.그러나 이는 또한 여러 포함이 다음의 여러 정의로 이어지지 않음을 의미합니다. VAL 링크 타임에 충돌합니다.C에서는 static 하나의 소스 파일만 정의되었는지 확인해야 합니다. VAL 다른 소스 파일이 선언하는 동안 extern.일반적으로 소스 파일에 이를 정의하고(아마도 초기화 프로그램을 사용하여) 이를 수행합니다. extern 헤더 파일에 선언.

static 전역 수준의 변수는 포함을 통해 얻었거나 기본 파일에 있었는지 여부에 관계없이 자체 소스 파일에서만 볼 수 있습니다.


편집자 주: C++에서는 const 둘 다 없는 객체 static ...도 아니다 extern 선언의 키워드는 암시적으로 static.

다른 팁

그만큼 static 그리고 extern 파일 범위 변수의 태그는 해당 변수가 다른 번역 단위에서 액세스 가능한지 여부를 결정합니다(예:다른 .c 또는 .cpp 파일).

  • static 변수 내부 연결을 제공하여 다른 번역 단위에서 숨깁니다.그러나 내부 연결이 있는 변수는 여러 번역 단위로 정의할 수 있습니다.

  • extern 변수 외부 연결을 제공하여 다른 번역 단위에서 볼 수 있도록 합니다.일반적으로 이는 변수가 하나의 번역 단위에서만 정의되어야 함을 의미합니다.

기본값(지정하지 않은 경우 static 또는 extern)은 C와 C++가 다른 영역 중 하나입니다.

  • C에서 파일 범위 변수는 다음과 같습니다. extern (외부 연결)이 기본적으로 적용됩니다.C를 사용하는 경우, VAL ~이다 static 그리고 ANOTHER_VAL ~이다 extern.

  • C++에서 파일 범위 변수는 다음과 같습니다. static (내부 연결) 기본적으로 const, 그리고 extern 그렇지 않은 경우 기본적으로.C++를 사용하는 경우 둘 다 VAL 그리고 ANOTHER_VAL ~이다 static.

초안에서 C 사양:

6.2.2 식별자의 연결 ...-5- 함수에 대한 식별자 선언에 스토리지 클래스 지정자가없는 경우, 스토리지 클래스 지정자 외부로 선언 된 것처럼 연결이 결정됩니다.객체에 대한 식별자의 선언에 파일 범위가 있고 스토리지 클래스 지정자가 없으면 연결이 외부입니다.

초안에서 C++ 사양:

7.1.1- 스토리지 클래스 지정자 [dcl.stc] ...-6- 스토리지 클래스 지정자 없이 네임스페이스 범위에서 선언된 이름은 이전 선언으로 인해 내부 연결이 있고 const로 선언되지 않은 경우를 제외하고 외부 연결을 갖습니다.const로 선언되고 extern으로 명시적으로 선언되지 않은 개체에는 내부 연결이 있습니다.

정적이라는 것은 파일당 하나의 사본을 얻는다는 것을 의미하지만 다른 사람들과 달리 그렇게 하는 것은 완전히 합법적입니다.작은 코드 샘플을 사용하여 이를 쉽게 테스트할 수 있습니다.

테스트.h:

static int TEST = 0;
void test();

테스트1.cpp:

#include <iostream>
#include "test.h"

int main(void) {
    std::cout << &TEST << std::endl;
    test();
}

테스트2.cpp:

#include <iostream>
#include "test.h"

void test() {
    std::cout << &TEST << std::endl;
}

이를 실행하면 다음과 같은 출력이 제공됩니다.

0x446020
0x446040

const C++의 변수에는 내부 연결이 있습니다.그래서, static 효과가 없습니다.

아아

const int i = 10;

one.cpp

#include "a.h"

func()
{
   cout << i;
}

two.cpp

#include "a.h"

func1()
{
   cout << i;
}

이것이 C 프로그램이라면 다음에 대해 '다중 정의' 오류가 발생합니다. i (외부 연결로 인해).

이 코드 수준의 정적 선언은 변수가 현재 컴파일 단위에서만 표시됨을 의미합니다.이는 해당 모듈 내의 코드만 해당 변수를 볼 수 있음을 의미합니다.

static 변수를 선언하는 헤더 파일이 있고 해당 헤더가 여러 C/CPP 파일에 포함되어 있는 경우 해당 변수는 해당 모듈에 대해 "로컬"이 됩니다.헤더가 포함된 N 위치에 대해 해당 변수의 N 복사본이 있습니다.그들은 서로 전혀 관련이 없습니다.해당 소스 파일 내의 모든 코드는 해당 모듈 내에서 선언된 변수만 참조합니다.

이 특별한 경우에는 'static' 키워드가 어떤 이점도 제공하지 않는 것 같습니다.뭔가 빠졌을 수도 있지만 그것은 중요하지 않은 것 같습니다. 이전에 이와 같은 작업을 본 적이 없습니다.

인라인의 경우 이 경우 변수는 인라인될 가능성이 높지만 이는 const로 선언되었기 때문입니다.컴파일러 ~할 것 같다 모듈 정적 변수를 인라인할 가능성이 더 높지만 이는 상황과 컴파일되는 코드에 따라 다릅니다.컴파일러가 '정적'을 인라인한다는 보장은 없습니다.

C 책(무료 온라인)에는 '정적'의 의미를 더 자세히 설명하는 연결에 관한 장이 있습니다(정답은 이미 다른 의견에 제공되어 있지만).http://publications.gbdirect.co.uk/c_book/chapter4/linkage.html

질문에 대답하려면 "정적은 헤더가 둘 이상의 소스 파일에 포함된 경우 VAL의 복사본이 하나만 생성된다는 의미입니까?"...

아니요.VAL은 항상 헤더를 포함하는 모든 파일에서 별도로 정의됩니다.

이 경우 C 및 C++ 표준에 따라 차이가 발생합니다.

C에서 파일 범위 변수는 기본적으로 extern입니다.C를 사용하는 경우 VAL은 정적이고 ANOTHER_VAL은 extern입니다.

최신 링커는 헤더가 다른 파일에 포함된 경우(동일한 전역 이름이 두 번 정의된 경우) ANOTHER_VAL에 대해 불평할 수 있으며, ANOTHER_VAL이 다른 파일에서 다른 값으로 초기화되면 확실히 불평할 것입니다.

C++에서 파일 범위 변수는 const인 경우 기본적으로 static이고, 그렇지 않은 경우 기본적으로 extern입니다.C++를 사용하는 경우 VAL과 ANOTHER_VAL은 모두 정적입니다.

또한 두 변수가 모두 const로 지정된다는 사실도 고려해야 합니다.이상적으로 컴파일러는 항상 이러한 변수를 인라인하고 해당 변수에 대한 저장소를 포함하지 않도록 선택합니다.스토리지를 할당할 수 있는 데에는 여러 가지 이유가 있습니다.내가 생각할 수 있는 것들은...

  • 디버그 옵션
  • 파일에 포함된 주소
  • 컴파일러는 항상 저장소를 할당합니다(복잡한 const 유형은 쉽게 인라인될 수 없으므로 기본 유형의 특별한 경우가 됩니다).

정의하지 않고는 정적 변수를 선언할 수 없습니다(그 이유는 스토리지 클래스 수정자 static 및 extern이 상호 배타적이기 때문입니다).정적 변수는 헤더 파일에 정의될 수 있지만 이렇게 하면 헤더 파일을 포함하는 각 소스 파일이 변수의 고유한 개인 복사본을 가지게 되며 이는 아마도 의도한 것이 아닐 것입니다.

이러한 선언이 전역 범위에 있다고 가정합니다(예:멤버 변수가 아님) 다음을 수행합니다.

공전 '내부 연결'을 의미합니다.이 경우에는 선언되므로 const 이는 컴파일러에 의해 최적화/인라인될 수 있습니다.을 생략하는 경우 const 그러면 컴파일러는 각 컴파일 단위에 저장소를 할당해야 합니다.

생략함으로써 공전 연결은 통근자 기본적으로.이번에도 당신은 구원받았습니다. constness - 컴파일러는 사용을 최적화/인라인할 수 있습니다.떨어뜨리면 const 그러면 당신은 정의된 기호 곱하기 링크 타임에 오류가 발생했습니다.

const C++에서는 변수가 기본적으로 정적이지만 extern C에서는 변수가 정적입니다.따라서 C++를 사용하는 경우 어떤 구성을 사용해야 하는지 알 수 없습니다.

(7.11.6 C++ 2003 및 Apexndix C에는 샘플이 있음)

C 및 C++ 프로그램으로 소스를 컴파일/링크 비교하는 예:

bruziuz:~/test$ cat a.c
const int b = 22;
int main(){return 0;}
bruziuz:~/test$ cat b.c
const int b=2;
bruziuz:~/test$ gcc -x c -std=c89 a.c b.c
/tmp/ccSKKIRZ.o:(.rodata+0x0): multiple definition of `b'
/tmp/ccDSd0V3.o:(.rodata+0x0): first defined here
collect2: error: ld returned 1 exit status
bruziuz:~/test$ gcc -x c++ -std=c++03 a.c b.c 
bruziuz:~/test$ 
bruziuz:~/test$ gcc --version | head -n1
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609

Static은 다른 컴파일 단위가 해당 변수를 외부로 가져오는 것을 방지하므로 컴파일러는 변수 값이 사용되는 위치에 "인라인"할 수 있고 해당 변수에 대한 메모리 저장소를 생성하지 않을 수 있습니다.

두 번째 예에서 컴파일러는 다른 소스 파일이 이를 확장하지 않을 것이라고 가정할 수 없으므로 실제로 해당 값을 메모리 어딘가에 저장해야 합니다.

Static은 컴파일러가 여러 인스턴스를 추가하는 것을 방지합니다.#ifndef 보호에서는 이는 덜 중요하지만 헤더가 두 개의 별도 라이브러리에 포함되어 있고 애플리케이션이 링크되어 있다고 가정하면 두 개의 인스턴스가 포함됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top