You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SDL2中渲染同一结构体多实例问题:子弹链表遍历致崩溃

SDL2多子弹链表遍历崩溃问题修复

核心错误点分析

1. 不必要的内存分配导致泄漏与指针混乱

遍历前的Entity *b = new Entity();和Entity *prev = new Entity();完全多余:

  • prev随后被赋值为&bulletHead,new出来的实体内存直接泄漏
  • b是遍历指针,不需要提前分配,直接声明指针变量即可

2. 内存释放不匹配

子弹用new Entity()创建,却用free(b)释放,C中new必须对应delete,混用C/C内存管理函数会触发未定义行为,直接导致程序崩溃。

3. 链表头未初始化

栈上的Entity bulletHead未显式初始化next指针,默认值是随机垃圾值,第一次遍历会访问非法内存。

4. 遍历删除节点时的指针逻辑错误

删除节点后执行b = prev;,随后for循环的b = b->next会跳过下一个节点,甚至出现空指针访问,破坏链表遍历流程。


修正后的完整代码片段

void Game::gameLoop(){
    Entity player;
    Entity bulletHead;
    Entity *bulletTail = &bulletHead; // 直接指向头节点,无需new

    // 初始化链表头的next指针为NULL,避免垃圾值
    memset(&bulletHead, 0, sizeof(Entity));

    player.x = 500;
    player.y = 640;
    player.dx = 0;
    player.dy = 0;
    player.texture = loadTexture((char*)"res/images/player.png");
    SDL_QueryTexture(player.texture, NULL, NULL, &player.w, &player.h);

    SDL_Texture *bulletTexture = loadTexture((char*)"res/images/bullet.png");

    while(gameState != GameState::EXIT){
        prepareScene(); // sets up rendering
        handleEvents(); // collects and precesses user input

        player.x += player.dx;
        player.y += player.dy;

        // Player key input
        if(player.reload > 0) player.reload--;

        if (up)
        {
            player.y -= 4;
        }

        if (down)
        {
            player.y += 4;
        }

        if (left)
        {
            player.x -= 4;
        }

        if (right)
        {
            player.x += 4;
        }

        // allow fire bullet every 8 frames
        if(fire && player.reload == 0){
            player.reload = 8;

            // Create bullet
            Entity *bullet = new Entity();
            memset(bullet, 0, sizeof(Entity));

            bulletTail->next = bullet;
            bulletTail = bullet;

            bullet->x = player.x + 8;
            bullet->y = player.y;
            bullet->dx = 0;
            bullet->dy = -PLAYER_BULLET_SPEED;
            bullet->health = 1;
            bullet->texture = bulletTexture;
            SDL_QueryTexture(bullet->texture, NULL, NULL, &bullet->w, &bullet->h); 
        }

        // handle physics and render for each bullet
        Entity *b = bulletHead.next;
        Entity *prev = &bulletHead;

        while(b != NULL){
            Entity *nextBullet = b->next; // 提前保存下一个节点,避免删除后丢失
            
            b->x += b->dx;
            b->y += b->dy;

            blit(b->texture, b->x, b->y);
        
            if(b->y < 0){
                if (b == bulletTail)
                {
                    bulletTail = prev;
                }
                prev->next = nextBullet;
                delete b; // 用delete匹配new
            } else {
                prev = b; // 只有不删除节点时才更新prev
            }
            
            b = nextBullet; // 用提前保存的节点指针推进遍历
        }

        blit(player.texture, player.x, player.y); // display image

        presentScene(); // displays scene
        SDL_Delay(16); // limits fps to around 62fps
    }

    // 清理剩余子弹链表
    Entity *current = bulletHead.next;
    while(current != NULL){
        Entity *next = current->next;
        delete current;
        current = next;
    }

    SDL_DestroyTexture(bulletTexture); // 别忘了释放纹理
    SDL_DestroyTexture(player.texture);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

关键修改说明

  • 初始化链表头bulletHead的next为NULL,避免遍历非法内存
  • 移除遍历前不必要的new操作,直接声明指针变量
  • 用delete替代free释放子弹节点,保证内存管理匹配
  • 遍历前提前保存下一个节点的指针,避免删除当前节点后丢失遍历路径
  • 改用while循环替代for循环,让遍历逻辑更清晰,避免指针混乱
  • 新增退出循环后的链表清理和纹理释放,避免内存泄漏

内容的提问来源于stack exchange,提问作者lool

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 13:23:13