(VC ++)运行时检查未初始化的变量:测试如何实现?

我试图理解这个测试完全是什么。 这个玩具代码

int _tmain(int argc, _TCHAR* argv[])
{
    int i;
    printf("%d", i);
    return 0;
}

编译成这个:

int _tmain(int argc, _TCHAR* argv[])

{012C2DF0推送ebp
012C2DF1 mov ebp,尤指
012C2DF3亚特,0D8h
012C2DF9推ebx
012C2DFA推esi
012C2DFB推送edi
012C2DFC lea edi,[ebp-0D8h]
012C2E02 mov ecx,36h
012C2E07 mov eax,0CCCCCCCCh
012C2E0C代表字词:[edi]
012C2E0E mov byte ptr [ebp-0D1h],0

int i;
printf("%d", i);

012C2E15 cmp byte ptr [ebp-0D1h],0
012C2E1C jne wmain + 3Bh(012C2E2Bh)
012C2E1E推12C2E5Ch
012C2E23调用__RTC_UninitUse(012C10B9h)
012C2E28添加esp,4
012C2E2B mov esi,esp
012C2E2D mov eax,dword ptr [i]
012C2E30推eax
012C2E31推12C5858h
012C2E36 call dword ptr ds:[12C9114h]
012C2E3C加上esp,8
012C2E3F cmp esi,尤指
012C2E41调用__RTC_CheckEsp(012C1140h)

return 0;

012C2E46 xor eax,eax
} 012C2E48 pop edi
012C2E49 pop esi
012C2E4A流行ebx
012C2E4B添加esp,0D8h
012C2E51 cmp ebp,尤指
012C2E53调用__RTC_CheckEsp(012C1140h)
012C2E58 mov esp,ebp
012C2E5A流行ebp
012C2E5B ret

强调的5行是唯一通过正确初始化变量i而删除的行。 行'push 12C2E5Ch,调用__RTC_UninitUse'调用显示错误框的函数,指向包含变量名称(“i”)的字符串作为参数。

我无法理解的是执行实际测试的3条线:

012C2E0E mov byte ptr [ebp-0D1h],0
012C2E15 cmp byte ptr [ebp-0D1h],0
012C2E1C jne wmain + 3Bh(012C2E2Bh)

这似乎是编译器正在探测我的堆栈区域(将字节设置为零,并立即测试它是否为零),以确保它在构建过程中无法看到的某处无法初始化。 但是,探测到的地址ebp-0D1h与我的实际地址无关。

更糟糕的是,似乎如果有初始化被探测地址的外部(其他线程?)初始化函数初始化为零,那么这个测试仍然会对该变量未初始化发出警告。

这是怎么回事? 也许这个探测针对的是完全不同的东西,比如测试某个字节是否可写?


这是我的猜测:编译器可能会在内存中分配显示变量初始化状态的标志。 在你的情况下,变量i[ebp-0D1h]的单个字节。 这个字节的调零意味着i没有被初始化。 我假设如果你初始化i这个字节将被设置为非零。 尝试一下这样的运行时间: if (argc > 1) i = 1; 这应该生成代码而不是省略整个检查。 你也可以添加另一个变量,看看你是否有两个不同的标志。

在这种情况下,标志的归零和测试恰好是连续的,但情况并非总是如此。


[ebp-0D1h]是编译器使用的一个临时变量,用于跟踪变量的“初始化”状态。 如果我们稍微修改一下源代码,它会更加清晰:

int _tmain(int argc, _TCHAR* argv[])
{
    int i, j;
    printf("%d %d", i, j);
    i = 1;
    printf("%d %d", i, j);
    j = 2;
    return 0;
}

产生以下(跳过不相关的部分):

mov DWORD PTR [ebp-12], -858993460      ; ccccccccH
mov DWORD PTR [ebp-8], -858993460       ; ccccccccH
mov DWORD PTR [ebp-4], -858993460       ; ccccccccH
mov BYTE PTR $T4694[ebp], 0
mov BYTE PTR $T4693[ebp], 0

在序言中,变量用0xCC填充,并且两个跟踪变量(一个用于i ,一个用于j )被设置为0。

; 7    :        printf("%d %d", i, j);    
    cmp BYTE PTR $T4693[ebp], 0
    jne SHORT $LN3@main
    push    OFFSET $LN4@main
    call    __RTC_UninitUse
    add esp, 4
$LN3@main:
    cmp BYTE PTR $T4694[ebp], 0
    jne SHORT $LN5@main
    push    OFFSET $LN6@main
    call    __RTC_UninitUse
    add esp, 4
$LN5@main:
    mov eax, DWORD PTR _j$[ebp]
    push    eax
    mov ecx, DWORD PTR _i$[ebp]
    push    ecx
    push    OFFSET $SG4678
    call    _printf
    add esp, 12                 ; 0000000cH

这大致对应于:

if ( $T4693 == 0 )
  _RTC_UninitUse("j");
if ( $T4694 == 0 )
  _RTC_UninitUse("j");
printf("%d %d", i, j);

下一部分:

; 8    :        i = 1;    
    mov BYTE PTR $T4694[ebp], 1
    mov DWORD PTR _i$[ebp], 1

所以,一旦i初始化,跟踪变量设置为1。

; 10   :        j = 2;
mov BYTE PTR $T4693[ebp], 1
mov DWORD PTR _j$[ebp], 2

在这里, j也是如此。

链接地址: http://www.djcxy.com/p/15231.html

上一篇: (VC++) Runtime Check for Uninitialized Variables: How is the test Implemented?

下一篇: Visual Studio 2010 crashing 10