我有这个 C 代码试图模拟重力,但内存不起作用

问题描述 投票:0回答:1

它基本上是通过 insertBody 程序越来越深入,我不明白为什么。当我将主体数量设置为 100 和 1000 时,它甚至不会生成它们,而设置为 20 时,它会生成 1 帧。另外,calculateForces 也是一项正在进行中的工作。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <SDL2/SDL.h>

#define THETA 0.5
#define PI 3.14159265359
#define GRAV_CONST 0.0000001
#define SCREEN_WIDTH 500
#define SCREEN_HEIGHT 500

typedef struct {
    float bodyMass;
    float position[2];
    float velocity[2];
} Body;

typedef struct Node {
    short internal;
    float center[2];
    float totalMass;
    float massCenter[2];
    struct Node *children[4];
    Body *storedBody;
} Node;

void drawCircle(SDL_Renderer *renderer, int centerX, int centerY, int radius) {
    for (int angle = 0; angle <= 360; angle++) {
        double radians = angle * M_PI / 180.0;

        int x = centerX + (int) (radius * cos(radians));
        int y = centerY + (int) (radius * sin(radians));

        SDL_RenderDrawPoint(renderer, x, y);
    }
}

Node* allocateNodeMemory() {
    Node *node = (Node*)malloc(sizeof(Node));
    if (node != NULL) {
        node -> internal = 0;
        node -> totalMass = 0.0f;
        for (int i = 0; i < 2; i++) {
            node -> massCenter[i] = 0.0f;
            node -> center[i] = 0.0f;
        }
        for (int i = 0; i < 4; i++) {
            node -> children[i] = NULL;
        }
        node -> storedBody = NULL;
    } else {
        fprintf(stderr, "Memory allocation failed\n");
        exit(EXIT_FAILURE);
    }
    return node;
}

void freeNodeMemory(Node* node) {
    if (node != NULL) {
        for (int i = 0; i < 4; i++) {
            freeNodeMemory(node -> children[i]);
        }
        free(node);
        node = NULL;
    }
}

void splitNode(Node* node) {
    for(int i = 0; i < 4; i++) {
        node -> children[i] = allocateNodeMemory();
        (i & 1) ? (node -> children[i] -> center[0] = node -> center[0] * 3 / 2) 
                : (node -> children[i] -> center[0] = node -> center[0] / 2);
        (i & 2) ? (node -> children[i] -> center[1] = node -> center[1] * 3 / 2) 
                : (node -> children[i] -> center[1] = node -> center[1] / 2);
    }
}

void calcTotalMass(Node* node) {
    if(node -> internal) {
        for(int i = 0; i < 4; i++) {
            calcTotalMass(node -> children[i]);
            node -> totalMass += node -> children[i] -> totalMass;
        }
    } else {
        node -> totalMass += node -> storedBody -> bodyMass;
    }
}

void calcCenterOfMass(Node* node) {
    
}

void insertBody(Node* node, Body* body) {
    if (node == NULL) {
        return;
    }

    if(node -> internal == 0 && node -> storedBody == NULL) {
        node -> storedBody = body;
    
    } else {
        if(!node -> internal) {
            splitNode(node);
            node -> internal = 1;

            if(node -> storedBody != NULL) {
                insertBody(node, node -> storedBody);
                node -> storedBody = NULL;
            }
        }

        int quadrant = 0;

        if(body -> position[0] > node -> center[0]) quadrant += 1;
        if(body -> position[1] > node -> center[1]) quadrant += 2;

        insertBody(node -> children[quadrant], body);
    }
}

void buildGravityField(Node* node, Body* bodies[], int numBodies) {
    node -> center[0] = SCREEN_WIDTH / 2;
    node -> center[1] = SCREEN_HEIGHT / 2;

    for(int i = 0; i < numBodies; i++) {
        insertBody(node, bodies[i]);
    }
}

void calculateForces(Node* node) {
    
}

void updateBodies(Body *bodies[], int numBodies) {
    for(int i = 0; i < numBodies; i++) {
        bodies[i] -> position[0] += bodies[i] -> velocity[0];
        bodies[i] -> position[1] += bodies[i] -> velocity[1];
    }
}

void updateGraphics(SDL_Renderer *renderer, Body *bodies[], int numBodies) {
    for(int i = 0; i < numBodies; i++) {
        drawCircle(renderer, bodies[i] -> position[0], bodies[i] -> position[1], 1);
    }
}

Body* generateBody() {
    Body* body = malloc(sizeof(Body));

    float radiusRand = ((float)rand() / RAND_MAX) * 50;
    float radius = (-1 * pow((radiusRand - 20), 3) / 35 + 21 * radiusRand / 2 + 5 * log(radiusRand - 1) - 30) / 2;
    float angleRand = rand();
    float velocityRand = ((float)rand() / RAND_MAX) * 16 - 8;
    float velocityAngleRand = sin(10 * rand()) / 10;
    float velocity = 2 * tanh(5 * velocityRand) + sin(10 * velocityRand) / 10 + sin(velocityRand / 2) / 2;
    float mass = -pow(((((float)rand() / RAND_MAX) * 10) - 5), 2) / 10 + 5;

    body -> bodyMass = mass;
    body -> position[0] = cos(angleRand) * radius + SCREEN_WIDTH / 2;
    body -> position[1] = sin(angleRand) * radius + SCREEN_HEIGHT / 2;
    body -> velocity[0] = cos(angleRand + velocityAngleRand + PI / 2) * velocity;
    body -> velocity[1] = sin(angleRand + velocityAngleRand + PI / 2) * velocity;

    return body;
}

Body* testGenerateBody(int pos) {
    Body* body = malloc(sizeof(Body));
    body -> bodyMass = 2;
    body -> position[0] = pos;
    body -> position[1] = pos;
    body -> velocity[0] = 0;
    body -> velocity[1] = 0;
}

int main(int argc, char *argv[]) {
    srand(time(NULL));

    printf("%d %d", sizeof(Body), sizeof(Node));

    Node* node = NULL;
    node = allocateNodeMemory();

    int numBodies = 20;
    Body* bodies[numBodies];
    
    for (int i = 0; i < numBodies; i++) {
        bodies[i] = generateBody();
    }

    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
        return 1;
    }

    SDL_Window *window = SDL_CreateWindow("SDL2 Circle", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                                          SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);

    if (!window) {
        fprintf(stderr, "Window could not be created! SDL_Error: %s\n", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

    if (!renderer) {
        fprintf(stderr, "Renderer could not be created! SDL_Error: %s\n", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    int quit = 0;
    SDL_Event e;

    //printf("%d", sizeof(Node));

    while (!quit) {
        while (SDL_PollEvent(&e) != 0) {
            if (e.type == SDL_QUIT) {
                quit = 1;
            }
        }
        SDL_Delay(16);

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);
        SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);

        node = allocateNodeMemory();
        buildGravityField(node, bodies, numBodies);
        calculateForces(node);
        updateBodies(bodies, numBodies);
        updateGraphics(renderer, bodies, numBodies);

        SDL_RenderPresent(renderer);

        freeNodeMemory(node);
        for (int i = 0; i < numBodies; i++) {
            free(bodies[i]);
        }
    }
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

我尝试过改变生成的主体数量,也尝试过改变生成方法。我已经将其范围缩小到 insertBody 函数,尽管我写了这个东西,但它还是让我感到困惑和害怕。

编辑: 我已经解决了这个问题,它生成了屏幕之外的实体,然后尝试将它们放入节点中,但它永远循环,因为它们不应该在那里。这是更新后的代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <SDL2/SDL.h>

#define THETA 0.5
#define PI 3.14159265359
#define GRAV_CONST 0.0000001
#define SCREEN_WIDTH 500
#define SCREEN_HEIGHT 500

typedef struct {
    short onScreen;
    float bodyMass;
    float position[2];
    float velocity[2];
} Body;

typedef struct Node {
    short internal;
    float center[2];
    float totalMass;
    float massCenter[2];
    struct Node *children[4];
    Body *storedBody;
} Node;

void drawCircle(SDL_Renderer *renderer, int centerX, int centerY, int radius) {
    for (int angle = 0; angle <= 360; angle++) {
        double radians = angle * M_PI / 180.0;

        int x = centerX + (int) (radius * cos(radians));
        int y = centerY + (int) (radius * sin(radians));

        SDL_RenderDrawPoint(renderer, x, y);
    }
}

Node* allocateNodeMemory() {
    Node *node = (Node*)malloc(sizeof(Node));
    if (node != NULL) {
        node -> internal = 0;
        node -> totalMass = 0.0f;
        for (int i = 0; i < 2; i++) {
            node -> massCenter[i] = 0.0f;
            node -> center[i] = 0.0f;
        }
        for (int i = 0; i < 4; i++) {
            node -> children[i] = NULL;
        }
        node -> storedBody = NULL;
    } else {
        fprintf(stderr, "Memory allocation failed\n");
        exit(EXIT_FAILURE);
    }
    return node;
}

void freeNodeMemory(Node* node) {
    if (node != NULL) {
        for (int i = 0; i < 4; i++) {
            freeNodeMemory(node -> children[i]);
        }
        free(node);
        node = NULL;
    }
}

void checkPosition(Body* bodies[], int numBodies) {
    for(int i = 0; i < numBodies; i++) {
        if(bodies[i] -> position[0] < 0) bodies[i] -> onScreen = 0;
        if(bodies[i] -> position[0] > SCREEN_WIDTH) bodies[i] -> onScreen = 0;
        if(bodies[i] -> position[1] < 0) bodies[i] -> onScreen = 0;
        if(bodies[i] -> position[1] < SCREEN_HEIGHT) bodies[i] -> onScreen = 0;
    }
}

void splitNode(Node* node) {
    for(int i = 0; i < 4; i++) {
        if(node -> children[i] == NULL) node -> children[i] = allocateNodeMemory();
        (i & 1) ? (node -> children[i] -> center[0] = node -> center[0] * 3 / 2) 
                : (node -> children[i] -> center[0] = node -> center[0] / 2);
        (i & 2) ? (node -> children[i] -> center[1] = node -> center[1] * 3 / 2) 
                : (node -> children[i] -> center[1] = node -> center[1] / 2);
    }
}

void calcTotalMass(Node* node) {
    if(node -> internal) {
        for(int i = 0; i < 4; i++) {
            calcTotalMass(node -> children[i]);
            node -> totalMass += node -> children[i] -> totalMass;
        }
    } else {
        node -> totalMass += node -> storedBody -> bodyMass;
    }
}

void calcCenterOfMass(Node* node) {
    
}

void insertBody(Node* node, Body* body) {
    if (node == NULL || body -> onScreen == 0) {
        return;
    }

    if(node -> internal == 0 && node -> storedBody == NULL) {
        node -> storedBody = body;
    
    } else {
        if(!node -> internal) {
            splitNode(node);
            node -> internal = 1;

            if(node -> storedBody != NULL) {
                insertBody(node, node -> storedBody);
                node -> storedBody = NULL;
            }
        }

        int quadrant = 0;

        if(body -> position[0] > node -> center[0]) quadrant += 1;
        if(body -> position[1] > node -> center[1]) quadrant += 2;

        insertBody(node -> children[quadrant], body);
    }
}

void buildGravityField(Node* node, Body* bodies[], int numBodies) {
    node = allocateNodeMemory();
    node -> center[0] = SCREEN_WIDTH / 2;
    node -> center[1] = SCREEN_HEIGHT / 2;

    for(int i = 0; i < numBodies; i++) {
        insertBody(node, bodies[i]);
    }
}

void calculateForces(Node* node) {
    
}

void updateBodies(Body *bodies[], int numBodies) {
    for(int i = 0; i < numBodies; i++) {
        bodies[i] -> position[0] += bodies[i] -> velocity[0];
        bodies[i] -> position[1] += bodies[i] -> velocity[1];
    }
}

void updateGraphics(SDL_Renderer *renderer, Body *bodies[], int numBodies) {
    for(int i = 0; i < numBodies; i++) {
        drawCircle(renderer, bodies[i] -> position[0], bodies[i] -> position[1], 1);
    }
}

Body* generateBody() {
    Body* body = malloc(sizeof(Body));

    float radiusRand = ((float)rand() / RAND_MAX) * 45.3;
    float radius = (-1 * pow((radiusRand - 20), 3) / 35 + 21 * radiusRand / 2 + 5 * log(radiusRand + 1) - 30) / 2;
    float angleRand = rand();
    float velocityRand = ((float)rand() / RAND_MAX) * 16 - 8;
    float velocityAngleRand = sin(10 * rand()) / 10;
    float velocity = 2 * tanh(5 * velocityRand) + sin(10 * velocityRand) / 10 + sin(velocityRand / 2) / 2;
    float mass = -pow(((((float)rand() / RAND_MAX) * 10) - 5), 2) / 10 + 5;

    body -> bodyMass = mass;
    body -> position[0] = cos(angleRand) * radius + SCREEN_WIDTH / 2;
    body -> position[1] = sin(angleRand) * radius + SCREEN_HEIGHT / 2;
    body -> velocity[0] = cos(angleRand + velocityAngleRand + PI / 2) * velocity;
    body -> velocity[1] = sin(angleRand + velocityAngleRand + PI / 2) * velocity;
    body -> onScreen = 1;

    return body;
}

Body* testGenerateBody(int pos) {
    Body* body = malloc(sizeof(Body));
    body -> bodyMass = 2;
    body -> position[0] = pos;
    body -> position[1] = pos;
    body -> velocity[0] = 0;
    body -> velocity[1] = 0;
}

int main(int argc, char *argv[]) {
    srand(time(NULL));

    //printf("%d %d", sizeof(Body), sizeof(Node));

    Node* node = NULL;

    int numBodies = 100;
    Body* bodies[numBodies];
    
    for (int i = 0; i < numBodies; i++) {
        bodies[i] = generateBody();
    }

    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
        return 1;
    }

    SDL_Window *window = SDL_CreateWindow("SDL2 Circle", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                                          SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);

    if (!window) {
        fprintf(stderr, "Window could not be created! SDL_Error: %s\n", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

    if (!renderer) {
        fprintf(stderr, "Renderer could not be created! SDL_Error: %s\n", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    int quit = 0;
    SDL_Event e;

    while (!quit) {
        while (SDL_PollEvent(&e) != 0) {
            if (e.type == SDL_QUIT) {
                quit = 1;
            }
        }
        SDL_Delay(16);

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);
        SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);

        checkPosition(bodies, numBodies);
        buildGravityField(node, bodies, numBodies);
        calculateForces(node);
        updateBodies(bodies, numBodies);
        updateGraphics(renderer, bodies, numBodies);

        SDL_RenderPresent(renderer);

        freeNodeMemory(node);
    }

    for (int i = 0; i < numBodies; i++) {
        free(bodies[i]);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}
c memory runtime-error sdl-2
1个回答
0
投票

对之前的代码迭代进行了一些测试后,很高兴看到程序似乎表现得更好了。然而,当我调试之前的代码时,我遇到了内存泄漏,因为各种节点正在构建但没有正确释放。考虑到这一点,我在“freeNodeMemory”函数、“buildGravityField”函数以及调用“buildGravityField”函数之后的“main”函数中添加了一些“printf”语句。

void freeNodeMemory(Node* node)
{
    printf("Node address being freed: %p\n", node);
    if (node != NULL)
    {
        for (int i = 0; i < 4; i++)
        {
            freeNodeMemory(node -> children[i]);
        }
        free(node);
        node = NULL;
    }
}

void buildGravityField(Node* node, Body* bodies[], int numBodies)
{
    node = allocateNodeMemory();
    node -> center[0] = SCREEN_WIDTH / 2;
    node -> center[1] = SCREEN_HEIGHT / 2;

    for(int i = 0; i < numBodies; i++)
    {
        insertBody(node, bodies[i]);
    }
    printf("Node address created: %p\n", node);
}

        checkPosition(bodies, numBodies);
        buildGravityField(node, bodies, numBodies);
        printf("Node address after call to buildGravityField: %p\n", node);
        calculateForces(node);
        updateBodies(bodies, numBodies);
        updateGraphics(renderer, bodies, numBodies);

随后运行该程序会产生一个重复的列表,说明正在创建节点结构,但实际上并未被释放。

craig@Vera:~/C_Programs/Console/Gravity/bin/Release$ ./Gravity 
Node address created: 0x55b4f9f5a2b0
Node address after call to buildGravityField: (nil)
Node address being freed: (nil)
Node address created: 0x55b4fa2e3c70
Node address after call to buildGravityField: (nil)
Node address being freed: (nil)
Node address created: 0x55b4f9f4e290
Node address after call to buildGravityField: (nil)
Node address being freed: (nil)

这个循环在程序执行的整个生命周期中持续进行。您可能没有意识到内存泄漏,因为节点结构尺寸相当小,因此内存消耗会很慢。

本例中的罪魁祸首是“buildGravityField”函数。我将让本网站上其他更有学识的支持者讨论这里发生的情况,但在参数列表中,“Node * node”被处理为按值而不是按引用的参数通道。因此,即使在函数内生成地址值并将其放入函数内的指针“节点”中,当函数离开时,该值也不会从函数返回/转义。

地址值需要以与“allocateNodeMemory”函数类似的方式返回。考虑到这一点,以下是“buildGravityField”函数的重构代码以及该函数的重构调用。

Node * buildGravityField(Body* bodies[], int numBodies)
{
    Node *node = allocateNodeMemory();      /* Define node pointer within function  */
    node -> center[0] = SCREEN_WIDTH / 2;
    node -> center[1] = SCREEN_HEIGHT / 2;

    for(int i = 0; i < numBodies; i++)
    {
        insertBody(node, bodies[i]);
    }
    printf("Node address created: %p\n", node);

    return node;                            /* Return the generated node address    */
}

        checkPosition(bodies, numBodies);
        //buildGravityField(node, bodies, numBodies);
        node = buildGravityField(bodies, numBodies);
        printf("Node address after call to buildGravityField: %p\n", node);
        calculateForces(node);
        updateBodies(bodies, numBodies);
        updateGraphics(renderer, bodies, numBodies);

编译并重新测试代码,得到以下终端输出。

craig@Vera:~/C_Programs/Console/Gravity/bin/Release$ ./Gravity 
Node address created: 0x55d2a0ba8160
Node address after call to buildGravityField: 0x55d2a0ba8160
Node address being freed: 0x55d2a0ba8160
Node address being freed: (nil)
Node address being freed: (nil)
Node address being freed: (nil)
Node address being freed: (nil)
Node address created: 0x55d2a0ba8160
Node address after call to buildGravityField: 0x55d2a0ba8160
Node address being freed: 0x55d2a0ba8160
Node address being freed: (nil)
Node address being freed: (nil)
Node address being freed: (nil)
Node address being freed: (nil)

可以看到,节点结构被创建、使用和释放。然后利用相同的存储区域重复该循环。我不知道其中是否还存在其他问题,但我想提醒您注意明显的内存泄漏。

这里的主要收获是深入研究“C”教程,尤其是与函数和参数使用以及调试工具的使用有关的内容。

© www.soinside.com 2019 - 2024. All rights reserved.