CFLAGS和CPPFLAGS的区别

125

我了解到CFLAGS(或C++中的CXXFLAGS)是给编译器用的,而CPPFLAGS则由预处理器使用。

但我仍然不理解它们之间的区别。

我需要为一个通过#include包含的头文件指定一个包含路径--因为#include是一个预处理指令,所以只需要在预处理器(CPPFLAGS)中指定路径吗?

在什么情况下需要为编译器提供额外的包含路径?

一般来说,如果预处理器找到并包含所需的头文件,为什么还需要告诉它要使用额外的包含目录? CFLAGS有什么用处吗?

(在我的情况下,我发现两者都可以让我编译程序,这让我更加困惑...至少在autoconf上下文中,我可以使用CFLAGS 或者 CPPFLAGS来实现我的目标。这是怎么回事?)


2
可能是 https://dev59.com/8nRB5IYBdhLWcg3w1Kn0 的重复内容。 - Michael Mrozek
6个回答

176

编译C程序的隐式make规则是:

%.o:%.c
    $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

$()语法是展开变量的方式。由于编译器调用中使用了CFLAGSCPPFLAGS,因此选择定义include路径的方式是个人品味的问题。例如,如果foo.c是当前目录中的文件,则:

```shell gcc -o foo $(CFLAGS) $(CPPFLAGS) foo.c ```

```shell gcc -o foo $(CPPFLAGS) $(CFLAGS) foo.c ```

这两种方式都是可以的。

make foo.o CPPFLAGS="-I/usr/include"
make foo.o CFLAGS="-I/usr/include"

两者会以完全相同的方式调用编译器,即

gcc -I/usr/include -c -o foo.o foo.c

当你有多种语言需要相同的include路径时,这两者之间的差异就会发挥作用。例如,如果你有一个名为bar.cpp的文件,则尝试:

make bar.o CPPFLAGS="-I/usr/include"
make bar.o CFLAGS="-I/usr/include"
然后编译就会进行。
g++ -I/usr/include -c -o bar.o bar.cpp
g++ -c -o bar.o bar.cpp

由于C++的隐式规则也使用CPPFLAGS变量,因此这个区别可以为您提供一个很好的选择指南——如果要将标志用于所有语言,请将其放在CPPFLAGS中;如果要将其用于特定语言,请将其放在CFLAGSCXXFLAGS等中。后者类型的示例包括符合标准或警告标志——您不会想将-std=c99传递给C++编译器!

然后,在您的makefile中可能会出现以下内容:

CPPFLAGS=-I/usr/include
CFLAGS=-std=c99
CXXFLAGS=-Weffc++

1
请注意,您不能仅使用CPPFLAGS运行独立的cpp并期望得到任何合理的结果,因为-std=c99会影响定义的符号(特别是在没有功能测试宏的情况下)。相反,您需要使用$(CC) $(CPPFLAGS) $(CFLAGS) -E - Jed
1
编译C程序的隐式make规则是"$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<"。不确定是否有区别,但在gmake上,目前CPPFLAGS出现在CFLAGS之后:$ make --version | head -n 1 -> GNU Make 4.3; $ make -p 2>/dev/null | grep -F 'COMPILE.c =' -> COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c - kelvin

12

CPPFLAGS宏用于指定#include目录。

在你的情况下,CPPFLAGSCFLAGS都可以使用,因为make(1)规则将预处理和编译合并到一个命令中(因此这两个宏都用于该命令)。

如果使用#include "..."形式,则不需要指定.作为包含目录。您也不需要指定标准编译器包含目录。但是需要指定所有其他包含目录。


这样更有意义,但我仍然不明白CFLAGS的作用。如果像你所暗示的那样,在更复杂的项目中编译是在预处理的单独步骤中完成的,那么如果CFLAGS没有为预处理器添加与CPPFLAGS相同的路径,预处理是否会成功但编译失败?如果预处理器已经处理了#include指令,那么编译器如何处理包含路径,我想我还是不太理解。 - EBM
4
如果您正在编译预处理文件,则不需要包含路径-所需的头文件已经添加到预处理的源代码中(运行gcc -E命令时查看文件输出-没有#include)。大多数现代编译器将预处理和编译步骤结合在一起,因此您不必担心这个问题。 - Scott Wales

4

0
我使用CPPFLAGS变量安装了httpd在Ubuntu 18.04上,用于-DLINUX标志。当运行时,CPPFLAGS从上到下逐个文件扫描代码,查找编译前的指令,并且不会被其他有意义的东西所扩展,例如大小优化、不增加输出文件大小的标志、针对处理器类型来减小代码大小和加速程序、除了情况之外禁用所有变量。 CPPFLAGS和CFLAGS之间唯一的区别是CFLAGS可以设置为指定要传递给编译器的附加开关。也就是说,CFLAGS环境变量在安装路径中创建一个目录(例如CFLAGS=-i/opt/include),以将调试信息添加到可执行目标的路径中:包括一般警报消息;关闭警报信息;独立位置生成;显示编译器驱动程序、预处理器、编译器版本号。
设置CPPFLAGS的标准方法:
sudo ./configure --enable-unixd=DLINUX #for example

一些已知变量列表:

CPPFLAGS - is the variable name for flags to the C preprocessor.
CXXFLAGS - is the standard variable name for flags to the C++ compiler.
CFLAGS is - the standard name for a variable with compilation flags.
LDFLAGS - should be used for search flags/paths (-L) - i.e. -L/usr/lib (/usr/lib are library binaries).
LDLIBS - for linking libraries. 

0

除了那些提到的隐式规则,最好查看make已经隐式定义并适用于您的环境的内容:

make -p

例如:
%.o: %.c
    $(COMPILE.c) $(OUTPUT_OPTION) $<

它扩展了

COMPILE.c = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

这将同时打印出# 环境数据。在这里,您将找到GCC的包含路径等其他有用信息。
C_INCLUDE_PATH=/usr/include

在 make 中,当涉及到搜索时,路径有很多,但光明只有一个……或类似的东西。

  1. C_INCLUDE_PATH 是系统级别的,请在您的 shell 的 *.rc 中设置它。
  2. $(CPPFLAGS) 用于预处理器包含路径。
  3. 如果您需要为 make 添加通用搜索路径,请使用:
VPATH = my_dir_to_search

...或者更加具体一些

vpath %.c src
vpath %.h include

VPATH 是一个通用的搜索路径,因此请谨慎使用。如果一个文件存在于 VPATH 中列出的多个位置中,则 make 将取列表中的第一个出现。


-1

CPPFLAGS 似乎是 GNU Make 的一个发明,在一些内置的配方中被引用。

如果您的程序由某些自由软件发行版构建,您可能会发现其中一些需要包来插入此变量,使用 CPPFLAGS 传递选项,例如 -D_WHATEVER=1 传递宏定义。

这种分离在 GNU 环境中是一个糟糕的想法,完全没有必要,因为:

  • 有一种方法可以运行 gcc 只进行预处理(同时忽略与预处理无关的编译器选项)。

  • 独立的 GNU cpp 对编译器选项具有容忍性,例如与预处理无关的警告选项 -W,甚至代码生成选项如 -fstrict-aliasing 和链接器传递选项如 -Wl,--whatever

因此,一般来说,需要调用独立预处理器的构建系统只需将其传递给 $(CFLAGS)

作为一个编写Makefile的应用程序开发人员,您不能依赖于CPPFLAGS的存在。不熟悉开源构建的内部专家的用户将不知道CPPFLAGS,并且在构建您的程序时会执行像make CFLAGS=-Dfoo=bar这样的操作。如果这样做不起作用,他们会感到恼怒。
作为一个发行版维护者,您不能依赖于程序来拉取CPPFLAGS;即使是表现良好的程序也会拉取CFLAGSLDFLAGSLDLIBS
对于应用程序开发人员来说,编写GNU Make代码以将预处理器标志从$(CFLAGS)中分离出来非常容易:
cpp_only_flags := $(foreach arg,                        \
                     $(CFLAGS),                         \
                     $(or $(filter -D%,$(arg)),         \
                          $(filter -U%,$(arg)),         \
                          $(filter -I%,$(arg)),         \
                          $(filter -iquote%,$(arg)),    \
                          $(filter -W%,$(arg)),         \
                          $(filter -M%,$(arg))))        \
                          $(CPPFLAGS) # also pull this in

all:
    @echo cpp_only_flags == $(cpp_only_flags)

演示:

$ make CFLAGS="-Wall -I/path/to/include -W -UMAC -DFOO=bar -o foo.o -lm"
cpp_only_flags == -Wall -I/path/to/include -W -UMAC -DFOO=bar

在GNU编译器和预处理器的情况下,这可能是不必要的;但它说明了一种技术,可以用于非GNU编译器和预处理器,在基于GNU Make的构建系统中使用。

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