me
me copied to clipboard
学习 C/C++ (Part 14: define)
复习一下define语法:
编译一个C程序的第一个步骤被称为preprocessing(预处理)阶段。C preprocessor(预处理器)在源代码编译之前对其进行一系列的文本操作,包括删除注释、插入被#include指令包含的文件内容,定义和替换由#define指令定义的符号以及确定代码的部分内容是否应该根据一些条件编译指令进行编译。(# 开头的指令都是preprocessor专用的)
#define name stuff
通过define这条指令后,在预处理 (preprocessing)阶段时,preprocessor就会把name替换成stuff。define和指针一样,用不好就是一地鸡毛,用的好就是yyds。
我们从hello world开始,
main.c
#define NAME nonocast
int main() {
return 0;
}
preprocessing: gcc -E main.c -o main.i
或 clang -E main.c -o main.i
, gcc的help中-E flag表示Only run the preprocessor
省略stdio.h的内容
int main() {
printf("%f\n", 3.14);
return 0;
}
String
#define URL "http://github.com/nonocast/me/issues"
int main() {
printf("%s\n", URL);
return 0;
}
Expression
#define AGE (20 / 2)
int main() {
printf("%d\n", AGE);
return 0;
}
预处理会保留括号: printf("%d\n", (20 / 2));
, 所以这里的define也可以省略括号: #define AGE 20 / 2
Function
#define SQUARE(x) x * x
int main() {
printf("%d\n", SQUARE(3));
return 0;
}
替换后: printf("%d\n", 3 * 3);
挑战一下异常情况
#define DEBUG 1
int main() {
DEBUG
DEBUG
DEBUG
return 0;
}
预处理后是三行1,编译通不通过那是后面的事情,预处理是OK的。
#define DEBUG
int main() {
DEBUG
DEBUG
DEBUG
return 0;
}
这个就变成了:
int main() {
return 0;
}
ifdef
main.c
#include <stdio.h>
#define DEBUG
int main() {
#ifdef DEBUG
printf("DEBUG MODE\n");
#endif
return 0;
}
虽然刚才DEBUG没啥用,但通过ifdef就可以起到编译前控制,这个就很有用处了,比如在不同的平台或版本上采用不同的代码块。
通过undef可以remove掉这个定义。
#include <stdio.h>
#define DEBUG
int main() {
#ifdef DEBUG
printf("DEBUG MODE\n");
#endif
#undef DEBUG
#ifdef DEBUG
printf("UNREACHABLE\n");
#endif
return 0;
}
第一个就不会起到作用,vscode也会自动帮你灰掉第二部分的代码块。
这里要区分一个场景,刚才ifdef/ifndef是通过检查是否定义,而不看这个值,那么#if就是用来做条件编译,仔细区分。
#include <stdio.h>
#define FEATURE_1 1
#define FEATURE_2 0
#define FEATURE_3 1
int main() {
#if FEATURE_1
printf("FEATURE 1: ON\n");
#elif FEATURE_2
printf("FEATURE 2: ON\n"); // did not show
#elif FEATURE_3
printf("FEATURE 3: ON\n");
#endif
return 0;
}
gcc/clang D flag
gcc/clang通过时可以外部注入define,这个真是上帝视角,
#include <stdio.h>
int main() {
#ifdef DEBUG
printf("%s\n", URL);
#endif
return 0;
}
然后通过clang -DDEBUG -DNAME='"nonocast"' -E main.c
注入,预处理后的如下:
int main() {
printf("%s\n", "nonocast");
return 0;
}
Makefile
main.c
#include <stdio.h>
int main() {
#ifdef DEBUG
printf("%s\n", NAME);
#endif
printf("Version: %s\n", VERSION);
return 0;
}
Makefile
VERSION=v1.3
CC=clang
CFLAGS=-arch arm64 `pkg-config --cflags openssl`
LDFLAGS=`pkg-config --libs openssl`
DEF=-DVERSION=\"$(VERSION)\" -DDEBUG -DNAME=\"nonocast\"
build: src/main.c
$(CC) $(CFLAGS) $(LDFLAGS) $(DEF) -o build/app src/main.c
run: build
./build/app
CMake
cmake_minimum_required(VERSION 3.10)
project(hello)
add_compile_definitions(DEBUG)
add_compile_definitions(NAME=\"nonocast\")
add_executable(app src/main.c)
FILE
预处理器会偷偷夹带一些内置的define
#include <stdio.h>
int main() {
printf("file: %s\n", __FILE__); // main.c
printf("function: %s\n", __FUNCTION__); // main
printf("line: %d\n", __LINE__); // 5
printf("date: %s\n", __DATE__); // May 15 2022
printf("time: %s\n", __TIME__); // 01:37:22/
return 0;
}