指针的意外行为(操作时)但使用双指针时定义的行为

Posted

技术标签:

【中文标题】指针的意外行为(操作时)但使用双指针时定义的行为【英文标题】:Unexpected behaviour by pointer (when manipulated) but defined behaviour when using a double pointer 【发布时间】:2021-01-24 07:31:19 【问题描述】:

我对指向变量的指针行为背后的原因感到很困惑。我会认为,如果我将一个指向 vector 的指针附加并访问它,无论我是否更改了指针本身,它在同一个变量上仍然应该是一样的。我的意思是,例如,我有一个整数指针向量,我修改了一个在其他地方定义的变量,该变量已附加到向量中。如果我要打印它们,它应该更新向量(实际上不是),但它应该打印一个整数的新值。我正在尝试将其应用于 SDL2 中的 SDL_Texture*,但它不太有意义。总之,同样的概念也适用于那里,但我使用的是纹理。我修改纹理并用它做“事情”,但在循环结束时,当我渲染它时,向量仍然迭代到附加到它的SDL_Texture*。我的问题是,当我更改和修改纹理时,当我去渲染它时它不会出现。这不是因为纹理没有正确加载或任何东西(我已经对其进行了测试,而不是使用矢量,我将其绘制为原始),而是在使用矢量时它无法正常工作。代码如下:

void Main::Mainloop()


    Screen_Object m_Screen_Object;

    TTF_Font* font = TTF_OpenFont("Anonymous_Pro.ttf", 30);
    SDL_Surface* surf = TTF_RenderText_Blended(font, "Starting fps", 0,0,0);
    SDL_Texture* tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
    SDL_FreeSurface(surf);
    SDL_Rect rct = 20, 100, 0,0;
    SDL_QueryTexture(tex, NULL, NULL, &rct.w, &rct.h);
    m_Screen_Object.Add_Texture(tex);


    Uint32 start, finish, counter;
    counter = 0;
    start = SDL_GetTicks();
    finish = SDL_GetTicks();



    bool running = true;

    while (running)
    
        Events::Event_Loop();
        if (Events::Quit_Application())
            running = false;
            break;
        


        ///Clear display to color
        SDL_SetRenderDrawColor(Render::Get_Renderer(), 0,255,0,255);
        SDL_RenderClear(Render::Get_Renderer());



        ///Do stuff here
        m_Screen_Object.Draw_Textures();
        

        finish = SDL_GetTicks();
        counter += 2;
        if (finish - start >= 500)
        
            start = SDL_GetTicks();

            SDL_DestroyTexture(tex);
            std::string fps = std::to_string(counter);
            surf = TTF_RenderText_Blended(font, fps.c_str(), 0,0,0);
            tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
            SDL_FreeSurface(surf);
            SDL_QueryTexture(tex, NULL, NULL, &rct.w, &rct.h);
            counter = 0;
        


        SDL_RenderPresent(Render::Get_Renderer());

    


    SDL_DestroyTexture(tex);
    TTF_CloseFont(font);






int main(int argc, char* argv[])


    Main::Mainloop();

    return 0;


这是Screen_Object的声明: 在标题中:

std::vector < SDL_Texture* > m_Textures;

在.cpp中:

void Screen_Object::Add_Texture(SDL_Texture* p_Texture)

    m_Textures.push_back(p_Texture);



void Screen_Object::Draw_Textures()


    for (unsigned int i=0; i < m_Textures.size(); i++)
    
        SDL_RenderCopy(Render::Get_Renderer(), m_Textures[i], NULL, &m_Rect);
    


现在这段代码不能以我认为应该的方式工作,因为我不明白为什么它不能工作,但是当我将向量的类型更改为SDL_Texture** 时,代码工作正常。没有** 的代码到底有什么问题,我只是无法从逻辑上理解为什么它不能正常工作

【问题讨论】:

我对指向变量的指针行为背后的原因感到很困惑。 -- 将一个非常简单的标准 C++ 放在一起会容易得多程序,没有 SDL,显示问题。使用指向 int 等的指针而不是完整的 SDL 类型的简单操作。 因为它适用于int,但不适用于SDL_Texture* 向我们展示您说“有效”的int 版本。 tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf); -- 向量不知道你已经搞砸了tex。向量现在在此之后包含一个垃圾指针值。 它仍然在同一个变量名下, -- Bingo。您正在查看name,而不是查看value。重要的是价值,而不是名称。 【参考方案1】:

问题似乎是您将指针存储在向量中,但在向量之外您使指针无效。

void Screen_Object::Add_Texture(SDL_Texture* p_Texture)

    m_Textures.push_back(p_Texture);


void Main::Mainloop()

    Screen_Object m_Screen_Object;
    //...
    SDL_Texture* tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
    //...
    m_Screen_Object.Add_Texture(tex); // <-- add pointer to vector
    //...
    tex =  SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf); // <-- This changes the pointer, but has no effect on the vector's 
    //...

所以在那一行之后的任何东西,如果你访问m_Textures向量中的指针,向量中的指针不再有效,或者更糟的是,它是有效的,但指向一个旧的(但仍然有效)SDL_Texture .

一个非常简单的解决方案是通过获取对该指针的引用来确保您使用存储在m_Textures 中的指针:

void Main::Mainloop()

    Screen_Object m_Screen_Object;

    TTF_Font* font = TTF_OpenFont("Anonymous_Pro.ttf", 30);
    SDL_Surface* surf = TTF_RenderText_Blended(font, "Starting fps", 0,0,0);

    SDL_Texture* tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
    m_Screen_Object.Add_Texture(tex);  // <-- Immediately do this
    auto& texRef = m_Screen_Object.back(); // <-- Get a reference to the pointer.

然后你使用texRef。这是对您添加到向量中的指针的实际引用,而不是指针的副本。

那么循环就是:

 texRef = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);

这会改变存储在向量中的实际指针。

【讨论】:

shared_ptr 有什么帮助,我只是稍微研究了一下,但我不太明白如何将其应用于SDL_Texture 这里可能无济于事。但是shared_ptr 基本上是一个引用计数指针——最后一个对指针有引用的实体,当它消失时,指针会自动被“删除”。如果你有一个 SDL_Texture 被共享,你会使用它,并且没有明确的所有权确定谁负责清理它——当它的 shared_ptr 检测到没有纹理时,纹理就会消失更多参考。 我们清楚了——texRef(或任何其他指向向量存储数据的指针/引用)仅在向量的下一次重新分配之前有效。例如。将新元素插入向量可能会在没有任何通知的情况下使引用无效。需要注意的事项。 是的,如果向量重新分配,引用将不再有效。指针值本身还是可以的,只是指针的引用不行。

以上是关于指针的意外行为(操作时)但使用双指针时定义的行为的主要内容,如果未能解决你的问题,请参考以下文章

是否打印空指针未定义行为?

经典算法双指针(尺取法):爱,是双向奔赴,还是你追我赶?

在 PostgreSQL 中将双精度转换为文本时出现意外行为

Vaadin 项目点击监听器双击行为

在 golang 中修改映射值 - 有和没有指针 - 意外行为 [关闭]

在链表中添加节点时使用双指针的原因是啥?