我们可以使用g++ 的-E 标志来调查预处理代码的样子:
在预处理阶段后停止;不要正确运行编译器。输出是经过预处理的源代码形式,发送到标准输出。
(source)
这是Main.cpp的预处理代码:
$ g++ -E Main.cpp
# 1 "Main.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "Main.cpp"
# 1 "SomeFile.hpp" 1
void func(double);
# 3 "Main.cpp" 2
int main()
{
double x = 3;
func(x);
return 0;
}
如果我们删除#include <iostream>,这里是SomeFile.cpp 的预处理代码:
$ g++ -E SomeFile.cpp
# 1 "SomeFile.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "SomeFile.cpp"
# 1 "SomeFile.hpp" 1
void func(float);
# 2 "SomeFile.cpp" 2
void func(float _value)
{
std::cout << _value << std::endl;
}
我们可以看到,在Main.cpp,TN_VALUE 是double,因此我们得到void func(double); 和double x = 3;。在SomeFile.cpp 中,TN_VALUE 是float。这是为什么呢?
这一切都与
有关
#ifndef TN_VALUE
#define TN_VALUE float
#endif
在SomeFile.hpp。这告诉预处理器,如果未定义TN_VALUE,则将TN_VALUE 设置为float。如果定义了TN_VALUE,则什么也不做(留下TN_VALUE 的值。
在Main.cpp,你有
#define TN_VALUE double
#include "SomeFile.hpp"
这里的顺序意味着TN_VALUE将在预处理器到达SomeFile.hpp中的ifndef之前定义,因此TN_VALUE将保留double的值。
然后在SomeFile.cpp 中,您立即拥有#include "SomeFile.hpp"。由于SomeFile.cpp 是独立于Main.cpp 的翻译单元,因此未定义TN_VALUE。这意味着,由于在预处理器到达SomeFile.hpp 中的ifndef 时未设置TN_VALUE,因此预处理器将到达#define TN_VALUE float 行,这将导致它将TN_VALUE 设置为float。
更多g++ -E分析
如果这是一个更大、更复杂的代码库,我们可能希望通过查看 SomeFile.hpp 和使用不同定义生成的预处理输出来更仔细地隔离问题。
我们可以查看SomeFile.hpp 的预处理输出,没有定义:
$ g++ -E SomeFile.hpp
# 1 "SomeFile.hpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "SomeFile.hpp"
void func(float);
我们也可以使用g++ 的-D 标志来进行定义。如果我们使用-D TN_VALUE=double,我们应该会看到与Main.cpp 匹配的内容:
$ g++ -D TN_VALUE=double -E SomeFile.hpp
# 1 "SomeFile.hpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "SomeFile.hpp"
void func(double);
我们已经从上面的分析中预料到了这些结果。但是,这表明我们可以根据不同定义的各种值来测试任何 C 或 C++ 头文件生成的预处理代码。