You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SDL3:如何通过appstate传递指针数组?代码错误排查

SDL3 appstate参数传递失败的问题排查

我先写了一个能正常渲染“Hello, World!”的SDL3程序:

#define SDL_MAIN_USE_CALLBACKS 1 // NOLINT
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

const int static WIDTH = 600;
const int static HEIGHT = 600;
const int static MAX = 255;
const float static SCALE = 4.0F;
const char static *MESSAGE = "Hello, World!"; // NOLINT

/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) { // NOLINT

  SDL_Window *window = nullptr;
  SDL_Renderer *renderer = nullptr;

  /* Create the window */
  if (!SDL_CreateWindowAndRenderer(MESSAGE, WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE,
                                   &window, &renderer)) {
    SDL_Log("Couldn't create window and renderer: %s", // NOLINT
            SDL_GetError());                           // NOLINT
    return SDL_APP_FAILURE;
  }
  *appstate = renderer;
  return SDL_APP_CONTINUE;
}

/* This function runs once per frame, and is the heart of the program. */
SDL_AppResult SDL_AppIterate(void *appstate) { // NOLINT
  auto *local_renderer = static_cast<SDL_Renderer *>(appstate);
  int width = 0;
  int height = 0;

  /* Center the message and scale it up */
  SDL_GetRenderOutputSize(local_renderer, &width, &height);
  SDL_SetRenderScale(local_renderer, SCALE, SCALE);
  const float xpos = ((static_cast<float>(width) / SCALE) -
                      (static_cast<float>(SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) *
                       SDL_strlen(MESSAGE))) /
                     2;
  const float ypos = ((static_cast<float>(height) / SCALE) -
                      SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) /
                     2;

  /* Draw the message */
  SDL_SetRenderDrawColor(local_renderer, 0, 0, 0, MAX);
  SDL_RenderClear(local_renderer);
  SDL_SetRenderDrawColor(local_renderer, MAX, MAX, MAX, MAX);
  SDL_RenderDebugText(local_renderer, xpos, ypos, MESSAGE);
  SDL_RenderPresent(local_renderer);

  return SDL_APP_CONTINUE;
}

/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { // NOLINT
  if (event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_QUIT) {
    return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
  }
  return SDL_APP_CONTINUE;
}

/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result) {
} // NOLINT misc-use-anonymous-namespace

我原本以为SDL_AppInit的appstate参数是指向数组的指针,通过*appstate = renderer;把renderer指针赋值给数组第一个元素,SDL_AppIterate能正常接收并转换为局部renderer指针。

之后我想把window指针也加入appstate,于是在SDL_AppInit中创建了void *state[2];,把state[0]设为renderer、state[1]设为window,然后尝试用appstate = (void **)&state;传递数组指针,但SDL_AppIterate中无法接收到这个数组指针(调试发现appstate是空的)。修改后的代码如下:

#define SDL_MAIN_USE_CALLBACKS 1 // NOLINT
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

const int static WIDTH = 600;
const int static HEIGHT = 600;
const int static MAX = 255;
const float static SCALE = 4.0F;
const char static *MESSAGE = "Hello, World!"; // NOLINT

/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) { // NOLINT

  SDL_Window *window = nullptr;
  SDL_Renderer *renderer = nullptr;
  void *state[2];
  
  /* Create the window */
  if (!SDL_CreateWindowAndRenderer(MESSAGE, WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE,
                                   &window, &renderer)) {
    SDL_Log("Couldn't create window and renderer: %s", // NOLINT
            SDL_GetError());                           // NOLINT
    return SDL_APP_FAILURE;
  }
  state[0] = renderer;
  state[1] = window;
  appstate = (void **)&state;
  return SDL_APP_CONTINUE;
}

/* This function runs once per frame, and is the heart of the program. */
SDL_AppResult SDL_AppIterate(void *appstate) { // NOLINT
// 调试发现appstate是空的,它应该是SDL_AppInit末尾传入的数组指针
  auto **state = static_cast<void **>(appstate);
  auto *local_renderer = static_cast<SDL_Renderer *>(state[0]);
  int width = 0;
  int height = 0;

  /* Center the message and scale it up */
  SDL_GetRenderOutputSize(local_renderer, &width, &height);
  SDL_SetRenderScale(local_renderer, SCALE, SCALE);
  const float xpos = ((static_cast<float>(width) / SCALE) -
                      (static_cast<float>(SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) *
                       SDL_strlen(MESSAGE))) /
                     2;
  const float ypos = ((static_cast<float>(height) / SCALE) -
                      SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) /
                     2;

  /* Draw the message */
  SDL_SetRenderDrawColor(local_renderer, 0, 0, 0, MAX);
  SDL_RenderClear(local_renderer);
  SDL_SetRenderDrawColor(local_renderer, MAX, MAX, MAX, MAX);
  SDL_RenderDebugText(local_renderer, xpos, ypos, MESSAGE);
  SDL_RenderPresent(local_renderer);

  return SDL_APP_CONTINUE;
}

/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { // NOLINT
  if (event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_QUIT) {
    return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
  }
  return SDL_APP_CONTINUE;
}

/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result) {
} // NOLINT misc-use-anonymous-namespace

请问我的代码哪里出错了?


问题分析与解决方案

你的代码存在两个核心错误:

1. 局部栈变量的生命周期问题

你在SDL_AppInit中定义的void *state[2];是栈上的局部变量,当SDL_AppInit函数执行完毕返回后,栈上的这个数组会被销毁,内存会被系统回收或重新使用。后续SDL的回调函数(比如SDL_AppIterate)尝试访问这个已经失效的地址,会导致未定义行为(比如拿到空指针或垃圾数据)。

2. appstate参数的赋值方式错误

SDL_AppInit的appstate参数是void**类型,它的作用是让你把要保存的状态指针写入到*appstate指向的地址,而不是修改appstate本身。你写的appstate = (void **)&state;只是修改了函数形参的指针值(形参是值传递,修改形参不会影响SDL内部保存的指针),SDL根本没有接收到你的状态数组地址。


修复方案

推荐使用动态分配的结构体来存储多个状态变量,这样内存会在堆上,生命周期可控,而且结构更清晰:

修复后的代码

#define SDL_MAIN_USE_CALLBACKS 1 // NOLINT
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

const int static WIDTH = 600;
const int static HEIGHT = 600;
const int static MAX = 255;
const float static SCALE = 4.0F;
const char static *MESSAGE = "Hello, World!"; // NOLINT

// 定义状态结构体,存储需要跨回调传递的变量
typedef struct {
    SDL_Window *window;
    SDL_Renderer *renderer;
} AppState;

/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) { // NOLINT
    SDL_Window *window = nullptr;
    SDL_Renderer *renderer = nullptr;

    /* Create the window */
    if (!SDL_CreateWindowAndRenderer(MESSAGE, WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE,
                                   &window, &renderer)) {
        SDL_Log("Couldn't create window and renderer: %s", // NOLINT
                SDL_GetError());                           // NOLINT
        return SDL_APP_FAILURE;
    }

    // 动态分配结构体内存
    AppState *state = static_cast<AppState*>(SDL_malloc(sizeof(AppState)));
    if (!state) {
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);
        SDL_Log("Failed to allocate app state");
        return SDL_APP_FAILURE;
    }
    state->window = window;
    state->renderer = renderer;

    // 将结构体指针赋值给*appstate,SDL会保存这个指针并传递给后续回调
    *appstate = state;
    return SDL_APP_CONTINUE;
}

/* This function runs once per frame, and is the heart of the program. */
SDL_AppResult SDL_AppIterate(void *appstate) { // NOLINT
    // 将appstate转换为我们定义的结构体指针
    AppState *state = static_cast<AppState*>(appstate);
    SDL_Renderer *local_renderer = state->renderer;
    int width = 0;
    int height = 0;

    /* Center the message and scale it up */
    SDL_GetRenderOutputSize(local_renderer, &width, &height);
    SDL_SetRenderScale(local_renderer, SCALE, SCALE);
    const float xpos = ((static_cast<float>(width) / SCALE) -
                      (static_cast<float>(SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) *
                       SDL_strlen(MESSAGE))) /
                     2;
    const float ypos = ((static_cast<float>(height) / SCALE) -
                      SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) /
                     2;

    /* Draw the message */
    SDL_SetRenderDrawColor(local_renderer, 0, 0, 0, MAX);
    SDL_RenderClear(local_renderer);
    SDL_SetRenderDrawColor(local_renderer, MAX, MAX, MAX, MAX);
    SDL_RenderDebugText(local_renderer, xpos, ypos, MESSAGE);
    SDL_RenderPresent(local_renderer);

    return SDL_APP_CONTINUE;
}

/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { // NOLINT
    if (event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_QUIT) {
        return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
    }
    return SDL_APP_CONTINUE;
}

/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result) {
    // 释放动态分配的结构体,同时销毁SDL资源
    if (appstate) {
        AppState *state = static_cast<AppState*>(appstate);
        SDL_DestroyRenderer(state->renderer);
        SDL_DestroyWindow(state->window);
        SDL_free(state);
    }
} // NOLINT misc-use-anonymous-namespace

关键修复点

  • 定义了AppState结构体,清晰管理多个状态变量
  • 使用SDL_malloc动态分配结构体(也可以用new,但SDL提供的内存函数更符合跨平台习惯)
  • 将结构体指针赋值给*appstate,确保SDL能正确接收并传递这个状态
  • 在SDL_AppQuit中释放结构体内存并销毁SDL资源,避免内存泄漏

内容的提问来源于stack exchange,提问作者user2309803

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 12:24:53