memory
bug
cppBuilder

C++ Builder XE4 | Memory corruption > bool値がtrueからfalseに変更される要因 > インデックス指定ミス

動作環境
C++ Builder XE4

概要

bool型変数の値をtrueにセットしたのに関わらず、処理の途中でfalseになっている。

調べたところ、配列のインデックス指定ミスだった。

再現code

Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------

static const int kNum = 5;

static void debugPrint(bool bfStart)
{
    if (bfStart) {
        OutputDebugString(L"True");
    } else {

        OutputDebugString(L"False");
    }
}

bool flags1[kNum] = { true, true, true, true, true };
bool bfAfter1 = true;

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    debugPrint(bfAfter1);
    for(int idx=0; idx<kNum; idx++) {
        flags1[kNum] = false;
    }
    debugPrint(bfAfter1);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
    bool bfBefore2 = true;
    bool flags2[kNum] = { true, true, true, true, true };
    bool bfAfter2 = true;

    debugPrint(bfAfter2);
    for(int idx=0; idx<kNum; idx++) {
        flags2[kNum] = false;
    }
    debugPrint(bfAfter2);
}
//---------------------------------------------------------------------------

実行例

Button1押下時にMemory corruptionが起こる (True -> False)。

デバッグ出力: True プロセス Project1.exe (3212)
デバッグ出力: False プロセス Project1.exe (3212)

Button2押下時はMemory corruptionは起きない (True -> True)。

デバッグ出力: True プロセス Project1.exe (3212)
デバッグ出力: True プロセス Project1.exe (3212)

理由

...
    for(int idx=0; idx<kNum; idx++) {
        flags1[kNum] = false;
    }
...
    for(int idx=0; idx<kNum; idx++) {
        flags2[kNum] = false;
    }

上記において、flags1[idx] = false;とするところをflags1[kNum] = false;としていた。
flags1[]とflags2[]はkNumのサイズを定義しているため、配列名[kNum] = false;は「定義外の変数」を変更し、Memory corruptionを生じる。

ただし、Memory corruptionがどのように影響するかは、配列のアドレスによって異なる。

デバッグ > インスペクタでアドレスを確認した。

Button1押下時

  • flags1[]は00406234から5個のアドレスに配置(00406238まで)
  • bfAfter1は00406239に配置

このため、flags1[KNum] = false;はbfAfter1の値をfalseにする (Memory corruption)。

Button2押下時

変数の配置はスタック領域になる (参考: ヒープとスタック)。

  • flags2[]は0012F4C8から5個のアドレスに配置
  • bfAfter2は0012F4C7に配置

このため、flags2[kNum] = false;はbfAfter2の値は変更しない (が、他の変数領域を破壊している)。

修正code

...
    for(int idx=0; idx<kNum; idx++) {
        flags1[idx] = false;
    }
...
    for(int idx=0; idx<kNum; idx++) {
        flags2[idx] = false;
    }