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

基于SDL2的C语言3D立方体程序内存泄漏及内存突增问题排查

SDL2 3D立方体程序内存泄漏与内存暴涨问题修复

核心问题分析

程序内存急剧暴涨至GB级的根本原因是透视投影计算出现超大坐标:当立方体靠近屏幕(相机平面)时,distance = fl + input.z + centerZ的绝对值趋近于0,导致perspective = fl / distance变成极大值,最终计算出的SDL_Point坐标会是远超屏幕范围的超大整数。SDL的SDL_RenderDrawLine在处理这种超大坐标时,会尝试绘制横跨巨大区域的线段,内部可能分配大量临时资源或陷入低效计算,直接导致内存占用飙升。

此外代码中还有几处逻辑bug会加剧问题或引发其他内存错误。

具体修复方案

1. 修复透视投影的超大坐标问题

修改projectPoint函数,添加距离阈值判断,当物体处于相机平面内或后方时标记为不可见,避免生成无效超大坐标:

// project 3D point onto the 2D screen
SDL_Point projectPoint(struct Vector3 input, bool* isVisible) {
    *isVisible = true;
    float perspective = 1.0f;
    float distance = fl + input.z + centerZ;
    // 限制最小距离,避免透视系数过大
    const float minDistance = 10.0f;
    if(fabs(distance) < minDistance) {
        // 点在相机后方或过近,标记为不可见
        *isVisible = false;
        SDL_Point p = { -1, -1 };
        return p;
    }
    perspective = fl / distance;
    
    SDL_Point p;
    p.x = input.x * perspective + centerX; 
    p.y = input.y * perspective + centerY; 
    return p;
}

同时修改drawCube函数,只绘制可见的线段:

// connects 8 points to form a cube
void drawCube(struct Vector3* input) {
    for(int i = 0; i < 4; i++) {
        bool visible0, visible1;
        SDL_Point p0 = projectPoint(input[i], &visible0);
        SDL_Point p1 = projectPoint(input[(i + 1) % 4], &visible1);
        if(visible0 && visible1) {
            SDL_RenderDrawLine(gRenderer, p0.x, p0.y, p1.x, p1.y);
        }
    }
    for(int i = 0; i < 4; i++) {
        bool visible0, visible1;
        SDL_Point p0 = projectPoint(input[i + 4], &visible0);
        SDL_Point p1 = projectPoint(input[(i + 1) % 4 + 4], &visible1);
        if(visible0 && visible1) {
            SDL_RenderDrawLine(gRenderer, p0.x, p0.y, p1.x, p1.y);
        }
    }
    for(int i = 0; i < 4; i++) {
        bool visible0, visible1;
        SDL_Point p0 = projectPoint(input[i], &visible0);
        SDL_Point p1 = projectPoint(input[i + 4], &visible1);
        if(visible0 && visible1) {
            SDL_RenderDrawLine(gRenderer, p0.x, p0.y, p1.x, p1.y);
        }
    }
}

2. 修复初始化函数的错误检查bug

init()中创建Renderer后错误检查的是gWindow,改为检查gRenderer:

bool init() {
    if(SDL_Init(SDL_INIT_VIDEO) != 0) {
        printf("SDL Error: %s", SDL_GetError());
        return false;
    }
    gWindow = SDL_CreateWindow("hi", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, gWidth, gHeight, 0);
    if(gWindow == NULL) {
        printf("SDL Error: %s", SDL_GetError());
        return false;
    }
    gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_PRESENTVSYNC);
    // 修复:检查gRenderer而非gWindow
    if(gRenderer == NULL) {
        printf("SDL Error: %s", SDL_GetError());
        return false;
    }
    return true;
}

3. 修复主循环的事件处理与退出逻辑

主循环中需循环处理所有待处理事件,且收到退出事件时应正常执行资源释放:

int main() {
    // initialize SDL stuff
    if(!init()) {
        return 1;
    }
    bool isRunning = true;
    SDL_Event e;
    const Uint8* gKeyStates = SDL_GetKeyboardState(NULL);

    struct Vector3 points[8] = {
        [0] = {-150, -150, 150},
        [1] = {150, -150, 150},
        [2] = {150, 150, 150},
        [3] = {-150, 150, 150},
        [4] = {-150, -150, -150},
        [5] = {150, -150, -150},
        [6] = {150, 150, -150},
        [7] = {-150, 150, -150},
    };

    while(isRunning) {
        // 循环处理所有事件,避免队列堆积
        while(SDL_PollEvent(&e)) {
            if(e.type == SDL_QUIT) {
                isRunning = false;
            }
        }
        if(gKeyStates[SDL_SCANCODE_LEFT]) {
            translateModel(points, -moveSpeed, 0, 0);
        }
        if(gKeyStates[SDL_SCANCODE_RIGHT]) {
            translateModel(points, moveSpeed, 0, 0);
        }
        if(gKeyStates[SDL_SCANCODE_UP]) {
            translateModel(points, 0, 0, -moveSpeed);
        }
        if(gKeyStates[SDL_SCANCODE_DOWN]) {
            translateModel(points, 0, 0, moveSpeed);
        }
        draw(points);
    }
    close();

    return 0;
}

关于Dr. Memory错误日志的说明

  • Error #1:栈越界访问,大概率是程序异常导致的栈溢出,修复上述核心问题后该错误可能消失。
  • Error #2:GDI相关的未初始化读取,属于SDL与系统交互的轻微问题,一般不会导致内存暴涨,可忽略或尝试更新SDL版本解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 18:15:53