它基本上是通过 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;
}
对之前的代码迭代进行了一些测试后,很高兴看到程序似乎表现得更好了。然而,当我调试之前的代码时,我遇到了内存泄漏,因为各种节点正在构建但没有正确释放。考虑到这一点,我在“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”教程,尤其是与函数和参数使用以及调试工具的使用有关的内容。