[toc]

预处理

1
gcc -E a.c

做了什么?

example

1
2
3
4
5
6
7
8
9
#include <stdio.h>

#define mian main
#define N 1000

int mian() {
printf("%d\n", N);
return 0;
}

输出了一大串代码…

image-20230103141627581

image-20230103141708387

是不是有一点熟悉…翻到最后的话
image-20230103141739040

这下你明白了:

预处理就是字符串替换

1
#define A B

它的作用就是,把代码里以“词法单元”形式出现的 A 全都替换成 B。

1
2
3
A.val = 1;
#define A B
A.val = 1;

预处理试试

宏函数

1
#define foo() { return 1; }
1
2
foo();
foo(1); // ?

宏函数可以有参数

1
2
3
4
5
#define foo(a) { return a; }

foo(0);
foo(a + b);
foo(a, b); // ?

可变参数

1
2
3
4
5
#define foo(a, ...) { bar(__VA_ARGS__, a); }

foo(m);
foo(m, n);
foo(m, n, p);

普通的函数也可以有可变参数列表!(你没想过 printf 是怎么做到的吗?)

1
2
3
4
#include <stdarg.h>

int vprintf(const char *format, va_list ap);
int printf(const char *format, ...);

man va_args

注意点1:参数的括号与运算优先级

1
2
3
#define add(a, b) a + b

c = d * add(a, b) * e; ///???

注意点2:语法语义

1
2
3
4
5
6
7
8
9
#define foo()  bar(); baz();

if (cond) foo(); // how will it expand?

#define foo() { bar(); baz(); }
if (cond) foo();
else return 0; // Syntax error

#define foo() do { bar(); } while(0) // jeez!

注意点3:副作用

1
2
3
#define max(a, b) ((a) > (b) ? (a) : (b))

max(a++, b); // ?

条件编译

1
2
3
4
5
6
#define A
#ifndef A
printf("A undefined!\n");
#else
printf("A defined!\n");
#endif

条件编译也可以嵌套

1
2
3
4
5
6
7
8
9
10
11
#define A
#define B
#ifdef A
# ifdef B
printf("A B both defined!\n");
# else
printf("B undefined!\n");
# endif
#else
printf("A undefined!\n");
#endif

也可以做条件判断!

1
2
3
4
5
6
#define A 1
#if A <= 2
printf("A <= 2!\n");
#else
printf("A > 2!\n");
#endif

只能判断数字。如果没有指定值的话:

1
2
3
4
5
6
#define A
#if A == 0
printf("A == 0!\n");
#else
printf("A != 0!\n");
#endif

error

但如果随便指定一个不存在的东西…

1
2
3
4
5
6
#define A SOMESTRANGESTUFF
#if A == 0
printf("A == 0!\n"); // THIS IS CHOSEN
#else
printf("A != 0!\n");
#endif

这就是玄学了(

特殊运算符

在预处理时有两个特殊的运算符。一是一元运算符 #,表示将文本替换成字符串;二是二元运算符 ##,表示将两个文本相连

1
2
3
4
5
#define concat(a, b) a ## b
#define print(a) printf(#a)

concat(hello, world);
print(hello world);

image-20230103165611133

1
2
3
#define EVAL(expr) printf(#expr " = %d\n", expr)

EVAL(1 + 2);

但上面的定义有一点小问题

1
2
3
4
5
#define this that
#define concat(a, b) a ## b

int concat(this, Func)(int a, int b);
// => int thisFunc(int a, int b);
1
2
3
4
5
6
7
#define this that
#define concat_temp(a, b) a ## b
#define concat(a, b) concat_temp(a, b);

int concat(this, Func)(int a, int b);
// => int concat_temp(that, Func)(int a, int b);
// => int thatFunc(int a, int b);

一些常用的自带宏

1
2
3
4
5
6
__FILE__: 当前代码所处的文件名

__LINE__: 当前代码在文件中的行数
__TIME__: 当前文件在编译时的时间

__x86_64__: 是否为 X86_64 架构编译

image-20230103171422726

1
2
gcc -m32 a.c   # ?
gcc -m64 a.c
1
2
3
4
5
#define Log(fmt, ...) fprintf(stderr, "[in file %s, line %d, function %s] " fmt "\n", __FILE__, __LINE__, __func__, __VA_ARGS__)

int main() {
Log("%s", "print a log");
}