컴파일러가 분명히 초기화되지 않은 변수를 감지하지 못함
내가 시도한 모든 C 컴파일러는 아래 코드 조각에서 초기화되지 않은 변수를 감지하지 못합니다. 그러나 여기서는 분명합니다.
이 스 니펫의 기능에 대해 신경 쓰지 마십시오. 실제 코드가 아니며이 문제를 조사하기 위해 제거했습니다.
BOOL NearEqual (int tauxprecis, int max, int value)
{
int tauxtrouve; // Not initialized at this point
int totaldiff; // Not initialized at this point
for (int i = 0; i < max; i++)
{
if (2 < totaldiff) // At this point totaldiff is not initialized
{
totaldiff = 2;
tauxtrouve = value; // Commenting this line out will produce warning
}
}
return tauxtrouve == tauxprecis ; // At this point tauxtrouve is potentially
// not initialized.
}
내가 주석 다른 한편, 만약 tauxtrouve = value ;
, 내가 얻을 "local variable 'tauxtrouve' used without having been initialized"
경고.
이 컴파일러를 시도했습니다.
- -Wall -WExtra가있는 GCC 4.9.2
- 모든 경고가 활성화 된 Microsoft Visual C ++ 2013
이 변수가 초기화되지 않은 명확성은 과장되어 있습니다. 경로 분석에는 시간이 소요되고 컴파일러 공급 업체는 기능을 구현하기를 원하지 않거나 너무 많은 시간이 소요될 것이라고 생각했거나 명시 적으로 옵트 인하지 않았습니다.
예를 들면 다음과 clang
같습니다.
$ clang -Wall -Wextra -c obvious.c
$ clang -Wall -Wextra --analyze -c obvious.c
obvious.c:9:11: warning: The right operand of '<' is a garbage value
if (2 < totaldiff) // at this point totaldiff is not initialized
^ ~~~~~~~~~
obvious.c:16:21: warning: The left operand of '==' is a garbage value
return tauxtrouve == tauxprecis ; // at this point tauxtrouve is potentially
~~~~~~~~~~ ^
2 warnings generated.
이러한 순진한 예제의 실행 시간 차이는 무시할 수 있습니다. 그러나 각각 루프와 무거운 중첩이있는 수천 개의 행, 수십 개의 함수가있는 변환 단위를 상상해보십시오. 경로의 수는 빠르게 합성되고 루프를 통한 첫 번째 반복이 해당 비교 전에 할당이 발생하는지 여부를 분석하는 데 큰 부담이됩니다.
편집 : @Matthieu는 LLVM / clang을 사용하면 초기화되지 않은 값을 찾는 데 필요한 경로 분석이 IR에서 사용되는 SSA 표기법으로 인해 중첩이 증가함에 따라 복합적이지 않다고 지적합니다.
-S -emit-llvm
내가 바랬던 것처럼 " " 만큼 간단 하지는 않았지만 그가 설명한 SSA 표기법 출력을 찾았습니다. 솔직히 말해서 LLVM IR에 대해 충분히 잘 모르지만 Matthieu의 말을 받아 들일 것입니다.
결론 : clang
와 함께 사용 --analyze
하거나 누군가에게 gcc
버그 를 수정하도록 설득하세요 .
; Function Attrs: nounwind uwtable
define i32 @NearEqual(i32 %tauxprecis, i32 %max, i32 %value) #0 {
br label %1
; <label>:1 ; preds = %7, %0
%tauxtrouve.0 = phi i32 [ undef, %0 ], [ %tauxtrouve.1, %7 ]
%i.0 = phi i32 [ 0, %0 ], [ %8, %7 ]
%2 = icmp slt i32 %i.0, %max
br i1 %2, label %3, label %9
; <label>:3 ; preds = %1
%4 = icmp slt i32 2, 2
br i1 %4, label %5, label %6
; <label>:5 ; preds = %3
br label %6
; <label>:6 ; preds = %5, %3
%tauxtrouve.1 = phi i32 [ %value, %5 ], [ %tauxtrouve.0, %3 ]
br label %7
; <label>:7 ; preds = %6
%8 = add nsw i32 %i.0, 1
br label %1
; <label>:9 ; preds = %1
%10 = icmp eq i32 %tauxtrouve.0, %tauxprecis
%11 = zext i1 %10 to i32
ret i32 %11
}
예, 초기화되지 않은 변수에 대한 경고를 표시해야하지만 GCC 버그 입니다. 주어진 예는 다음과 같습니다.
unsigned bmp_iter_set ();
int something (void);
void bitmap_print_value_set (void)
{
unsigned first;
for (; bmp_iter_set (); )
{
if (!first)
something ();
first = 0;
}
}
그리고 -O2 -W -Wall
.
안타깝게도 올해는이 버그의 10 주년입니다!
이 답변은 GCC에만 적용됩니다.
추가 조사와 의견을 말한 후 이전 답변보다 더 많은 일이 진행되고 있습니다. 이 코드 조각에는 초기화되지 않은 두 개의 변수가 있으며 각 변수는 다른 이유로 감지되지 않습니다.
우선 옵션에 대한 GCC 문서 에 다음과 -Wuninitialized
같이 나와 있습니다.
Because these warnings depend on optimization, the exact variables or elements for which there are warnings depends on the precise optimization options and version of GCC used.
Previous versions of the GCC manual worded this more explicitly. Here's an excerpt from the manual for GCC 3.3.6:
These warnings are possible only in optimizing compilation, because they require data flow information that is computed only when optimizing. If you don't specify -O, you simply won't get these warnings.
It seems the current version may give some warnings without uninitialized variables without -O
, but you still get much better results with it.
If I compile your example using gcc -std=c99 -Wall -O
, I get:
foo.c: In function ‘NearEqual’:
foo.c:15:21: warning: ‘tauxtrouve’ is used uninitialized in this function [-Wuninitialized]
return tauxtrouve == tauxprecis ; // at this point tauxtrouve is potentially
^
(Note this is with GCC 4.8.2 as I don't have 4.9.x installed, but the principle should be the same.)
So that detects the fact that tauxtrouve
is uninitialized.
However, if we partially fix the code by adding an initializer for tauxtrouve
(but not for totaldiff
), then gcc -std=c99 -Wall -O
accepts it without any warnings. This would appear to be an instance of the "bug" cited in haccks's answer.
There is some question as to whether this should really be considered a bug: GCC doesn't promise to catch every possible instance of an uninitialized variable. Indeed, it can't do so with perfect accuracy, because that's the halting problem. So warnings like this can be helpful when they work, but the absence of warnings does not prove that your code is free of uninitialized variables! They are really not a substitute for carefully checking your own code.
In the bug report linked by haccks, there is much discussion as to whether the bug is even fixable, or whether trying to detect this particular construct would result in an unacceptable false positive rate for other correct code.
Michael, I don't know which version of Visual Studio 2013 you tried this on, but it is most certainly outdated. Visual Studio 2013 Update 4 correctly produces the following error message on the first use of totaldiff
:
error C4700: uninitialized local variable 'totaldiff' used
You should consider updating your work environment.
By the way, here is what I see directly in the editor:
참고URL : https://stackoverflow.com/questions/27063678/compiler-not-detecting-obviously-uninitialized-variable
'Program Tip' 카테고리의 다른 글
장고 쿼리는 마지막 n 레코드를 가져옵니다. (0) | 2020.12.07 |
---|---|
Sql Server 2008에서 테이블 변수 자르기 / 지우기 (0) | 2020.12.07 |
MS-SQL Server에서 별칭이 지정된 열에 대해 GROUP BY를 수행하려면 어떻게합니까? (0) | 2020.12.07 |
파이썬에서 __main__ 모듈의 파일 이름을 얻는 방법은 무엇입니까? (0) | 2020.12.07 |
jquery에서 양식 데이터를 객체로 얻는 방법 (0) | 2020.12.07 |