【问题标题】:Values of a vector are changing when they shouldn't be向量的值在不应该发生变化时发生变化
【发布时间】:2018-06-02 06:17:53
【问题描述】:

我正在从头开始编写游戏引擎,作为一项空闲时间的学习练习。我目前正在实现一个渲染队列,但负责队列的向量的值不断变化。 (当它应该是 10.0f 时,总是与 -107374176 相同)向量 objRID 的类型为 OBJR*,其中 OBJR 是一个包含位置信息的结构,以及指向位图的指针。我使用的位图库似乎不是罪魁祸首,但可以在:http://partow.net/programming/bitmap/index.html 找到。

总体异常是 0x1CCCCCCCC 的读取访问冲突。我已经逐步完成了该程序,并发现结构的值在第 19 次迭代后的“rep stos”的每次迭代中都会发生变化。关于“rep stos”如何影响看似无关的事情,我没有真正的想法。 (A 首先对汇编程序没有很好的了解)除了手头的错误之外,我非常愿意接受建议。

如果有人可以解释以下程序集如何影响向量 objRID,我想我将来可以自己解决这个问题。

163: int loop()
164: {
00007FF7AC74D580 40 55                push        rbp  
00007FF7AC74D582 57                   push        rdi  
00007FF7AC74D583 48 81 EC A8 01 00 00 sub         rsp,1A8h  
00007FF7AC74D58A 48 8D 6C 24 20       lea         rbp,[rsp+20h]  
00007FF7AC74D58F 48 8B FC             mov         rdi,rsp  
00007FF7AC74D592 B9 6A 00 00 00       mov         ecx,6Ah  
00007FF7AC74D597 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
00007FF7AC74D59C F3 AB                rep stos    dword ptr [rdi] <---- 19th - 26th iteration here

我不想把整个程序都扔在这里,但我相信这样会少很多混乱。

程序的结构如下:

#include "stdafx.h"
#include <Windows.h>
#include "bitmap_image.hpp"
#define maxObjects 1024




struct VEC2_f {
    float x, y;

    VEC2_f(float x, float y)
    {
        VEC2_f::x = x;
        VEC2_f::y = y;
    }

    VEC2_f()
    {
        VEC2_f::x = 0.0f;
        VEC2_f::y = 0.0f;
    }
};

struct OBJR {
    VEC2_f pos, vel;
    int ID = -1;
    bitmap_image* Lbmp;

    OBJR(bitmap_image* Lbmp, VEC2_f pos, VEC2_f vel)
    {
        OBJR::Lbmp = Lbmp;
        OBJR::pos = pos;
        OBJR::vel = vel;
    }

    OBJR(bitmap_image* Lbmp, float x, float y, float vx, float vy)
    {
        OBJR::Lbmp = Lbmp;
        OBJR::pos = VEC2_f(x, y);
        OBJR::vel = VEC2_f(vx, vy);
    }

    //if -1 then ID isn't set yet
    int getID()
    {
        return ID;
    }
};



std::vector<OBJR*> objRID;
int IDCOUNTER = 0;
bool running = true;
HWND con;
HDC dc;
COLORREF color;



void objInit(OBJR* Lobj)
{
    if (objRID.size() > maxObjects)
    {
        objRID.pop_back();
        Lobj->ID = maxObjects;          }
    Lobj->ID = IDCOUNTER++;
    objRID.push_back(Lobj);
}

void input()
{

}

void update()
{

}

VEC2_f interpolate(float interpolation, VEC2_f pos, VEC2_f vel)
{
    return VEC2_f(pos.x + (vel.x * interpolation), pos.y + (vel.y * interpolation));
}




void renderBitmap(bitmap_image* Lbmp, VEC2_f Ipos)
{
    unsigned int h, w;
    rgb_t colorT;
    h = Lbmp->height();  <--- Read access violation here
    w = Lbmp->width();

    for (unsigned int y = 0; y < h; y++)
    {
        for (unsigned int x = 0; x < w; x++)
        {
            colorT = Lbmp->get_pixel(x, y);
            color = RGB(colorT.red, colorT.green, colorT.blue);
            SetPixelV(dc, x + Ipos.x, y + Ipos.y, color);
        }
    }
}

void renderOBJR(float interpolation, OBJR* obj)
{
    renderBitmap(obj->Lbmp, interpolate(interpolation, obj->pos, obj->vel));
}


void render(float interpolation)
{
    for (int i = 0; i < objRID.size(); i++)
    {
        renderOBJR(interpolation, objRID[i]);
    }
}


void resizeWindow()
{
    RECT r;
    GetWindowRect(con, &r);
    MoveWindow(con, r.left, r.top, 800, 600, true);
}


int init()
{
    con = GetConsoleWindow();
    dc = GetDC(con);
    resizeWindow(); 
    return 0;
}


int loop()
{  //<--- this is where the disassembly was taken from and is where the Lbmp becomes invalid
    const int TPS = 60;
    const int SKIP_TICKS = 1000 / TPS;
    const int FRAMESKIP = 1;

    DWORD next_tick = GetTickCount();
    float interpolation;
    int loop;

    while (running)
    {
        loop = 0;
        while (GetTickCount() > next_tick && loop < FRAMESKIP)
        {
            input();
            update();

            next_tick += SKIP_TICKS;
            loop++;
        }
        interpolation = float(GetTickCount() + SKIP_TICKS - next_tick) / float(SKIP_TICKS);
        render(interpolation);

    }
    return 0;
}

int deInit()
{

    ReleaseDC(con, dc);
    return 0;
}



void test() 
{
    bitmap_image bitmap = bitmap_image("testBW.bmp");
    VEC2_f pos = VEC2_f(10.f, 10.f);
    VEC2_f vel = VEC2_f();
    OBJR test1 = OBJR(&bitmap, pos, vel);
    objInit(&test1);
    renderBitmap(&bitmap, pos);

}

int main()
{

    init();
    test();
    loop();
    deInit();
    return 0;
}

【问题讨论】:

  • stosd 将 32 位写入内存。所以它几乎可以改变任何你指向它的东西(通过将“它的”地址加载到rdi)。所以stos 和许多其他指令高度相关,不幸的是,必须了解代码的逻辑才能识别实际问题(还没有研究它,只是解释为什么单个错误stos 可能对任何事情都是致命的)。
  • 使用调试器不是比看汇编更容易吗?您可以设置一个观察点(在写入特定内存时中断)并查看您是否不小心写入了向量。
  • rep stosd 是实现 memset 的一种方式。一些编译器会内联它。看起来您的编译器正在使用它来初始化堆栈内存,其毒性值为0xCCCCCCCC,用于4 * 0x6A 字节。
  • 您是否知道test() 在临时test1 对象处提供objInit 本地指针,该对象在从test() 函数返回时释放,即使用该指针进行的任何其他操作都是UB。 (bitmp 也是如此,因为您只保留已释放的指针,因此您不应在 test() 之外访问它)
  • 您正在存储指向局部变量的指针。在函数返回后取消引用它们是未定义的。

标签: c++ assembly memory 64-bit game-engine


【解决方案1】:

rep stosd 是实现 memset 的一种方式。一些编译器会内联它。

看起来您的编译器正在使用它来初始化堆栈内存,其毒性值为0xCCCCCCCC,用于4 * 0x6A 字节。 (注意前面有一个mov rdi, rsp。)我认为这是一个调试版本,因为优化的版本不会做额外的工作。

总体异常是 0x1CCCCCCCC 的读取访问冲突。

看起来你从一个未初始化的对象中读取了一个指针,并且“毒药”值完成了创建一个你可以很容易看到的指针值的工作是虚假的,并且哪些错误而不是默默地继续直到稍后的一些错误远离真正的问题。

指针高半部分的0x00000001 可疑。也许你部分覆盖了这个对象?在存储指针值的内存上设置一个观察点,并找出 else 对其进行了哪些修改。


或者在局部变量超出范围后保留指向它们的指针,然后下一个函数调用将堆栈内存重新用于其堆栈帧。调试模式代码毒化了整个堆栈帧,覆盖了std::vector&lt;OBJR*&gt; objRID; 指向的一些对象。

所以变化的不是向量内容或向量对象本身,而是向量内容指向的对象。

同样,0xCCCCCCCC 毒药正在寻找程序中的错误。

(@molbdnilo 发现了这一点。我第一次没有花时间仔细阅读你的整个 C++ 代码。)

【讨论】:

  • 我现在通过简单地使变量全局化来解决这个问题,但我不相信迭代器的潜在问题是固定的。我将设置一个观察点,看看是否有任何东西修改了内存地址。感谢您的帮助。
猜你喜欢
  • 2022-07-29
  • 2014-11-29
  • 1970-01-01
  • 2014-03-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-28
  • 2017-05-09
  • 1970-01-01
相关资源
最近更新 更多