编译失败,#include "..." 但不是 #include <...>
Compilation fails with #include "..." but not with #include <...>
我目前正在研究 C 库 NanoVG 库。该库依赖于 OpenGL 函数并且有 2 header 个文件 nanovg.h
和 nanovg_gl.h
。后一个文件包含部分实现。为了方便起见,我把这两个 header 文件放在了 /usr/include/nanovg
.
当我尝试将以下代码编译为 object 文件时,gcc 没有报错:
// working.c
#include <GL/gl.h>
#include <nanovg/nanovg.h>
#define NANOVG_GL3_IMPLEMENTATION
#include <nanovg/nanovg_gl.h>
(命令:gcc -c working.c -o working.o
)
现在,我将 /usr/include/nanovg/
中的 header 个文件复制到工作目录,并将代码替换为:
// notworking.c
#include <GL/gl.h>
#include "nanovg.h"
#define NANOVG_GL3_IMPLEMENTATION
#include "nanovg_gl.h"
(命令:gcc -c notworking.c -o notworking.o
)
Gcc 现在抱怨一些 OpenGL 函数没有声明:
... (many more similar complaints)
src/nanovg_gl.h: In function ‘glnvg__renderDelete’:
src/nanovg_gl.h:1540:3: warning: implicit declaration of function ‘glDeleteBuffers’; did you mean ‘glSelectBuffer’? [-Wimplicit-function-declaration]
1540 | glDeleteBuffers(1, &gl->fragBuf);
| ^~~~~~~~~~~~~~~
...
为什么一个文件编译顺利而另一个文件编译不顺利?
更深一点:
使用 cpp
工具,我发现两个 pre-processed 文件之间的区别仅限于 # 指令,但就“C 内容”而言,我看不出有任何区别。下面是 pre-processed working.c
的片段。如果我从 pre-processed notworking.c
添加 # 行,则 gcc 不再编译 pre-processed working.c
并抱怨 glDeleteBuffers
.[= 缺少声明36=]
// ...
if (gl ==
// # 1533 "src/nanovg_gl.h" 3 4 // <- uncomment this line and glDeleteBuffers is considered missing by gcc
((void *)0)
// # 1533 "src/nanovg_gl.h" // <- idem
) return;
glnvg__deleteShader(&gl->shader);
if (gl->fragBuf != 0)
glDeleteBuffers(1, &gl->fragBuf); // <- the function that gcc complains about is here
// ...
编辑: 为了确保我没有做任何可能导致差异的偷偷摸摸的事情,我遵循了以下步骤,希望可以在另一台计算机上重现:
- GCC 版本:
gcc (Ubuntu 10.3.0-1ubuntu1) 10.3.0
- 复制
GL/gl.h
的版本 here 到工作目录并调用它 glfoo.h
- 将
nanovg
的 header 复制到 /usr/include/nanovg/
和 nanovg/
(相对于工作目录)。
- 在工作目录中将以下内容保存为
test.c
:
#include "glfoo.h"
#include <nanovg/nanovg.h>
#define NANOVG_GL3_IMPLEMENTATION
#include <nanovg/nanovg_gl.h>
- 运行
gcc -c test.c -o test.o
=> 编译作品
- 将第 2 行和第 4 行的 <...> 替换为“..”并且 运行 命令 => 编译失败。
只是尝试了这些确切的步骤,我能够重现它。
简直
#include <file.h>
从编译器默认列出的路径中包含文件,而
#include "file.h"
包含当前文件夹(您正在编译的文件夹)中的文件。
与您的情况一样,从 <> 切换到 "" 会导致文件丢失,从而导致编译器错误。
经过一番调查后,我找到了解决方案。 gcc
不会对系统 headers 应用与“正常”文件相同的警告级别(这主要是因为系统 headers有时会做一些奇怪的事情,这些事情不受 C 标准的支持,但对于它们附带的平台来说是“安全的”。
gcc documentation 状态(强调我的):
-Wsystem-headers
:
Print warning messages for constructs found in system header files. Warnings from system headers are normally suppressed, on
the assumption that they usually do not indicate real problems and
would only make the compiler output harder to read. Using this
command-line option tells GCC to emit warnings from system headers as
if they occurred in user code. However, note that using -Wall in
conjunction with this option does not warn about unknown pragmas in
system headers—for that, -Wunknown-pragmas
must also be used.
当您通过 <...>
包含 nanovg 时,它被视为一个系统 header。
这样做gcc -Wsystem-headers working.c
实际上会引起警告。
请注意,您的代码在 working.c
和 notworking.c
中均无效,因为 working.c
只是隐藏了警告消息。访问任何超出 GL 1.1 定义的 GL 函数的正确方法是使用 GL 扩展机制,这意味着您必须在 run-time 处查询 GL 函数指针。完整的 GL 加载程序库,如 GLEW 和 glad 可以自动为您完成。许多这些加载器(包括 GLEW 和 GLAD)通过将每个 GL 函数名称重新 #define
转换为内部函数指针来工作,因此当您包含加载器附带的 header 时,每个 GL 函数在您的代码(和 nanovg 的)中调用的将是 re-routed 到 loader-libraries 函数指针,并且您的代码实际上可以工作(前提是您在任何 GL 函数之前在 run-time 处正确初始化加载程序被称为)。
我目前正在研究 C 库 NanoVG 库。该库依赖于 OpenGL 函数并且有 2 header 个文件 nanovg.h
和 nanovg_gl.h
。后一个文件包含部分实现。为了方便起见,我把这两个 header 文件放在了 /usr/include/nanovg
.
当我尝试将以下代码编译为 object 文件时,gcc 没有报错:
// working.c
#include <GL/gl.h>
#include <nanovg/nanovg.h>
#define NANOVG_GL3_IMPLEMENTATION
#include <nanovg/nanovg_gl.h>
(命令:gcc -c working.c -o working.o
)
现在,我将 /usr/include/nanovg/
中的 header 个文件复制到工作目录,并将代码替换为:
// notworking.c
#include <GL/gl.h>
#include "nanovg.h"
#define NANOVG_GL3_IMPLEMENTATION
#include "nanovg_gl.h"
(命令:gcc -c notworking.c -o notworking.o
)
Gcc 现在抱怨一些 OpenGL 函数没有声明:
... (many more similar complaints)
src/nanovg_gl.h: In function ‘glnvg__renderDelete’:
src/nanovg_gl.h:1540:3: warning: implicit declaration of function ‘glDeleteBuffers’; did you mean ‘glSelectBuffer’? [-Wimplicit-function-declaration]
1540 | glDeleteBuffers(1, &gl->fragBuf);
| ^~~~~~~~~~~~~~~
...
为什么一个文件编译顺利而另一个文件编译不顺利?
更深一点:
使用 cpp
工具,我发现两个 pre-processed 文件之间的区别仅限于 # 指令,但就“C 内容”而言,我看不出有任何区别。下面是 pre-processed working.c
的片段。如果我从 pre-processed notworking.c
添加 # 行,则 gcc 不再编译 pre-processed working.c
并抱怨 glDeleteBuffers
.[= 缺少声明36=]
// ...
if (gl ==
// # 1533 "src/nanovg_gl.h" 3 4 // <- uncomment this line and glDeleteBuffers is considered missing by gcc
((void *)0)
// # 1533 "src/nanovg_gl.h" // <- idem
) return;
glnvg__deleteShader(&gl->shader);
if (gl->fragBuf != 0)
glDeleteBuffers(1, &gl->fragBuf); // <- the function that gcc complains about is here
// ...
编辑: 为了确保我没有做任何可能导致差异的偷偷摸摸的事情,我遵循了以下步骤,希望可以在另一台计算机上重现:
- GCC 版本:
gcc (Ubuntu 10.3.0-1ubuntu1) 10.3.0
- 复制
GL/gl.h
的版本 here 到工作目录并调用它glfoo.h
- 将
nanovg
的 header 复制到/usr/include/nanovg/
和nanovg/
(相对于工作目录)。 - 在工作目录中将以下内容保存为
test.c
:
#include "glfoo.h"
#include <nanovg/nanovg.h>
#define NANOVG_GL3_IMPLEMENTATION
#include <nanovg/nanovg_gl.h>
- 运行
gcc -c test.c -o test.o
=> 编译作品 - 将第 2 行和第 4 行的 <...> 替换为“..”并且 运行 命令 => 编译失败。
只是尝试了这些确切的步骤,我能够重现它。
简直
#include <file.h>
从编译器默认列出的路径中包含文件,而
#include "file.h"
包含当前文件夹(您正在编译的文件夹)中的文件。
与您的情况一样,从 <> 切换到 "" 会导致文件丢失,从而导致编译器错误。
经过一番调查后,我找到了解决方案。 gcc
不会对系统 headers 应用与“正常”文件相同的警告级别(这主要是因为系统 headers有时会做一些奇怪的事情,这些事情不受 C 标准的支持,但对于它们附带的平台来说是“安全的”。
gcc documentation 状态(强调我的):
-Wsystem-headers
:Print warning messages for constructs found in system header files. Warnings from system headers are normally suppressed, on the assumption that they usually do not indicate real problems and would only make the compiler output harder to read. Using this command-line option tells GCC to emit warnings from system headers as if they occurred in user code. However, note that using -Wall in conjunction with this option does not warn about unknown pragmas in system headers—for that,
-Wunknown-pragmas
must also be used.
当您通过 <...>
包含 nanovg 时,它被视为一个系统 header。
这样做gcc -Wsystem-headers working.c
实际上会引起警告。
请注意,您的代码在 working.c
和 notworking.c
中均无效,因为 working.c
只是隐藏了警告消息。访问任何超出 GL 1.1 定义的 GL 函数的正确方法是使用 GL 扩展机制,这意味着您必须在 run-time 处查询 GL 函数指针。完整的 GL 加载程序库,如 GLEW 和 glad 可以自动为您完成。许多这些加载器(包括 GLEW 和 GLAD)通过将每个 GL 函数名称重新 #define
转换为内部函数指针来工作,因此当您包含加载器附带的 header 时,每个 GL 函数在您的代码(和 nanovg 的)中调用的将是 re-routed 到 loader-libraries 函数指针,并且您的代码实际上可以工作(前提是您在任何 GL 函数之前在 run-time 处正确初始化加载程序被称为)。