使用#include "..."编译失败,但使用#include <...>可以通过编译。

3

我目前正在玩弄C语言库NanoVG。该库依赖于OpenGL函数,并有2个头文件nanovg.hnanovg_gl.h,后者包含部分实现。为了方便,我已将这两个头文件放在/usr/include/nanovg中。

但是,当我尝试将以下代码编译成对象文件时,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/复制到工作目录,并用以下代码替换:

// 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工具,我发现这两个预处理文件之间的差异仅限于 # 指令,但就“C内容”而言,我没有看到任何区别。下面是working.c的预处理片段。如果我从notworking.c的预处理文件中添加#行,则gcc将不再编译预处理的working.c并抱怨缺少对glDeleteBuffers的声明。

// ... 
 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
// ...

编辑: 为确保我没有做任何可能导致差异的卑鄙行径,我遵循了以下步骤,这些步骤希望在另一台计算机上可复现:

  1. GCC版本:gcc (Ubuntu 10.3.0-1ubuntu1) 10.3.0
  2. 此处找到的 GL/gl.h 版本复制到工作目录并将其命名为 glfoo.h
  3. nanovg 的头文件(如存储在仓库中)复制到 /usr/include/nanovg/nanovg/ 中(相对于工作目录)。
  4. 在工作目录中将以下内容保存为 test.c
#include "glfoo.h"
#include <nanovg/nanovg.h>
#define NANOVG_GL3_IMPLEMENTATION
#include <nanovg/nanovg_gl.h>
  1. 运行gcc -c test.c -o test.o => 编译成功
  2. 将第2和第4行中的<...>替换为“..”,然后运行命令 => 编译失败。

我刚尝试了这些步骤,能够重现该问题。


3
也许将整个文件夹nanovg复制到您的工作目录中,并将其用作"nanovg/nanovg.h"?你确定这些头文件是独立的,不依赖于其他任何内容吗? - Zoso
打开gl.h文件,检查是否实际声明了glDeleteBuffers。我对OpenGL一无所知,但似乎有些版本有这个函数,而有些则没有。例如,这个没有声明它(但它确实声明了建议使用的glSelectBuffer)。然而,我不知道为什么从<...>改为"..."会导致包含不同版本的GL/gl.h文件。我的猜测(奥卡姆剃刀原理也表明)是你改变的不止这一个东西。 - Clifford
1
如果在包含“nanovg_gl.h”之前包含“<GL/glext.h>”,它是否能正常工作? - Clifford
@Zoso:不,复制整个包含文件夹似乎没有任何区别。nanovg.h 不包含任何 #include 语句,因此我认为它不依赖于其他任何内容。@Clifford:确实,我的 GL/gl.h 没有声明 glDeleteBuffers,但我真的很好奇为什么在一种情况下会有影响而在另一种情况下则没有。包括 GL/glext.h 并没有帮助,但是使用 GL/glew.h (在 GL/gl.h 之前)错误确实消失了。 - Ahmad B
1
这里的问题不是为什么 notworking.c 不工作,而是为什么 working.c 实际上可以工作,因为 glDeleteBuffers 不应该在 gl.h 中,也不在 glext.h 中(除非你 #define GL_GLEXT_PROTOTYPES,但你绝对不应该这样做)。你必须始终使用像 GLEW 或 glad 这样的 GL 加载器来访问 GL 1.1 以上的每个函数。而 nanovg 源代码实际上希望你使用一个加载器,该加载器直接提供 GL 符号,或者使用一些预处理宏来重新定义它们(就像大多数加载器所做的那样)。 - derhass
编辑以添加重现步骤。希望这能解决 Clifford 的猜测。 - Ahmad B
2个回答

4
调查后,我找到了解决方案。与“普通”文件相比,gcc在系统头文件上不会应用相同的警告级别(这主要是因为系统头文件有时会做一些不受C标准支持但对它们所属平台来说“安全”的奇怪事情)。 gcc文档中明确说明(加粗为我添加):

-Wsystem-headers:

打印在系统头文件中发现的构造的警告消息。系统头文件的警告通常被抑制,因为它们通常不表示真正的问题,而且只会使编译器的输出更难以阅读。 使用此命令行选项告诉GCC发出来自系统头文件的警告,就好像它们在用户代码中发生一样。但是,请注意,与此选项结合使用的-Wall不会警告系统头文件中的未知pragma-对于此,必须还要使用 -Wunknown-pragmas

当您通过<...>包含nanovg时,它被视为系统头文件。
因此,实际上执行gcc -Wsystem-headers working.c会产生警告。
请注意,您的代码在working.cnotworking.c中都无法正常工作,因为working.c只是隐藏了警告消息。访问GL 1.1定义之外的任何GL函数的正确方法是使用GL扩展机制,这意味着您必须在运行时查询GL函数指针。完整的GL加载程序库(如GLEW和glad)可以自动执行此操作。其中许多加载程序(包括GLEW和GLAD)通过重新#define每个GL函数名到内部函数指针来工作,因此当您包含与加载程序一起提供的标头时,调用您的代码(以及nanovg的代码)中的每个GL函数将被重新路由到加载程序库的函数指针,并且您的代码实际上可以正常工作(前提是在调用任何GL函数之前正确初始化加载器)。

哦,我本以为编译器会更加慎重地选择在系统头文件中抑制哪些警告。抑制有关“保留”标识符的声明的警告是有道理的,因为系统头文件有时应该声明保留标识符。但是,在系统头文件中使用未声明的函数即使是错误也很可能出现。 - Eric Postpischil
好吧,事情就是这样。我宁愿不在这里讨论那种行为的有用性。这应该与GCC开发人员讨论。 - derhass

-3

简单地

#include <file.h>

从默认列出的路径中包含文件到编译器中

#include "file.h"

从当前文件夹(编译时所在的文件夹)包含文件。

就像您的情况一样,从“<>”切换到“”会导致某些文件丢失,从而导致编译器错误。


3
不,头文件不是丢失了,编译器错误信息也没有报告文件丢失。该问题将在系统位置中使用 <> 编译,并将头文件与在源文件目录中使用 "" 进行编译进行比较。在这两种情况下,编译器都能找到头文件,所以这不是问题的原因。问题在于,在后一种情况下,编译器报告 glDeleteBuffers 没有声明。这意味着编译器根据头文件被包含的位置不同而以不同的方式处理它们。 - Eric Postpischil
2
No. "..." 在搜索 -i 命令行开关和默认系统路径(按顺序)指定的路径之前搜索当前文件夹。即使这是真的,也不能回答问题;gl.h 包含在 <...> 中,这就是 glDeleteBuffers 应该被找到的地方。 - Clifford

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接