STL List复制一个struct,但是复制的值偏移了两个内存地址

发布于 2024-09-08 12:59:14 字数 5072 浏览 5 评论 0 原文

我正在使用 MinGW 编译器在 Windows 7 上使用 Code::Blocks 进行编译(我只能假设它是最新版本;Code::Blocks 和 MinGW 都是在上周安装的)。我的问题是在特定情况下出现的,我尝试编写一个更简单的脚本来演示该问题失败了(这意味着我的结构有问题)。另外,我对这篇文章的长度表示歉意。

目前,我正在使用一个类 FXSDL,它将充当 SDL 包装器:

class FXSDL
{
    public:
        FXSDL();
        virtual ~FXSDL();
        int Initialize();
        int Render();

        FXID CreateCharacter(FXID hRefID, string fpImage, int wpxTile, int hpxTile, map<int, vector<int> > htAnims);
        int SetAnim(FXID hRefID, FXID hAnimID);
        FXID hPlayer;
    protected:
    private:
        list<FXSurface> m_lstFXObjects;
        list<FXSurface>::iterator m_liFirst;
        SDL_Surface* m_lpsfSDLScreen;
        Uint32 m_tmOld;
        Uint32 m_tmFrame;
};

我的列表的值类型是:

struct FXSurface
{
    FXID hRefID;
    int wpxTile;
    int hpxTile;
    int wpxTotal;
    int hpxTotal;
    int cntTiles;

    map<int, vector<int> > htAnims; // All animations
    map<int, vector<int> >::iterator vCurr; // Currently active animation
    vector<int>::iterator fiCurr; // Currently active frame
    SDL_Surface* lpsfSDL;
    SDL_Rect* lprcTiles;    // Predefined frame positions
    string* fpImage;
};

我已经实现了非常简单的初始化和渲染函数。 CreateCharacter 函数需要几个参数,其中最重要的是 htAnims,一个整数向量映射(想法是:我用易于记忆的表示形式定义数字 id,例如 FXA_IDLE 或 FXA_WALK,作为键,系列将动画帧表示为向量的数值)。这可以相当容易地实现为多维整数数组,但动画的长度是可变的,我希望能够添加新的动画(或重新定义现有的动画),而不必重新转换数组。

CreateCharacter 函数很简单。它创建一个新的 FXSurface,用所需的数据填充它,并将新的 FXSurface 推送到列表中:

FXID FXSDL::CreateCharacter(FXID hRefID, string fpImage, int wpxTile, int hpxTile, map<int, vector<int> > htAnims)
{
    //list<FXSurface>::iterator lpsfTemp;
    FXSurface lpsfTemp;
    list<FXSurface>::iterator lpsfPos;
    SDL_Rect* lprcCurr = NULL;
    int cntTileW = 0;
    int cntTileH = 0;
    int cntCurr = 0;

    // Start off by initializing our container struct
    //lpsfTemp = new FXSurface();
    lpsfTemp.lpsfSDL = IMG_Load(fpImage.c_str()); // Try to load the requested image
    if(lpsfTemp.lpsfSDL != NULL) // If we didn't fail to
    {
        // Assign some variables for tracking
        lpsfTemp.hRefID = hRefID;
        lpsfTemp.fpImage = &fpImage;
        lpsfTemp.wpxTotal = lpsfTemp.lpsfSDL->w;
        lpsfTemp.hpxTotal = lpsfTemp.lpsfSDL->h;

        // If a tile width was specified, use it
        if(wpxTile != 0)
        {
            lpsfTemp.wpxTile = wpxTile;
            lpsfTemp.hpxTile = hpxTile;
        } // Otherwise, assume one tile
        else
        {
            lpsfTemp.wpxTile = lpsfTemp.wpxTotal;
            lpsfTemp.hpxTile = lpsfTemp.hpxTotal;
        }

        // Determine the tiles per row and column for later
        cntTileW = lpsfTemp.wpxTotal / lpsfTemp.wpxTile;
        cntTileH = lpsfTemp.hpxTotal / lpsfTemp.hpxTile;

        // And the total number of tiles
        lpsfTemp.cntTiles = cntTileW * cntTileH;
        lpsfTemp.lprcTiles = new SDL_Rect[cntTileW*cntTileH];

        // So we don't calculate this every time, determine each frame's coordinates and store them
        for(int h = 0; h < cntTileH; h++)
        {
            for(int w = 0; w < cntTileW; w++)
            {
                cntCurr = (h*cntTileW)+w;
                lprcCurr = new SDL_Rect;
                lprcCurr->w = lpsfTemp.wpxTile;
                lprcCurr->h = lpsfTemp.hpxTile;
                lprcCurr->x = w*lpsfTemp.wpxTile;
                lprcCurr->y = h*lpsfTemp.hpxTile;

                lpsfTemp.lprcTiles[cntCurr] = *lprcCurr;
                lprcCurr = NULL;
            }
        }

        // Now acquire our list of animations and set the default
        //lpsfTemp.htAnims = new map<int, vector<int> >(*htAnims);
        lpsfTemp.htAnims = htAnims;
        lpsfTemp.vCurr = lpsfTemp.htAnims.find(FXA_WALK_EAST);
        lpsfTemp.fiCurr = lpsfTemp.vCurr->second.begin();

        this->m_lstFXObjects.push_back(lpsfTemp);
    }
    else
    {
        hRefID = 0;
    }

    return hRefID;
}

正是在推送对象时发生了错误。我已经多次单步执行代码了。最初,我只能知道我的迭代器无法取消对 FXSurface 对象的引用。在使用监视来识别迭代器和列表对象指向的确切内存地址并取消引用该地址之后,我注意到了段错误的原因:当列表时,我放入原始 FXSurface 中的所有值都被推入了两个内存块对象复制了它!

我这样做的过程很简单。我在 CreateCharacter 的 return 语句处设置了一个断点,这为我提供了 lpsfTemp(我稍后添加到列表中的 FXSurface)和 m_lstFXObjects(我将其添加到的列表)的视图。我滚动浏览 m_lstFXObjects 的成员,这将我带到 _M_node,其中包含迄今为止我添加的唯一对象的内存地址。我以 (FXSurface) 的形式向这个地址添加一个监视 - 这里是十六进制地址 -

首先,找到地址:(

这里应该有一张图片显示突出显示的 _M_node 属性,其中包含列表项的地址,但我无法发布图片,而且我只能发布一个网址,第二个网址位于 http://www.fauxsoup.net/so/address.jpg)

接下来,我们投射并引用地址。该图显示了 lpsfTemp 和 m_lstFXObjects 中的副本;注意到差异了吗?

http://www.fauxsoup.net/so/dereferenced.jpg - 看到了吗?所有值的顺序都正确,只是偏移了两个列表,

我最初将 fpImages 存储为 char*,所以我认为这可能会导致问题,但现在它只是一个指针,问题仍然存在。也许这是由于 map 造成的。 >我储存?

I'm compiling using Code::Blocks on Windows 7 using the MinGW compiler (which I can only assume is the latest version; both Code::Blocks and MinGW were installed this past week). My issue crops up under a particular circumstance, and my attempts to write a simpler script that demonstrates the problem have failed (which implies that there is something wrong with my structure). Also, my apologies for how long this post is.

Currently, I'm rolling with one class, FXSDL, which will act as an SDL wrapper:

class FXSDL
{
    public:
        FXSDL();
        virtual ~FXSDL();
        int Initialize();
        int Render();

        FXID CreateCharacter(FXID hRefID, string fpImage, int wpxTile, int hpxTile, map<int, vector<int> > htAnims);
        int SetAnim(FXID hRefID, FXID hAnimID);
        FXID hPlayer;
    protected:
    private:
        list<FXSurface> m_lstFXObjects;
        list<FXSurface>::iterator m_liFirst;
        SDL_Surface* m_lpsfSDLScreen;
        Uint32 m_tmOld;
        Uint32 m_tmFrame;
};

The value type of my list is:

struct FXSurface
{
    FXID hRefID;
    int wpxTile;
    int hpxTile;
    int wpxTotal;
    int hpxTotal;
    int cntTiles;

    map<int, vector<int> > htAnims; // All animations
    map<int, vector<int> >::iterator vCurr; // Currently active animation
    vector<int>::iterator fiCurr; // Currently active frame
    SDL_Surface* lpsfSDL;
    SDL_Rect* lprcTiles;    // Predefined frame positions
    string* fpImage;
};

I've implemented very simple initialize and render function. The CreateCharacter function takes a few parameters, the most important of which is htAnims, a map of integer vectors (idea being: I define numeric ids with easy-to-remember representations, such as FXA_IDLE or FXA_WALK, as the key, and the series of number values representing frames for the animation as a vector). This could be fairly easily implemented as a multidimensional integer array, but animations are variable in length and I want to be able to add new anims (or redefine existing ones) without having to recast an array.

The CreateCharacter function is simple. It creates a new FXSurface, populates it with the required data, and pushes the new FXSurface onto the list:

FXID FXSDL::CreateCharacter(FXID hRefID, string fpImage, int wpxTile, int hpxTile, map<int, vector<int> > htAnims)
{
    //list<FXSurface>::iterator lpsfTemp;
    FXSurface lpsfTemp;
    list<FXSurface>::iterator lpsfPos;
    SDL_Rect* lprcCurr = NULL;
    int cntTileW = 0;
    int cntTileH = 0;
    int cntCurr = 0;

    // Start off by initializing our container struct
    //lpsfTemp = new FXSurface();
    lpsfTemp.lpsfSDL = IMG_Load(fpImage.c_str()); // Try to load the requested image
    if(lpsfTemp.lpsfSDL != NULL) // If we didn't fail to
    {
        // Assign some variables for tracking
        lpsfTemp.hRefID = hRefID;
        lpsfTemp.fpImage = &fpImage;
        lpsfTemp.wpxTotal = lpsfTemp.lpsfSDL->w;
        lpsfTemp.hpxTotal = lpsfTemp.lpsfSDL->h;

        // If a tile width was specified, use it
        if(wpxTile != 0)
        {
            lpsfTemp.wpxTile = wpxTile;
            lpsfTemp.hpxTile = hpxTile;
        } // Otherwise, assume one tile
        else
        {
            lpsfTemp.wpxTile = lpsfTemp.wpxTotal;
            lpsfTemp.hpxTile = lpsfTemp.hpxTotal;
        }

        // Determine the tiles per row and column for later
        cntTileW = lpsfTemp.wpxTotal / lpsfTemp.wpxTile;
        cntTileH = lpsfTemp.hpxTotal / lpsfTemp.hpxTile;

        // And the total number of tiles
        lpsfTemp.cntTiles = cntTileW * cntTileH;
        lpsfTemp.lprcTiles = new SDL_Rect[cntTileW*cntTileH];

        // So we don't calculate this every time, determine each frame's coordinates and store them
        for(int h = 0; h < cntTileH; h++)
        {
            for(int w = 0; w < cntTileW; w++)
            {
                cntCurr = (h*cntTileW)+w;
                lprcCurr = new SDL_Rect;
                lprcCurr->w = lpsfTemp.wpxTile;
                lprcCurr->h = lpsfTemp.hpxTile;
                lprcCurr->x = w*lpsfTemp.wpxTile;
                lprcCurr->y = h*lpsfTemp.hpxTile;

                lpsfTemp.lprcTiles[cntCurr] = *lprcCurr;
                lprcCurr = NULL;
            }
        }

        // Now acquire our list of animations and set the default
        //lpsfTemp.htAnims = new map<int, vector<int> >(*htAnims);
        lpsfTemp.htAnims = htAnims;
        lpsfTemp.vCurr = lpsfTemp.htAnims.find(FXA_WALK_EAST);
        lpsfTemp.fiCurr = lpsfTemp.vCurr->second.begin();

        this->m_lstFXObjects.push_back(lpsfTemp);
    }
    else
    {
        hRefID = 0;
    }

    return hRefID;
}

It is precisely as the object is pushed that the error occurs. I've stepped through the code numerous times. Initially, I was only able to tell that my iterators were unable to dereference to the FXSurface object. After using watches to identify the exact memory address that the iterator and list objects pointed to, and dereferencing the address, I noticed the reason for my segfaults: all the values which I put into the original FXSurface were pushed down two memory blocks when the list object copied it!

My process for doing this is simple. I set up a breakpoint at the return statement for CreateCharacter, which gives me a view of lpsfTemp (the FXSurface I later add to the list) and m_lstFXObjects (the list I add it to). I scroll through the members of m_lstFXObjects, which brings me to _M_node, which contains the memory address of the only object I have added so far. I add a watch to this address in the form of (FXSurface)-hex address here-

First, find the address:

(There should be a picture here showing the highlighted _M_node attribute containing the list item's address, but I can't post pictures, and I can only post one URL. The second one is by far more important. It's located at http://www.fauxsoup.net/so/address.jpg)

Next, we cast and deference the address. This image shows both lpsfTemp and the copy in m_lstFXObjects; notice the discrepancy?

http://www.fauxsoup.net/so/dereferenced.jpg - See? All the values are in the correct order, just offset by two listings

I had initially been storing fpImages as a char*, so I thought that may have been throwing things off, but now it's just a pointer and the problem persists. Perhaps this is due to the map<int, vector<int> > I store?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

请远离我 2024-09-15 12:59:14

FXSDL析构函数,但没有复制构造函数并且没有赋值运算符。哟,您使用的是裸指针,但违反了 三法则

我不会再看下去了。

使用智能指针来管理资源。不要将裸资源放入类型中,除非该类型的唯一目的是管理这一资源。来自昨天给出的另一个答案

根据经验:如果您必须手动管理资源,将每个资源包装到其自己的对象中。

FXSDL has a destructor, but no copy constructor and no assignment operator. Yo you're using naked pointers, but violate the Rule of Three.

I'm not going to look any further.

Use smart pointers to manage resources. Do not put a naked resource into a type, except when that type's only intention is to manage this one resource. From another answer given yesterday:

As a rule of thumb: If you have to manually manage resources, wrap each into its own object.

秉烛思 2024-09-15 12:59:14

乍一看,我会说您双重删除了 lpsfSDL 和/或 lprcTiles。当结构中有原始指针时,您需要遵循三法则(实现复制构造函数、赋值运算符和析构函数)来正确管理内存。

At a glance, I'd say you're double-deleting lpsfSDL and/or lprcTiles. When you have raw pointers in your structure, you need to follow the rule-of-three (implement copy constructor, assignment operator, and destructor) to properly manage the memory.

冷︶言冷语的世界 2024-09-15 12:59:14

这些行对我来说看起来是错误的:

            lprcCurr = new SDL_Rect;
            lprcCurr->w = lpsfTemp.wpxTile;
            lprcCurr->h = lpsfTemp.hpxTile;
            lprcCurr->x = w*lpsfTemp.wpxTile;
            lprcCurr->y = h*lpsfTemp.hpxTile;

            lpsfTemp.lprcTiles[cntCurr] = *lprcCurr;
            lprcCurr = NULL;

lpsfTemp.lprcTiles 是一个 SDL_Rect*lprcTemp.lprcTiles[cntCurr] 是一个 SDL_Rect。恕我直言,您应该写这个:

            SDL_Rect tmpRect;
            tmpRect.w = lpsfTemp.wpxTile;
            tmpRect.h = lpsfTemp.hpxTile;
            tmpRect.x = w*lpsfTemp.wpxTile;
            tmpRect.y = h*lpsfTemp.hpxTile;

            lpsfTemp.lprcTiles[cntCurr] = tmpRect;

完全转储 lprcCurr 。

现在这段代码:

    lpsfTemp.vCurr = lpsfTemp.htAnims.find(FXA_WALK_EAST);
    lpsfTemp.fiCurr = lpsfTemp.vCurr->second.begin();

这很糟糕。一旦push_back完成,这些迭代器就无效。该push_back正在制作lpsfTemp副本。地图和矢量成员将复制自身,这些迭代器将复制自身,但它们将指向 lpsfTemp 的成员,这些成员将在 CreateCharacter 后立即销毁退出。

解决此问题的一种方法是在开始时 Push_back FXSurface 对象,使用 back() 获取其引用并对其进行操作,而不是 lpsfTemp。然后迭代器将保持一致,并且它们应该保持一致,因为您使用的列表不会复制其对象。如果您使用向量或双端队列或列表以外的任何内容,您将需要在复制构造函数和赋值运算符中管理所有这些指针和迭代器。

另一件事:当您访问 lprcTiles 数组时,请仔细检查您的数组边界。那里有任何错误,你可能会在谁知道什么上乱写乱画。

我不知道这些是否会对您有帮助。

These lines look wrong to me:

            lprcCurr = new SDL_Rect;
            lprcCurr->w = lpsfTemp.wpxTile;
            lprcCurr->h = lpsfTemp.hpxTile;
            lprcCurr->x = w*lpsfTemp.wpxTile;
            lprcCurr->y = h*lpsfTemp.hpxTile;

            lpsfTemp.lprcTiles[cntCurr] = *lprcCurr;
            lprcCurr = NULL;

lpsfTemp.lprcTiles is a SDL_Rect*. lprcTemp.lprcTiles[cntCurr] is a SDL_Rect. You should be writing this, IMHO:

            SDL_Rect tmpRect;
            tmpRect.w = lpsfTemp.wpxTile;
            tmpRect.h = lpsfTemp.hpxTile;
            tmpRect.x = w*lpsfTemp.wpxTile;
            tmpRect.y = h*lpsfTemp.hpxTile;

            lpsfTemp.lprcTiles[cntCurr] = tmpRect;

Dump the lprcCurr entirely.

Now this code:

    lpsfTemp.vCurr = lpsfTemp.htAnims.find(FXA_WALK_EAST);
    lpsfTemp.fiCurr = lpsfTemp.vCurr->second.begin();

This is bad. These iterators are invalid as soon as the push_back completes. That push_back is making a copy of lpsfTemp. The map and vector members are going to copy themselves and those iterators will copy themselves but they will be pointing to lpsfTemp's members which are going to be destroyed as soon as CreateCharacter exits.

One way to fix that would be to push_back a FXSurface object at the beginning, use back() to get its reference and operate on that instead of lpsfTemp. Then the iterators would stay consistent and they should stay consistent since you are using a list which does not copy its objects around. If you were using a vector or deque or anything other than a list you would need to manage all those pointers and iterators in the copy constructor and assignment operator.

Another thing: Double and triple check your array bounds when you access that lprcTiles array. Any mistake there and you could be scribbling over who knows what.

I don't know if any of that will help you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文