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

调用free后内存未释放,malloc时内存持续上涨问题排查

内存泄漏排查:SDL2子弹对象未正确释放问题

我用SDL2和SDL_image开发飞船射击程序,按空格键发射PNG格式子弹,子弹向右移动。屏幕所有元素都是object结构体实例,存储在链表中;create_ent函数负责为新对象分配内存并完成初始化,move_ents函数根据对象的xvector和yvector属性移动所有对象,同时检测对象是否超出屏幕范围,若超出则释放对象并维护链表结构。

通过任务管理器监测内存使用情况发现:运行move_ents函数后内存并未释放,且每次调用create_ent时内存持续上涨。已知free后的内存可能会保留给当前程序复用而非直接还给操作系统,但这种持续上涨的情况让我疑惑:是任务管理器显示问题,还是我的内存释放代码存在错误?Visual Studio未给出任何错误提示。

相关代码

#include <stdio.h>
#include <SDL.h>
#include <SDL_ttf.h>
#include <SDL_image.h>

#define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 720
#define TARGET_FPS 60

struct object {
    SDL_Surface* surface;
    SDL_Texture* texture;
    SDL_Rect rect;
    SDL_Point center;
    int is_ship;
    int inside;
    long float x;
    long float y;
    float w;
    float h;
    long float xvector;
    long float yvector;
    long float mass;
    long float forcex;
    long float forcey;
    float rot_speed;
    double angle;
    struct object* next;
};

void create_ent(SDL_Renderer* renderer, struct object* p, char* img_path, long float x, long float y, float w, float h, long float xvector, long float yvector, long float mass, double angle, int ship) {
    struct object* new = malloc(sizeof(struct object));

    while (p->next != NULL)
        p = p->next;

    p->next = new;

    new->surface = IMG_Load(img_path);
    if (!(new->surface))
        printf("Dammit! An image wasn't found.");

    new->texture = SDL_CreateTextureFromSurface(renderer, new->surface);

    new->x = x;
    new->y = y;
    new->w = w;
    new->h = h;
    new->xvector = xvector;
    new->yvector = yvector;
    new->mass = mass;
    new->forcex = 0;
    new->forcey = 0;
    new->inside = 1;
    new->angle = angle;
    new->center = (SDL_Point){ w / 2, h / 2 };
    new->is_ship = ship;
    new->next = NULL;
}

void move_ents(struct object* p) {
    struct object* temp;

    while (p->next != NULL) {
        if ((p->next)->inside == 0) {
            if ((p->next)->next == NULL) {
                free(p->next);
                p->next = NULL;
                break;
            }
            else {
                temp = (p->next)->next;
                free(p->next);
                p->next = temp;
            }
        }

        p = p->next;

        if (!(p->is_ship))
            if ((p->x > WINDOW_WIDTH + p->w / 2) || (p->y > WINDOW_HEIGHT + p->h / 2) || (p->x < 0 - p->w / 2) || (p->y < 0 - p->h / 2)) {
                p->inside = 0;
                printf("object outside of window");
            }

        p->xvector += (p->forcex / p->mass);
        p->yvector += (p->forcey / p->mass);
        p->x += p->xvector;
        p->y += p->yvector;
    }
}

int main(void) {
    while (SDL_PollEvent(&event) != 0) {
        switch (event.type) {
        case SDL_QUIT:
            quit = 1;
            break;
        case SDL_KEYUP:
            switch (event.key.keysym.sym) {
            case SDLK_SPACE:
                create_ent(renderer, head, "allmine/Laser/laser.png", ship->x + 10, ship->y + 10, 5, 25, 10, 0, 1, ship->angle, 0);
                break;
            case SDLK_RIGHT:
                rightpressed = 0;
                break;
            case SDLK_LEFT:
                leftpressed = 0;
                break;
            case SDLK_UP:
                uppressed = 0;
                break;
            default:
                break;
            }
            break;
        case SDL_KEYDOWN:
            switch (event.key.keysym.sym) {
            case SDLK_RIGHT:
                rightpressed = 1;
                break;
            case SDLK_LEFT:
                leftpressed = 1;
                break;
            case SDLK_UP:
                uppressed = 1;
                break;
            default:
                break;
            }
        default:
            break;
        }
    }

    if (rightpressed)
        ship->angle += 5;
    else if (leftpressed)
        ship->angle -= 5;

    draw_ents(renderer, head);
    apply_forces(head, 10000);
    move_ents(head);

    SDL_RenderPresent(renderer);

    // Tickrate
    frameStart = SDL_GetTicks();
    frameTime = SDL_GetTicks() - frameStart;
    if (frameTime < 1000 / TARGET_FPS)
        SDL_Delay((1000 / TARGET_FPS) - frameTime);
    }
}

问题分析与修复方案

1. 核心问题:SDL资源未释放

你的object结构体包含SDL_Surface*和SDL_Texture*,这两类资源属于SDL的内核对象,必须通过专用接口释放,直接free结构体只会释放结构体本身的内存,而SDL分配的GPU/CPU资源会永久泄漏,这是内存持续上涨的主要原因。

2. 链表释放逻辑漏洞

原代码在释放节点后直接break或移动指针,会导致后续需要释放的节点被跳过,无法完成完整的链表清理;同时释放节点后的指针移动逻辑错误,可能跳过正常节点的检测和移动操作。

3. 任务管理器显示问题

确实,free不会立刻将内存归还给操作系统,而是留作程序后续复用,但内存持续上涨说明肯定存在真实泄漏,并非单纯显示问题,不需要改用realloc。


具体修复代码

修正move_ents函数(先释放SDL资源,再释放结构体)

void move_ents(struct object* p) {
    while (p != NULL && p->next != NULL) {
        struct object* current = p->next;
        // 检测是否需要释放
        if (current->inside == 0) {
            // 先释放SDL资源
            SDL_FreeSurface(current->surface);
            SDL_DestroyTexture(current->texture);
            // 再释放结构体内存并维护链表
            p->next = current->next;
            free(current);
            // 不移动p,继续检查新的p->next
            continue;
        }

        // 非飞船对象的边界检测
        if (!current->is_ship) {
            if ((current->x > WINDOW_WIDTH + current->w / 2) || 
                (current->y > WINDOW_HEIGHT + current->h / 2) || 
                (current->x < 0 - current->w / 2) || 
                (current->y < 0 - current->h / 2)) {
                current->inside = 0;
                printf("object outside of window");
                // 留到下次循环处理释放,保持逻辑一致
                p = current;
                continue;
            }
        }

        // 更新速度与位置
        current->xvector += (current->forcex / current->mass);
        current->yvector += (current->forcey / current->mass);
        current->x += current->xvector;
        current->y += current->yvector;

        p = current;
    }
}

修正create_ent函数的错误处理(图片加载失败时释放已分配内存)

void create_ent(SDL_Renderer* renderer, struct object* p, char* img_path, long float x, long float y, float w, float h, long float xvector, long float yvector, long float mass, double angle, int ship) {
    struct object* new = malloc(sizeof(struct object));
    if (!new) {
        printf("Failed to allocate memory for new object");
        return;
    }

    while (p->next != NULL)
        p = p->next;

    new->surface = IMG_Load(img_path);
    if (!(new->surface)) {
        printf("Dammit! An image wasn't found.");
        // 加载失败时释放已分配的结构体
        free(new);
        p->next = NULL;
        return;
    }

    new->texture = SDL_CreateTextureFromSurface(renderer, new->surface);
    if (!new->texture) {
        printf("Failed to create texture from surface");
        SDL_FreeSurface(new->surface);
        free(new);
        p->next = NULL;
        return;
    }

    // 其他初始化代码...
    new->x = x;
    new->y = y;
    new->w = w;
    new->h = h;
    new->xvector = xvector;
    new->yvector = yvector;
    new->mass = mass;
    new->forcex = 0;
    new->forcey = 0;
    new->inside = 1;
    new->angle = angle;
    new->center = (SDL_Point){ w / 2, h / 2 };
    new->is_ship = ship;
    new->next = NULL;

    p->next = new;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 02:30:56