一文彻底解决SDL2库main函数链接问题
SDL2库开发中常出现main函数的链接错误,原因是SDL.h将main重定义为SDL_main。本文给出此问题的正确解决方案,帮助开发者正确使用SDL库。
1.问题概述
在使用SDL2库进行C/C++图形界面开发时,相信开发者会遇到在包含SDL.h后,链接器找不到main函数的问题,在Windows上,报错信息通常类似:
Undefined reference to 'WinMain'
2.原因分析
这是由于SDL.h中定义了main宏,它将main替换为SDL_main
#define main SDL_main
显然,若不进行其他处理,链接器会拒绝链接一个没有main函数的项目。SDL既然选择这样做,那么必然有其原因,我们当然可以直接
#undef main
但是,SDL作为跨平台但和操作系统交互的底层库,内置了平台特定的初始化和清理逻辑,这些逻辑包括但不限于:
1.统一处理命令行参数,如Windows下的Unicode到UTF-8的编码转换
2.调用平台特定的初始化函数,如Windows的窗口子系统,Android JNI绑定等
直接#undef main,将会失去这些自动初始化的调用,并失去对应的资源清理环节。虽然通常情况下,这样做不会带来问题,但是可能会埋下隐患,在跨平台迁移时造成一些意料之外的错误。
3.解决方案
3.1:不管
这也是大多数SDL教程所做的!如上所示,直接在#include <SDL.h>后取消定义main宏
#include <SDL.h>
#undef main
int main() {
// ...
return 0;
}
另一些教程也会这样做:
#define SDL_MAIN_HANDLED 1
这让SDL知道,应用程序已经自行处理了main函数。但是通常情况下,我们需要使用SDL的main而不是自定义它。
3.2:正确链接SDL_main
如果你查看SDL_main.h,那么你可能会被SDL所“误导”:
SDL3/SDL_main.h
/*
* Redefine main() if necessary so that it is called by SDL.
*
* In order to make this consistent on all platforms, the application's main()
* should look like this:
*
* ```c
* #include <SDL3/SDL.h>
* #include <SDL3/SDL_main.h>
*
* int main(int argc, char *argv[])
* {
* }
* ```
*/
SDL2/SDL_main.h
/**
* \file SDL_main.h
*
* The application's main() function must be called with C linkage,
* and should be declared like this:
* ```c
* #ifdef __cplusplus
* extern "C"
* #endif
* int main(int argc, char *argv[])
* {
* }
* ```
*/
遗憾的是,通常情况下,官方给出的做法并不有效,链接器仍然抱怨找不到main函数。
我们可以这样想:既然SDL将我们的main搞掉了,那么它必然会提供自己的main给操作系统,这个main是平台特定的,如在Windows上为WinMain,在Linux上就是main。
如果你使用CMake,那么在链接SDL库时,IDE通常会给出几个候选:SDL2::SDL_main, SDL2::SDL2, SDL2::SDL_test...
其中,SDL2::SDL2是SDL的核心库,任何一个使用了SDL库的程序必然已经链接了它,SDL_test是测试库,和main函数无关。那么,只有看起来有点多余的SDL_main可能有用。
实际上,SDL_main库中就包含了SDL的平台特定main函数实现,只是以动态库的形式存在。如果直接下载SDL的Release,那么不能直接跳转到其实现。
于是问题变得简单了,我们只需要尝试链接SDL_main库,以CMake为例:
add_executable(${PROJECT_NAME}
...
)
#链接SDL库
target_link_libraries(${PROJECT_NAME} PUBLIC SDL2::SDL2main SDL2::SDL2)
必须将SDL2main写在SDL2之前,即先链接SDL2main库!
然后,在写有main函数的源文件中:
#include <SDL.h>
int main(int argc, char * argv[]) {
// ...
return 0;
}
此时如果不包含SDL.h,或者取消main宏的定义,链接器反而会报错,因为SDLmain中的main函数已经被链接器识别。
在此情况下,建议按照头文件中的提示,写上main函数的两个形参,无论用不用。
这样,我们就正确地使用了SDL自带的,平台特定的初始化和清理逻辑,而不造成任何不必要的损失。
更多推荐
所有评论(0)