此答案涉及 MinGW / GCC,而不是 Visual Studio。
此答案主要针对 Windows。在其他操作系统上事情会更容易,请参阅答案底部的摘要。
我试图让这个答案保持简单,这样即使是新手也可以使用它。
常见错误
您可能会遇到的常见错误是:
-
SDL.h: No such file or directory(编译时)
-
undefined reference to各种功能(链接时)
- 神秘的
.dll 相关错误(运行程序时)。
此列表按从差到好排序。如果您更改某些内容并遇到不同的错误,请使用此列表来判断您是否使事情变得更好或更糟。
请参阅下面的答案以了解如何解决所有这些问题。
序言
在我们修复错误之前,我想告诉你一些事情。
0。不要听从不好的建议。
一些资源会建议您使用#define SDL_MAIN_HANDLED 或#undef main。不要盲目地听从这个建议,这不是 SDL2 的用途。
如果你做的每件事都正确,那就没有必要了。首先了解预期的方法。然后,您可以研究它的具体作用,并做出明智的决定。
1.弄清楚如何直接从控制台编译,你可以稍后开始使用 IDE。
如果您使用的是 IDE,我建议首先确保您能够直接从控制台编译程序,以排除任何 IDE 配置问题。弄清楚之后,您可以在 IDE 中使用相同的编译器选项。
如果您刚开始,我还建议避免使用 CMake,以排除任何与 CMake 相关的问题。您可以稍后开始使用它。
2。下载正确的 SDL2 文件。确保您拥有正确的文件。您需要来自 here 的名为 SDL2-devel-2.0.x-mingw.tar.gz 的存档。
在任何目录中解压它,最好是靠近源代码的某个地方。直接解压到编译器目录通常被认为是一种不好的做法。
3.了解编译器标志和链接器标志之间的区别。“标志”是您在构建程序时在命令行中指定的选项。当您使用单个命令时,例如 g++ foo.cpp -o foo.exe,您的所有标志都会添加到同一个位置(添加到该单个命令)。
但是当您分两步构建程序时,例如:
-
g++ foo.cpp -c -o foo.o(正在编译)
-
g++ foo.o -o foo.exe(链接)
您必须知道要添加标志的两个命令中的哪一个。需要添加到第一个命令的标志是“编译器标志”,需要添加到第二个命令的标志是“链接器标志”。在下面的答案中,当告诉您添加标志时,我将指定它是编译器标志还是链接器标志。
大多数 IDE 将要求您分别指定编译器和链接器标志,因此即使您现在使用单个命令,最好知道哪个标志在哪里。
除非另有说明,否则标志的顺序无关紧要。
SDL.h: No such file or directory
或与包含SDL.h 或SDL2/SDL.h 相关的任何类似错误。
您需要告诉编译器SDL.h 位于哪个目录中。它位于您下载的 SDL 文件中(参见序言)。
将以下内容添加到您的编译器标志中:-I,后跟一个目录。
示例:-IC:/Users/HolyBlackCat/Downloads/SDL2-2.0.12/x86_64-w64-mingw32/include/SDL2。 (相对路径也可以,例如-ISDL2-2.0.12/x86_64-w64-mingw32/include/SDL2。)
请注意,标志可能会有所不同,具体取决于您编写#include 的方式:
- 如果您使用
#include <SDL.h>,则路径应以.../include/SDL2 结尾(如上)。这是推荐的方式。
- 如果您使用
#include <SDL2/SDL.h>,则路径应以.../include 结尾。
undefined reference to各种功能
错误消息将提及各种SDL_... 函数(通常是您在程序中使用的函数)和/或WinMain。如果提及SDL_main,请参阅下面的“仅限undefined reference to SDL_main”部分。
您需要添加以下链接器标志:-lmingw32 -lSDL2main -lSDL2。 顺序很重要。标志必须出现在任何 .c/.cpp/.o 文件之后。
编写-lSDL2main -lSDL2 告诉链接器使用您下载的SDL 文件中包含的名为libSDL2main.a、libSDL2.a(或libSDL2.dll.a)的文件。 您还需要一个标志来告诉链接器在哪里寻找那些.a 文件。将以下内容添加到您的链接器标志中:-L,然后是目录。
例如:-LC:/Users/HolyBlackCat/Desktop/SDL2-2.0.12/x86_64-w64-mingw32/lib。 (相对路径也可以,例如-LSDL2-2.0.12/x86_64-w64-mingw32/lib。)
我添加了所有这些标志,但没有任何改变:
如果您这样做并且仍然得到相同的 undefined reference 错误,则您可能使用了错误的 SDL .a 文件。您下载的存档包含两组文件:i686-w64-mingw32(32 位)和x86_64-w64-mingw32(64 位)。您必须使用与您的编译器匹配的文件,它也可以是 32 位或 64 位。打印 (8*sizeof(void*)) 以查看您的编译器是 32 位还是 64 位。即使您认为自己使用了正确的文件,也请尝试其他文件以确保确定。
一些 MinGW 版本可以使用 -m32 和 -m64 标志在 32 位和 64 位模式之间切换(这些可能必须添加到编译器和链接器标志)。你也可以试试。
我得到undefined reference 到一个特定的功能:
• 仅限undefined reference to WinMain
有几种可能性,所有这些都在上一节中介绍过:
- 您忘记了
-lmingw32 和/或-lSDL2main 标志。
您必须按照这个确切的顺序使用以下链接器标志,after
任何.c/.cpp/.o 文件:-lmingw32 -lSDL2main -lSDL2
- 您使用的
libSDL2main.a 文件与您的编译器不匹配(32 位文件与 64 位编译器,反之亦然)。
在解决这个问题时尽量避免#define SDL_MAIN_HANDLED或#undef main,解释见前言。
• 仅限undefined reference to SDL_main
你需要有一个main 函数。您的main 函数必须类似于int main(int, char **)。 不 int main() 和不 void main()。这是 SDL2 的怪癖之一。
允许添加任何参数名称,例如int main(int argc, char **argv)。第二个参数也可以写成char *[] 或名称:char *argv[]。不允许进行其他更改。
如果您的项目有多个源文件,请确保在定义 main 函数的文件中包含 SDL.h,即使它不直接使用 SDL。
在解决这个问题时尽量避免#define SDL_MAIN_HANDLED或#undef main,解释见前言。
.dll相关错误
您已经成功构建了一个.exe,但由于提到一些.dlls 的神秘错误而无法运行它。你快到了!
您创建的.exe 需要一些.dlls 才能运行。如果它没有找到它们,它会告诉你并拒绝运行。这很容易。它还可以找到您安装的其他一些程序遗留下来的错误版本,然后您会得到一些非常神秘的错误。
您的程序会在不同的位置寻找.dlls,但最可靠的解决方案是将它们放在与.exe 相同的目录中。首先搜索此目录,因此您可能在其他地方拥有的其他版本的 .dlls 不会干扰。
你需要弄清楚你的程序需要哪个.dlls,并将它们放在你的.exe所在的目录中(系统.dlls除外,你不需要需要复制)。
您可能会遇到两种类型的错误:
-
一个错误只是告诉您缺少 .dll。
-
SDL2.dll 丢失。 — 在您下载的 SDL 文件中。
请注意,这些文件包含两个不同的 SDL2.dlls:一个 32 位的(在 i686-w64-mingw32 目录中)和一个 64 位的(在 x86_64-w64-mingw32 中)。找一个合适的,如果有必要两个都试试。
-
其他一些.dll 丢失了。 — 它随您的编译器一起提供。查看您的gcc.exe 所在的目录。
如果您复制了一个.dll 并要求更多,这是正常的,您可能需要重复此操作约 3 次。
-
一个神秘的 .dll 相关错误。 — 您的程序在系统某处发现了错误版本的 .dll。
您需要将所有需要的非系统.dlls 复制到您的.exe 所在的目录中。
首先,复制SDL2.dll(请参阅上面的“SDL2.dll 缺失”部分了解在哪里可以找到它)。
然后,从您的编译器目录(gcc.exe 所在的目录)中复制以下.dlls:
-
libgcc_s_seh-1.dll(名称可能因您的 MinGW 版本而异,但始终以 libgcc 开头)
-
libstdc++-6.dll(仅适用于 C++,如果您使用 C 编写,请跳过)
-
libwinpthread-1.dll(名称可能因您的 MinGW 版本而异,但始终会提及 thread;有些版本根本不使用此名称)
对于某些 MinGW 发行版,这些可能不是正确的 .dlls。如果您没有看到这些或复制它们没有帮助,请继续阅读。
这应该可以解决您的所有错误。如果您想了解更多或没有帮助:
更多关于.dlls的信息
可以使用-static 链接器标志创建一个不依赖于任何(非系统).dlls 的.exe,这称为“静态链接”。很少这样做,如果您正确执行了上述步骤,则不需要这样做。这需要与平常不同的链接器标志(大约 20 个),请参阅 SDL 随附的 sdl2.pc 文件以了解确切的标志(它们位于 Libs.private 部分)。
在上一节中,我试图猜测您的程序依赖于.dlls。我怎么知道的?有几种方法可以自己弄清楚:
-
粗略的方式:
打开控制台,cd到你的.exe所在的目录,然后输入set PATH=(别担心,这不会改变你的系统PATH设置,只会影响当前的控制台会话)。
删除您可能已经复制到那里的所有.dlls。
现在如果你从这个控制台运行.exe,它将只能找到系统.dlls。它将仅在当前目录(没有)中查找非系统目录。现在您将收到明确的错误消息,说明需要什么.dlls,您可以将它们一一复制,直到它开始工作。这样你就可以知道你的程序使用的所有非系统.dlls。
请注意,此方法并非完全万无一失。 C:\Windows 和一些嵌套目录总是被搜索,即使在这种情况下。通常情况下,如果只有系统的.dlls 并不重要,但如果一些糟糕的安装程序在那里复制了一个自定义的.dll,那么这种方法将无法正常工作。
最好在C:\Windows 和嵌套目录中搜索不应该存在的.dlls,然后删除它们。查找 SDL2.dll 和编译器附带的任何 .dlls(在您的 gcc.exe 所在的目录中)。
-
文明的方式:
使用Dependency Walker 或ntldd 之类的工具。您必须检查他们的输出并确定每个.dll 是系统还是非系统。如果 .dll 随您的编译器一起提供或属于您使用的库 (SDL2.dll),则它是非系统库。任何剩余的.dlls 都是系统的,忽略它们。
其他问题
-
问:我的程序在运行时总是打开一个控制台窗口,除了我使用 SDL 打开的任何窗口。我该如何摆脱它?
-
问:我的程序有默认文件图标,但我想要一个自定义的。
-
答:您的图标必须采用.ico 格式。如果您的图形编辑器不支持它,请制作一系列常见尺寸的.pngs(例如16x16、32x32、48x48、64x64),然后使用ImageMagick将它们转换为单个.ico:magick *.png result.ico(或使用convert 而不是magick)。
创建一个扩展名为.rc的文件(比如icon.rc),在其中输入:MAINICON ICON "icon.ico"(第一部分是任意名称,您可以更改它;第二部分必须始终为ICON ,第三部分是图标的路径)。使用windres -i icon.rc -o icon.o 将文件转换为.o(编译器附带windres 程序)。链接时指定生成的.o 文件,例如g++ foo.cpp icon.o -o foo.exe.
最近版本的 SDL2 有一个很好的属性,即使用与窗口图标相同的图标,因此您不必使用 SDL_SetWindowIcon。
-
问:我收到错误'SDL_VideoMode' wasn't declared in this scope。
- 答:
SDL_VideoMode 存在于 SDL1.2 中,但不是 SDL2 的一部分。您的代码是为过时的 SDL1.2 编写的。查找处理新 SDL2 的更好教程。
我不在 Windows 上,我有什么不同?
上面的整个答案主要针对 MinGW 和 Windows。如果您不在 Windows 上,事情会变得更容易:
另一方面:
替代方法:在 Windows 上使用类似 Linux 的环境
上一节关于安装库和自动确定编译器标志的部分听起来方便吗?你也可以在 Windows 上使用它。
下载 MSYS2。您将拥有一个包管理器(您可以从中安装 SDL)、最新的编译器和一组从 Linux 移植的常用实用程序(包括pkg-config)。这解决了大多数特定于 Windows 的问题。