在函数内创建渲染器时出现 SDL2 段错误

SDL2 Segfault when Creating a Renderer inside a Function

使用 sdl2 库,我试图在函数内创建 windows、表面和渲染器,但我一直收到段错误。我的 init_display 函数在这里:

void init_display (SDL_Window *window, SDL_Surface *surface, SDL_Renderer *renderer, int window_width, int window_height) {

    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow("Cellular Automaton", 0, 0, window_width, window_height, SDL_WINDOW_SHOWN);
    surface = SDL_GetWindowSurface(window);
    renderer = SDL_CreateSoftwareRenderer(surface);

    SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
    SDL_RenderClear(renderer);
}

在执行期间,我的程序然后调用第二个函数,该函数使用刚刚创建的渲染器,特别是 SDL_SetRenderDrawColor

void draw_blocks (SDL_Renderer *renderer, int grid[], int x_blocks, int y_blocks, int block_size, int border_size, int window_width, int window_height) {
    int x, y;

    SDL_Rect blocks[x_blocks][y_blocks];
    for (y = 0; y < y_blocks; ++y) {
        for (x = 0; x < x_blocks; ++x) {
            blocks[x][y].x = (x * (block_size + border_size)) + border_size;
            blocks[x][y].y = (y * (block_size + border_size)) + border_size;
            blocks[x][y].w = block_size;
            blocks[x][y].h = block_size;
        }
    }

    for (x = 0; x < (x_blocks * y_blocks); ++x) {
        if (grid[x] == 1) {
            SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
            SDL_RenderFillRect (renderer, &blocks[x % x_blocks][x / x_blocks]);
        }
        else if (grid[x] == 0) {
                SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
                SDL_RenderFillRect (renderer, &blocks[x % x_blocks][x / x_blocks]);

        }
    }
}

我最终通过这个函数 draw_blocks 跟踪了段错误并进入 SDL_SetRenderDrawColor。这是我的 gdb 输出:

(gdb) run

Starting program: /home/arch/dev/cell/cell 

[Thread debugging using libthread_db enabled]

Using host libthread_db library "/usr/lib/libthread_db.so.1".

Xlib:  extension "GLX" missing on display ":0.0".



Breakpoint 1, draw_borders (renderer=0xffffffffffffffff, x_blocks=80, y_blocks=80, block_size=8, border_size=1, window_width=721, window_height=721) at ./cell.c:112

112     SDL_SetRenderDrawColor(renderer, 0x7F, 0x7F, 0x7F, 0xFF);

(gdb) step

SDL_SetRenderDrawColor (a=0xffffffffffffffff, b=127 '7', c=127 '7', d=127 '7', e=255 '7') at /home/arch/downloads/sdl/src/dynapi/SDL_dynapi_procs.h:365

365 SDL_DYNAPI_PROC(int,SDL_SetRenderDrawColor,(SDL_Renderer *a, Uint8 b, Uint8 c, Uint8 d, Uint8 e),(a,b,c,d,e),return)

(gdb) next

SDL_SetRenderDrawColor_REAL (renderer=0xffffffffffffffff, r=127 '7', g=127 '7', b=127 '7', a=255 '7') at /home/arch/downloads/sdl/src/render/SDL_render.c:1281

1281    {

(gdb) step

1282        CHECK_RENDERER_MAGIC(renderer, -1);

(gdb) step



Program received signal SIGSEGV, Segmentation fault.

0x00007ffff7b0c410 in SDL_SetRenderDrawColor_REAL (renderer=0xffffffffffffffff, r=127 '7', g=127 '7', b=127 '7', a=255 '7') at /home/arch/downloads/sdl/src/render/SDL_render.c:1282

1282        CHECK_RENDERER_MAGIC(renderer, -1);

(gdb) up

#1  0x00000000004014b6 in draw_borders (renderer=0xffffffffffffffff, x_blocks=80, y_blocks=80, block_size=8, border_size=1, window_width=721, window_height=721) at ./cell.c:112

112     SDL_SetRenderDrawColor(renderer, 0x7F, 0x7F, 0x7F, 0xFF);

(gdb) up

#2  0x0000000000400d19 in main () at ./cell.c:38

38      draw_borders (renderer, x_blocks, y_blocks, block_size, border_size, window_width, window_height);

(gdb) up

Initial frame selected; you cannot go up.

(gdb)

我觉得它是指针和引用的问题,但我现在对这些没有太多经验。根据我的阅读和思考,我认为使用 renderer = SDL_CreateSoftwareRenderer(surface); 创建的渲染器只是 init_display 函数的本地渲染器,不会传递给主函数; SDL_SetRenderDrawColor然后尝试使用不存在的渲染器,但我可能错了。我尝试研究并尝试各种修复,但我要么最终创建了一个 renderer** 参数(SDL_SetRenderDrawColor 需要 renderer*),要么我最终完全取消引用指针并使其成为 renderer .如何在 main 之外的函数中正确创建渲染器?这甚至是与我的段错误相关的问题吗?

您的问题出在 init_display 函数上。在 C 中,函数参数按值传递,因此当您执行 window = SDL_CreateWindow 时,您实际上更改了函数局部 window 变量,而您最初传递给此函数的变量值保持不变。

但是,您可以传递指针到指针:

void init_display (SDL_Window **window, SDL_Surface **surface, SDL_Renderer **renderer, int window_width, int window_height) {

    SDL_Init(SDL_INIT_VIDEO);

    *window = SDL_CreateWindow("Cellular Automaton", 0, 0, window_width, window_height, SDL_WINDOW_SHOWN);
    *surface = SDL_GetWindowSurface(*window);
    *renderer = SDL_CreateSoftwareRenderer(*surface);

    SDL_SetRenderDrawColor(*renderer, 0xFF, 0xFF, 0xFF, 0xFF);
    SDL_RenderClear(*renderer);
}

(不是一个好的代码,过度使用指针取消引用,但这会起作用)

然后当你调用这个函数时:

SDL_Window *window;
SDL_Surface *surface;
SDL_Renderer *renderer;
init_display(&window, &surface, &renderer);

其他代码不需要任何更改。

您还可以将指针传递给具有您需要的所有字段的结构,并从函数中填充该结构。当你有很多字段时,这实际上很常见。

附带说明 - 如果您只需要渲染器并且对 window 或表面不感兴趣,为什么不直接将渲染器设为函数的 return 值?例如。 SDL_Renderer *init_display(int w, int h)

上面代码的稍微好一点的版本会在局部变量中缓存指针以消除不必要的取消引用:

void init_display (SDL_Window **window, SDL_Surface **surface, SDL_Renderer **renderer, int window_width, int window_height) {

    SDL_Init(SDL_INIT_VIDEO);

    SDL_Window *_window = SDL_CreateWindow("Cellular Automaton", 0, 0, window_width, window_height, SDL_WINDOW_SHOWN);
    SDL_Surface *_surface = SDL_GetWindowSurface(_window);
    SDL_Renderer *_renderer = SDL_CreateSoftwareRenderer(_surface);

    SDL_SetRenderDrawColor(_renderer, 0xFF, 0xFF, 0xFF, 0xFF);
    SDL_RenderClear(_renderer);

    *window = _window;
    *surface = _surface;
    *renderer = _renderer;
}