I'm trying to create a small game with a chess board and only on knight, the player attempt to move it on every box of the board. My program works as expected, but I've an uncaught exception at the end which is thrown by SDL_Quit, but I strived to correctly free all used resources before exiting. Someone said me my problem may be caused by the goto, but I tried to remove and replacing it by ifs, but the problem persisted. I'm on Windows 10 (x64), and compiled with Visual Studio, but I downloaded and installed a virtual machine of Ubuntu (x64) to perform a memory check with valgrind. My code :
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <SDL.h>
#include <stdbool.h>
#include <stdio.h>
#include <SDL_image.h>
/*
A knight X xan move on up to 8 different positions :
+---+---+---+---+---+
| | O | | O | |
+---+---+---+---+---+
| O | | | | O |
+---+---+---+---+---+
| | | X | | |
+---+---+---+---+---+
| O | | | | O |
+---+---+---+---+---+
| | O | | O | |
+---+---+---+---+---+
*/
#define MAX_POSSIBLE_MOVES 8
#define BOARD_SIZE 8
#define BOX_SIZE 75
typedef struct {
int x;
int y;
} coords_t;
coords_t get_clicked_box_coords(SDL_Event* click_event)
{
return (coords_t) { .x = click_event->button.x / BOX_SIZE, .y = click_event->button.y / BOX_SIZE };
}
#define INVALID_COORDS (coords_t) { .x = -1, .y = -1 }
typedef bool board_t[BOARD_SIZE][BOARD_SIZE];
bool are_coords_valid(coords_t coords)
{
return coords.x >= 0 && coords.x < BOARD_SIZE && coords.y >= 0 && coords.y < BOARD_SIZE;
}
bool is_move_correct(coords_t current_position, coords_t move, board_t board)
{
const int distance_x = abs(move.x - current_position.x);
const int distance_y = abs(move.y - current_position.y);
return are_coords_valid(move) && !board[move.y][move.x] && ((distance_x == 2 && distance_y == 1) || (distance_x == 1 && distance_y == 2));
}
bool can_move(board_t board, coords_t current_position)
{
if (current_position.x == -1) {
return true;
}
const int increment_x[BOARD_SIZE] = { 1, 1, -1, -1, 2, 2, -2, -2 };
const int increment_y[BOARD_SIZE] = { 2, -2, 2, -2, 1, -1, 1, -1 };
for (int i = 0; i < BOARD_SIZE; i++) {
const coords_t move = { .x = current_position.x + increment_x[i], .y = current_position.y + increment_y[i] };
if (is_move_correct(current_position, move, board)) {
return true;
}
}
return false;
}
void draw_board(board_t board, coords_t current_position, SDL_Renderer* renderer, SDL_Texture* knight)
{
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
SDL_Rect rect = { .x = x * BOX_SIZE, .y = y * BOX_SIZE, .w = BOX_SIZE, .h = BOX_SIZE };
if (are_coords_valid(current_position) && is_move_correct(current_position, (coords_t) { .x = x, .y = y }, board)) {
SDL_SetRenderDrawColor(renderer, 0x99, 0x66, 0xCC, SDL_ALPHA_OPAQUE);
}
else if (board[y][x]) {
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
}
else if (x % 2 == y % 2) {
SDL_SetRenderDrawColor(renderer, 0x79, 0x44, 0x3B, SDL_ALPHA_OPAQUE);
}
else {
SDL_SetRenderDrawColor(renderer, 0xE3, 0xDA, 0xC9, SDL_ALPHA_OPAQUE);
}
SDL_RenderFillRect(renderer, &rect);
SDL_RenderDrawRect(renderer, &rect);
if (y == current_position.y && x == current_position.x) {
SDL_RenderCopy(renderer, knight, NULL, &rect);
}
}
}
}
void update_window_title(SDL_Window* window, unsigned score)
{
char new_title[22] = { 0 };
snprintf(new_title, 22, "Cavalier | Score : %u", score);
SDL_SetWindowTitle(window, new_title);
}
#define RETURN_CODE_OK 0
#define RETURN_CODE_ERROR_INIT 2
#define RETURN_CODE_ERROR_ALLOC 1
int main(int argc, char* argv[])
{
int return_code = RETURN_CODE_OK;
board_t board = { false };
coords_t current_position = INVALID_COORDS;
unsigned score = 0;
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Surface* knight = NULL;
SDL_Texture* knight_texture = NULL;
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
fprintf(stderr, "Error while initializing SDL : '%s'.\n", SDL_GetError());
return_code = RETURN_CODE_ERROR_INIT;
}
if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG) {
fprintf(stderr, "Error while initializing PNG loader : '%s'.\n", IMG_GetError());
return_code = RETURN_CODE_ERROR_INIT;
}
if (return_code == RETURN_CODE_OK) {
window = SDL_CreateWindow("Cavalier", 100, 100, 600, 600, SDL_WINDOW_SHOWN);
if (window == NULL) {
fprintf(stderr, "Error while creating window : '%s'.\n", SDL_GetError());
return_code = RETURN_CODE_ERROR_ALLOC;
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (renderer == NULL) {
fprintf(stderr, "Error while creating renderer : '%s'.\n", SDL_GetError());
return_code = RETURN_CODE_ERROR_ALLOC;
}
knight = IMG_Load("knight.png");
if (knight == NULL) {
fprintf(stderr, "Error while loading knight.png : '%s'.\n", SDL_GetError());
return_code = RETURN_CODE_ERROR_ALLOC;
}
knight_texture = SDL_CreateTextureFromSurface(renderer, knight);
if (knight_texture == NULL) {
fprintf(stderr, "Error while converting knight.png into texture : '%s'.\n", SDL_GetError());
return_code = RETURN_CODE_ERROR_ALLOC;
}
if (return_code != RETURN_CODE_ERROR_ALLOC) {
SDL_Event event;
bool is_window_open = true;
update_window_title(window, 0);
while (is_window_open && can_move(board, current_position)) {
coords_t clicked_box = INVALID_COORDS;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
is_window_open = false;
break;
}
if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) {
clicked_box = get_clicked_box_coords(&event);
if (current_position.x == -1 || is_move_correct(current_position, clicked_box, board)) {
update_window_title(window, ++score);
break;
}
else {
clicked_box = INVALID_COORDS;
}
}
}
if (are_coords_valid(clicked_box)) {
if (current_position.x != -1) {
board[current_position.y][current_position.x] = true;
}
current_position = clicked_box;
}
SDL_RenderClear(renderer);
draw_board(board, current_position, renderer, knight_texture);
SDL_RenderPresent(renderer);
SDL_Delay(1000 / 60);
}
printf("Score : %u.\nHit [Enter] to close...\n", score);
scanf("%*[^\n]");
}
}
if (knight_texture != NULL) {
SDL_DestroyTexture(knight_texture);
}
if (knight != NULL) {
SDL_FreeSurface(knight);
}
if (renderer != NULL) {
SDL_DestroyRenderer(renderer);
}
if (window != NULL) {
SDL_DestroyWindow(window);
}
IMG_Quit();
SDL_Quit();
return return_code;
}
An excerpt of valgrind's log (full log) :
==15799== Invalid read of size 8
==15799== at 0x40286C8: strncmp (strcmp.S:172)
==15799== by 0x400668D: is_dst (dl-load.c:216)
==15799== by 0x400810E: _dl_dst_count (dl-load.c:253)
==15799== by 0x400810E: expand_dynamic_string_token (dl-load.c:395)
==15799== by 0x40082B7: fillin_rpath.isra.0 (dl-load.c:483)
==15799== by 0x4008602: decompose_rpath (dl-load.c:654)
==15799== by 0x400ABF5: cache_rpath (dl-load.c:696)
==15799== by 0x400ABF5: cache_rpath (dl-load.c:677)
==15799== by 0x400ABF5: _dl_map_object (dl-load.c:2165)
==15799== by 0x4003494: openaux (dl-deps.c:64)
==15799== by 0x4BC3C27: _dl_catch_exception (dl-error-skeleton.c:208)
==15799== by 0x4003C7B: _dl_map_object_deps (dl-deps.c:248)
==15799== by 0x400EA0E: dl_open_worker_begin (dl-open.c:592)
==15799== by 0x4BC3C27: _dl_catch_exception (dl-error-skeleton.c:208)
==15799== by 0x400DF99: dl_open_worker (dl-open.c:782)
==15799== Address 0x5e44009 is 9 bytes inside a block of size 15 alloc'd
==15799== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==15799== by 0x40271FF: malloc (rtld-malloc.h:56)
==15799== by 0x40271FF: strdup (strdup.c:42)
==15799== by 0x4008594: decompose_rpath (dl-load.c:629)
==15799== by 0x400ABF5: cache_rpath (dl-load.c:696)
==15799== by 0x400ABF5: cache_rpath (dl-load.c:677)
==15799== by 0x400ABF5: _dl_map_object (dl-load.c:2165)
==15799== by 0x4003494: openaux (dl-deps.c:64)
==15799== by 0x4BC3C27: _dl_catch_exception (dl-error-skeleton.c:208)
==15799== by 0x4003C7B: _dl_map_object_deps (dl-deps.c:248)
==15799== by 0x400EA0E: dl_open_worker_begin (dl-open.c:592)
==15799== by 0x4BC3C27: _dl_catch_exception (dl-error-skeleton.c:208)
==15799== by 0x400DF99: dl_open_worker (dl-open.c:782)
==15799== by 0x4BC3C27: _dl_catch_exception (dl-error-skeleton.c:208)
==15799== by 0x400E34D: _dl_open (dl-open.c:883)
I compiled with SDL2 v2.0.22 and SDL2_image v2.6.1.
My picture knight.png :
But I don't understand valgrind's log, because invalid writes are from functions I never used explicitly (but are called internally), so where is the problem ? Did I a mistake ? Or did a library do ? And what can I do to make my program work correctly ?
The last thing is that I shared my whole code to many people to make them test it, but some had no problem at all whereas one had a far bigger memory leak than me (about 150'000 bytes whereas I only have up to hundreds of bytes), and I don't understand what is the file dl-open.c ? An asset loader for SDL ?
Thanks for any help !
EDIT : Code updated (without goto), I'm currently trying to make a minimal reproducible example as stated in the comments.