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
相关产品推荐
相关产品推荐

