Недавно я пытался изучить SDL, графическую библиотеку, среди прочего, для C. Я не продвинулся слишком далеко, но с основами, которые я изучил из этого урока (Да, я знаю, что это для C ++, но кажется, что большинство вещей все еще то же самое), я попытался создайте «шаблонную» SDL-программу для запуска всех моих будущих программ. Вот что у меня было:
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags);
void mainLoop(SDL_Window *window, SDL_Surface *surface);
void closeProg(SDL_Window *window);
SDL_Surface *loadImage(char *path);
int main(int argc, char *args[]) {
SDL_Window *window = NULL;
SDL_Surface *surface = NULL;
char windowName[13] = "SDL Tutorial";
int windowXPos = SDL_WINDOWPOS_UNDEFINED;
int windowYPos = SDL_WINDOWPOS_UNDEFINED;
int windowWidth = 600;
int windowHeight = 600;
int flags = SDL_WINDOW_SHOWN;
if (!initProg(&window, &surface, windowName, windowXPos, windowYPos, windowWidth, windowHeight, flags)) {
return 1;
}
mainLoop(window, surface);
closeProg(window);
return 0;
}
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags) {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
printf("Failed to initialize SDL.\nError: %s\n", SDL_GetError());
return 0;
} else {
*window = SDL_CreateWindow(name, x, y, w, h, flags);
if (*window == NULL) {
printf("Failed to create a window.\nError:%s\n", SDL_GetError());
return 0;
} else {
*surface = SDL_GetWindowSurface(*window);
return 1;
}
}
}
void mainLoop(SDL_Window *window, SDL_Surface *surface) {
// Simple program to fade between white and black background
int g = 0;
int diff = -1;
int quit = 0;
SDL_Event e;
while (!quit) {
while(SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = 1;
}
}
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, g, g, g));
SDL_UpdateWindowSurface(window);
if (g == 0 || g == 255) {
diff *= -1;
}
g += diff;
SDL_Delay(10);
}
}
void closeProg(SDL_Window *window) {
SDL_DestroyWindow(window);
window = NULL;
SDL_Quit();
}
Программа разделена на три секции: одна для инициализации SDL, одна для основной программы и одна для закрытия SDL и освобождения поверхностей.
Я понял, что проблема в том, что если бы я создал другую поверхность, мне пришлось бы добавить код в функцию closeProg()
, чтобы освободить поверхность в конце. NB: Имейте в виду, что эти "поверхности" являются просто указателями на фактическую вещь, с которой, насколько я знаю, вы на самом деле не взаимодействуете, вы просто имеете дело с указателем на нее.
Чтобы обойти это, я решил создать массив этих указателей на поверхности, а затем создать функцию, которая создает поверхность и добавляет ее указатель на массив. Это позволило бы мне в конце функции closeProg()
go пройти через каждую поверхность и освободить ее, а затем освободить массив. (Обратите внимание, что поверхность окна не добавляется в этот список, поскольку она автоматически освобождается с помощью функции SDL_DestroyWindow()
)
Вот объявление этого нового массива:
// The pointer to the array. Currently NULL until something is added
SDL_Surface **surfaces = NULL;
// Keeps track of the size of the array
size_t surfaceCount = 0;
Это функция, добавляющая в массив:
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
size_t new_count = *surfaceCount + 1;
SDL_Surface **temp = realloc(surfaces, sizeof(*temp) * new_count);
if (temp == NULL) {
printf("Failed to reallocate to %d bytes of memory.", sizeof(*temp) * new_count);
return 0;
} else {
surfaces = temp;
*(surfaces + new_count - 1) = surface;
*surfaceCount = new_count;
return 1;
}
}
И здесь она используется. Обратите внимание, что loadImage()
возвращает поверхность изображения, а showSurfaces()
печатает содержимое массива surfaces
.
// Check contents before adding anything
showSurfaces(surfaces, *surfaceCount);
// Add an image
SDL_Surface *fire = loadImage("./fire.bmp");
addSurface(fire, surfaces, surfaceCount);
// Check contents again
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents again
SDL_Surface *ice = loadImage("./ice.bmp");
addSurface(ice, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents a final time
SDL_Surface *man = loadImage("./man.bmp");
addSurface(man, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
До первых двух поверхностей все прошло хорошо. Однако, когда я попытался добавить третью поверхность, программа зависла непосредственно перед закрытием (указывает, что что-то не так в функции closeProg()
?). Я решил распечатать содержимое массива, и вот что я получил.
No current surfaces.
Current surfaces:
Index 0: 00753C98
Current surfaces:
Index 0: 00753C98
Index 1: 00754780
Current surfaces:
Index 0: 02805150
Index 1: 008F00C0
Index 2: 201339FC
В первых двух распечатках все выглядело хорошо, но в третьем вы Я могу заметить, что предыдущие адреса изменились. Это происходило неоднократно и независимо от того, сколько поверхностей я добавил. После первых двух перераспределений содержимое массива продолжало меняться.
Я думаю, именно поэтому программа зависает при закрытии, потому что в функции closeProg программе сказано освободить неизвестный указатель, который не является поверхностью следовательно, он падает. Не говоря уже о том, что я также установил этот указатель на NULL, и кто знает, что еще это может вызвать.
Является ли это изменение содержимого массива нормальным? И если нет, я был бы очень признателен, если бы вы могли помочь мне найти причину этого странного поведения. Я повторяю, что я ПОЛНЫЙ НАЧИНАЮЩИЙ в этом и поэтому любая помощь, даже в вопросах, не связанных с этим вопросом, будет ОТЛИЧНО оценена. Спасибо вам в advance:)
Для справки, вот изображения, которые я использовал .
Вот полный код, если вам интересно:
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags);
void mainLoop(SDL_Window *window, SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount);
void closeProg(SDL_Window *window, SDL_Window **surfaces, size_t surfaceCount);
SDL_Surface *loadImage(char *path);
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount);
int main(int argc, char *args[]) {
SDL_Window *window = NULL;
SDL_Surface *surface = NULL;
// The pointer to the array. Currently NULL untill something is added
SDL_Surface **surfaces = (SDL_Surface *)calloc(1, sizeof(*surfaces));
// Keeps track of the size of the array
size_t surfaceCount = 0;
char windowName[13] = "SDL Tutorial";
int windowXPos = SDL_WINDOWPOS_UNDEFINED;
int windowYPos = SDL_WINDOWPOS_UNDEFINED;
int windowWidth = 600;
int windowHeight = 600;
int flags = SDL_WINDOW_SHOWN;
if (!initProg(&window, &surface, windowName, windowXPos, windowYPos, windowWidth, windowHeight, flags)) {
return 1;
}
mainLoop(window, surface, surfaces, &surfaceCount);
closeProg(window, surfaces, surfaceCount);
return 0;
}
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags) {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
printf("Failed to initialize SDL.\nError: %s\n", SDL_GetError());
return 0;
} else {
*window = SDL_CreateWindow(name, x, y, w, h, flags);
if (*window == NULL) {
printf("Failed to create a window.\nError:%s\n", SDL_GetError());
return 0;
} else {
*surface = SDL_GetWindowSurface(*window);
return 1;
}
}
}
void mainLoop(SDL_Window *window, SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
// Simple program to fade between white and black background
int g = 0;
int diff = -1;
// Check contents before adding anything
showSurfaces(surfaces, *surfaceCount);
// Add an image
SDL_Surface *fire = loadImage("./fire.bmp");
addSurface(fire, surfaces, surfaceCount);
// Check contents again
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents again
SDL_Surface *ice = loadImage("./ice.bmp");
addSurface(ice, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents a final time
SDL_Surface *man = loadImage("./man.bmp");
addSurface(man, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
int quit = 0;
SDL_Event e;
while (!quit) {
while(SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = 1;
}
}
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, g, g, g));
SDL_UpdateWindowSurface(window);
if (g == 0 || g == 255) {
diff *= -1;
}
g += diff;
SDL_Delay(10);
}
}
void closeProg(SDL_Window *window, SDL_Window **surfaces, size_t surfaceCount) {
// Go through the array and free each surface.
for (int i = 0; i < surfaceCount; i++){
SDL_FreeSurface(*(surfaces + i));
*(surfaces + i) = NULL;
}
// Free the array itself.
free(surfaces);
SDL_DestroyWindow(window);
window = NULL;
SDL_Quit();
}
SDL_Surface *loadImage(char *path) {
SDL_Surface *image = SDL_LoadBMP(path);
if (image == NULL) {
printf("Failed to load image.\nError: %s\n", SDL_GetError());
}
return image;
}
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
size_t new_count = *surfaceCount + 1;
SDL_Surface **temp = realloc(surfaces, sizeof(*temp) * new_count);
if (temp == NULL) {
printf("Failed to reallocate to %d bytes of memory.", sizeof(*temp) * new_count);
return 0;
} else {
surfaces = temp;
*(surfaces + new_count - 1) = surface;
*surfaceCount = new_count;
return 1;
}
}
void showSurfaces(SDL_Surface **surfaces, size_t surfaceCount) {
if (surfaceCount == 0) {
printf("\nNo current surfaces.\n");
} else {
printf("\nCurrent surfaces:\n");
for (int i = 0; i < surfaceCount; i++) {
printf("\tIndex %d: %p\n", i, *(surfaces + i));
}
putchar('\n');
}
}
Если вы можете повторить эту ошибку, пожалуйста, прокомментируйте ниже, чтобы я знал, что это не что-то не так с моей машиной или что-то в этом роде.
ПРИМЕЧАНИЕ: я использую SDL 2.0