내보낸 일부 const 변수를 참조하는 일부 const 변수가 값 0을 얻는 이유는 무엇입니까?
-
06-09-2019 - |
문제
다음을 고려하세요.다음과 같이 두 개의 내보낸 상수가 있습니다.
// somefile.h
extern const double cMyConstDouble;
extern const double cMyConstDouble2;
그리고
// somefile.cpp
const double cMyConstDouble = 3.14;
const double cMyConstDouble2 = 2.5*cMyConstDouble;
이제 이러한 상수는 두 개의 정적(로컬로 표시되는) 상수를 정의하기 위해 다른 곳에서 참조됩니다.
// someotherfile.cpp
#include "somefile.h"
static const double cAnotherDouble = 1.1*cMyConstDouble;
static const double cAnotherDouble2 = 1.1*cMyConstDouble2;
printf("cAnotherDouble = %g, cAnotherDouble2 = %g\n",
cAnotherDouble, cAnotherDouble2);
결과는 다음과 같습니다.
cAnotherDouble = 3.454, cAnotherDouble2 = 0
왜 두 번째 double은 0입니까?저는 .NET 2003 C++ 컴파일러(13.10.3077)를 사용하고 있습니다.
해결책
CMYCONSTDOUBLE은 외부로 선언되므로 컴파일러는 그 값을 가정 할 수 없으며 CMYCONSTDOUBLE2의 컴파일 시간 초기화를 생성하지 않습니다. cmyconstdouble2가 컴파일 시간 초기화되지 않으므로 CanotherDouble2에 대한 초기화 순서는 무작위 (정의되지 않은)입니다. 보다 정적 초기화 치실 자세한 내용은.
다른 팁
나는 여기에서 내 발가락을 extern 문제에 담그지 않을 것이지만, 왜 단순히 구성원을 적절한 헤더 파일에 배치하지 않고 외부를 사용하여 "내보내기"를 잊어 버리나요? 이것이 C ++에서 Ocsts를 사용하는 방법과 내부 연계가있는 이유입니다.
다시 말해:
// someheader.h
const double cMyConstDouble = 3.14;
const double cMyConstDouble2 = 2.5*cMyConstDouble;
그리고 필요한 파일을 필요로하십시오.
한 소스 파일의 하나의 정적 변수가 다른 CPP 파일의 다른 정적 변수에 의존하기 때문에 이것은 위험합니다. 확인하다 정적 초기화 치실 자세한 내용은.
초기화를 변경하면 cMyConstDouble2
여기에:
const double cMyConstDouble2 = 2.5*3.14;
그러면 프로그램이 올바르게 작동해야 합니다.그 이유는 변수가
- POD 유형 있음
- 상수 표현식으로 초기화됩니다. (1)
정적 초기화 시간에 초기화됩니다.이러한 초기화에는 다음이 포함됩니다.
- 제로 초기화 모두 정적 저장 기간을 갖는 객체
- 상수 표현식으로 초기화된 POD 초기화
표시된 변수 중 cMyConstDouble
정적 초기화 시점에 완전히 초기화된다는 두 가지 조건을 모두 만족합니다.하지만, cMyConstDouble2
초기화가 상수 표현식의 요구 사항을 충족하지 않기 때문에 그렇지 않습니다.특히 정수형이 아닌 변수(여기서는 부동소수점형)가 포함되어 있습니다.그러나 부동소수점 리터럴 ~이다 산술 상수 표현식에 허용됩니다.그렇기 때문에 2.5*3.14
산술 상수 표현입니다.이것이 바로 초기화 프로그램을 변경하면 정적으로 초기화되어야 하는 이유입니다.
무슨 일이 일어날 것인가? cMyConstDouble2
상수가 아닌 표현을 유지한다면?대답은 '당신은 모른다'입니다.표준에서는 해당 변수를 정적으로 초기화하는 것을 허용하지만 그렇게 할 것을 요구하지는 않습니다.귀하의 경우 동적으로 초기화되었으므로 정적 초기화 시간 직후의 값은 여전히 0입니다.방법에 대한 느낌을 얻으려면 복잡한 즉, 다음은 예입니다.
inline double fd() { return 1.0; }
extern double d1;
double d2 = d1; // unspecified:
// may be statically initialized to 0.0 or
// dynamically initialized to 1.0
double d1 = fd(); // may be initialized statically to 1.0
동적 초기화가 다른 정적 저장 변수를 변경하지 않는 경우(다음에서 충족됨) 당신의 코드) 그리고 정적 초기화가 정적 초기화가 필요하지 않은 모든 개체가 동적으로 초기화될 때 동적 초기화에 의해 생성되는 것과 동일한 값을 생성하는 경우(또한 당신의 코드) - 그러면 변수가 정적으로 초기화될 수 있습니다.이 두 조건은 두 변수에 대한 위의 코드에서도 충족됩니다. d2
그리고 d1
:
분석 d2
= d1
다른 정적 저장 변수는 변경되지 않습니다.- 둘 다일 때
d2
그리고d1
동적으로 초기화된 다음d2
다음과 같이 초기화됩니다.0.0
, 왜냐하면d2
이전에 정의되었습니다d1
, 및 동적 초기화d2
의 가치를 잡을 것이다d1
정적 초기화 직후 상태 기준(제로 초기화만d1
일어났습니다).
분석 d1
= fd()
다른 정적 저장 변수는 변경되지 않습니다.- 둘 다일 때
d2
그리고d1
동적으로 초기화된 다음= fd()
초기화됩니다d1
에게1.0
.
따라서 컴파일러는 초기화할 수 있습니다. d1
정적으로 1.0
, 선택적 정적 초기화에 대한 두 조건이 모두 충족되기 때문입니다.
만약에 컴파일러는 초기화를 결정합니다
d1
그리고d2
동적으로d2
로 초기화됩니다0.0
, 왜냐하면 그것은d1
제로 초기화 직후였습니다.하지만, 만약에 컴파일러는 초기화를 결정합니다
d1
정적으로 그리고d2
동적으로d2
로 초기화됩니다1.0
, 동적 초기화 이후d2
완전히 초기화된 값을 가져옵니다.d1
정적 초기화 직후였습니다.
그 가치가 무엇인지 잘 모르겠습니다. d2
언제인가? d1
그리고 d2
그러나 정적으로 초기화됩니다.즉, 여부 d2
잡아야 할 것 같다. 0.0
아니면 그 1.0
, 정적 초기화에는 정의된 순서가 없기 때문입니다.
(1) 상수 표현식에는 정적 저장 기간이 있는 객체의 초기화 순서를 고려할 때 산술 상수 표현식(적분 상수 표현식뿐만 아니라)도 포함됩니다.