服务端视角的C++从入门到精通(二十三)

91 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第32天,点击查看活动详情

宏、枚举和常量(下)

C++里面的宏,还可以实现类似函数的功能,但是要注意的是,由于宏做的是纯文本替换,所以对于下面这个例子,可能无法达到开发者本来想要达成的效果:

#define X 5
#define Y X+5
#define Z Y/5+1

这里Z的值,预期应该是(5+5)/5+1=3(5+5)/5+1=3,但实际上却是5+5/5+1=75+5/5+1=7,这种错误就在于没有认识到#define生效的阶段。这里可能要多引入一点C++编译过程的解释。

一段C++代码编译的过程可以被细分为这样几步:

  1. 预处理,对应到g++里面就是-E选项,这一步的产出就是.i文件,这个文件其实还是文本文件,只不过会将预编译指令进行展开,比如本文中讲到的#define以及包含头文件用的#include等。
  2. 汇编,对应到g++里面就是-S选项,这一步的产出就是.s文件,即C++高层语言对应的低级语言即汇编语言代码,文本文件类型,其实基本就是1:1的翻译,当然也可能编译器会根据优化级别对指令进行相应的优化,这部分内容不在本文讨论范围之内。
  3. 单个文件编译,对应到g++里面就是-c选项,这一步的产出就是.o文件,是人类不可读的二进制文件了,每一个.c、.cpp文件都对应一个.o文件。
  4. 将静态库和.o文件一起进行静态编译,这里使用的是ld软件。

所以其实#define发生在词法、句法分析之前,这也就是为什么宏无法理解运算符优先级的原因了。

宏其实还有一种用法,就是用来拼接变量的名称,具体用法见如下代码:

#include <cstdio>
#define FUNC1(a) #a
#define FUNC2(a,b) a##b
int main() {
    int hello = 1, world = 2, helloworld = 3;
    printf(FUNC1(hello)FUNC1(world)"=%d",FUNC2(hello,world));
    return 0;
}

输出就是:

helloworld=3