Open nonocast opened 2 years ago
复习一下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
gcc -E main.c -o main.i
clang -E main.c -o main.i
省略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
printf("%d\n", (20 / 2));
#define AGE 20 / 2
Function
#define SQUARE(x) x * x int main() { printf("%d\n", SQUARE(3)); return 0; }
替换后: printf("%d\n", 3 * 3);
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; }
#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通过时可以外部注入define,这个真是上帝视角,
#include <stdio.h> int main() { #ifdef DEBUG printf("%s\n", URL); #endif return 0; }
然后通过clang -DDEBUG -DNAME='"nonocast"' -E main.c注入,预处理后的如下:
clang -DDEBUG -DNAME='"nonocast"' -E main.c
int main() { printf("%s\n", "nonocast"); return 0; }
#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_minimum_required(VERSION 3.10) project(hello) add_compile_definitions(DEBUG) add_compile_definitions(NAME=\"nonocast\") add_executable(app src/main.c)
预处理器会偷偷夹带一些内置的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; }
复习一下define语法:
编译一个C程序的第一个步骤被称为preprocessing(预处理)阶段。C preprocessor(预处理器)在源代码编译之前对其进行一系列的文本操作,包括删除注释、插入被#include指令包含的文件内容,定义和替换由#define指令定义的符号以及确定代码的部分内容是否应该根据一些条件编译指令进行编译。(# 开头的指令都是preprocessor专用的)
通过define这条指令后,在预处理 (preprocessing)阶段时,preprocessor就会把name替换成stuff。define和指针一样,用不好就是一地鸡毛,用的好就是yyds。
我们从hello world开始,
main.c
preprocessing:
gcc -E main.c -o main.i
或clang -E main.c -o main.i
, gcc的help中-E flag表示Only run the preprocessorString
Expression
预处理会保留括号:
printf("%d\n", (20 / 2));
, 所以这里的define也可以省略括号:#define AGE 20 / 2
Function
替换后:
printf("%d\n", 3 * 3);
挑战一下异常情况
预处理后是三行1,编译通不通过那是后面的事情,预处理是OK的。
这个就变成了:
ifdef
main.c
虽然刚才DEBUG没啥用,但通过ifdef就可以起到编译前控制,这个就很有用处了,比如在不同的平台或版本上采用不同的代码块。
通过undef可以remove掉这个定义。
第一个就不会起到作用,vscode也会自动帮你灰掉第二部分的代码块。
这里要区分一个场景,刚才ifdef/ifndef是通过检查是否定义,而不看这个值,那么#if就是用来做条件编译,仔细区分。
gcc/clang D flag
gcc/clang通过时可以外部注入define,这个真是上帝视角,
然后通过
clang -DDEBUG -DNAME='"nonocast"' -E main.c
注入,预处理后的如下:Makefile
main.c
Makefile
CMake
FILE
预处理器会偷偷夹带一些内置的define
参考阅读