🎭🎭前言
本篇文章学自比特鹏哥,若构成侵权,请及时联系作者,本人一定及时处理
如果您阅读了本文,并觉得本文不错,您的赞👍将是我最大的奖励🙏🙏
本文部分案例使用的时c


| 阶段 | 详解 |
|---|---|
| 预处理( xxx.c–>xxx.i ) | 1. 完成了头文件的包含#include 2. #define定义的符号和宏的定义 3. 删除注释 文本操作 |
| 编译(xxx.i–>xxx.s) | 把c语言代码转化成汇编代码 1.语法分析 2. 词法分析 3. 语义分析 4. 符号总结 |
| 汇编(xxx.s–>xxx.o) | ***把汇编代码转化为机器指令(二进制指令)1. 生成符号表 *** |
| 连接(xxx.o–>xxx.out) | 多个目标文件和链接库进行连接 |
| 符号分类 | 详解 |
|---|---|
| FILE | 显示正在进行编译的源文件 |
| LINE | 当前文件的行号 |
| DATE | 文件被编译的日期 |
| TIME | 文件按被编译的时间 |
| STDC | 如果编译器遵循ANSI C ,其值为1,否则为定义 |
ANSI C是由美国国家标准协会(ANSI)及国际标准化组织(ISO)推出的关于C语言的标准。ANSI C 主要标准化了现存的实现, 同时增加了一些来自 C++ 的内容 (主要是函数原型) 并支持多国字符集 (包括备受争议的三字符序列)。 ANSI C 标准同时规定了 C 运行期库例程的标准。
printf("file:%s line:%d\n",__FILE__,__LINE__);
#define NAME stuff
//建议不加分号
#define MIN 10 //定义字面量的值
#define reg register//为关键字,创建一个别名
#define do_forever for(;;)//用形象的符号来代替一种实现
#define CASE break;case //在写case时自动把break写上
//如果定义的stuff 太长,可以分行书写,但除了最后一行外,其他每行的后面都加上一个反斜杠(续行符)
#define DEBUG_PRINT printf("file:%s \t line:%d\t \
date:%s \t time:%s\n",\
__FILE__,__LINE__, \
__DATE__,__TIME__)
#include
#define CASE break;case //在写case时自动把break写上
int main() {
int option = 1;
switch (option) {
case 1:
printf("hhh");
CASE 2:
CASE 3 :
break;
}
return 0;
}
#define机制包括一个规定,允许把参数替换到文本中
#define name(name-list) stuff
#include
using namespace std;
#define SQUARE(X) X*X
int main() {
cout << SQUARE(3 + 1)<<endl;//3+1*3+1 =7
cout << 10*SQUARE(3 + 1)<<endl;//10*3+1*3+1 =34
}
提示:用于对数值表达式进行求值的宏定义都应该加上方括号,避免在使用宏时中的操作符或临近操作符之间不可预料的相互作用
#include
using namespace std;
#define M 100
#define MAX(X,Y) ((X)>(Y)?(X):(Y))
int main() {
int max = MAX(360, M);
cout << max << endl;
}
#include
using namespace std;
#define PRINT(X) printf("the value of "#X" is %d\n",X);
int main() {
int a = 10;
PRINT(a);
return 0;
}
//把参数插入到字符串中
#include
using namespace std;
#define PRINT(X,FORMAT) printf("the value of "#X" is " FORMAT,X);
int main() {
int a = 10;
PRINT(a,"%d");
return 0;
}
//拼接字符
#include
using namespace std;
#define CAT(X,Y,Z) X##Y##Z
int main() {
int class12 = 100;
cout << CAT(class,1,2);
return 0;
}
int a=10;
int b=a+1;//b=11,a=10 无副作用
int b=++a;//b=11,a=11 有副作用
#include
using namespace std;
#define MAX(X,Y) ((X)>(Y)?(X):(Y))
int main() {
int a = 1,b=2;
int m = MAX(a++,b++);
/*
过程详解
int m=(a++)>(b++)?(a++):(b++)
后置递增:先比较后递增
1<2 返回b++ 即3
然后a、b完成递增 即a=1+1=2,b=3+1=4
*/
cout << m<<endl;//3
cout << a << endl;//2
cout << b << endl;//4
return 0;
}
#define MAX(X,Y) ((X)>(Y)?(X):(Y))
宏的优势【使用宏进行简单计算而不是用函数的原因】
宏的劣势
宏有时候可以做到函数做不到的事情:宏的参数可以出现类型
#include
using namespace std;
#define MALLOC(num,type) (type*)malloc(num*sizeof(type))
int main() {
int* p = MALLOC(10, int);
if (p == NULL) {
return -1;
}
for (int i = 0; i < 10; i++) {
*(p + i) = i;
}
for (int i = 0; i < 10; i++) {
cout << *(p + i)<<" ";
}
free(p);
p = NULL;
return 0;
}
| 属性 | #define定义宏 | 函数 |
|---|---|---|
| 代码长度 | 每次使用宏,宏代码会插入到程序中。较长的宏,会是程序的长度大幅度增长 | 函数代码只出现在一个地方;每此时用这个函数时,都调用那个地方的同一份代码 |
| 执行速度 | 更快 | 存在函数的调用和返回的额外开销,较慢 |
| 操作符优先级 | 宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号 | 函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。 |
| 带有副作用的参数 | 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。 | 函数参数只在传参的时候求值一次,结果更容易控制。 |
| 递归 | 宏是不能递归的 | 函数可以递归 |
命名约定
宏的名称全部大写
函数名不要全部大写,推荐驼峰命名法
//如果现存的一个名字需要重新被定义,那么它的旧名首先需要被移除
#define NAME
#include
#define __DEBUG__

//1 单个的条件编译
#if 常量表达式
//...
#endif
//2多个分支的条件编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
//3 判断是否被定义
#if define(symbol)
#ifdef symbol
//4 嵌套指令
#if defined(OS_UNIX)
#ifdef option1
unix_version_option1();
#endif
#ifdef option2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef option2
msdos_version_option2();
#endif
#endif
#include
using namespace std;
int main() {
#if 1==2
cout << "1==2";
#elif 2==3
cout << "2==3";
#else
cout << "else";
#endif
return 0;
}
#include
using namespace std;
#define TEST 0
int main() {
#ifdef TEST
cout << "test\n";
#endif
#if defined(TEST)
cout << "test\n";
#endif
#ifndef WHERE
cout << "where\n";
#endif // !WHERE
#if !defined(WHERE)
cout << "where\n";
#endif // !WHERE
return 0;
}
| 包含类型 | 书写类型 | 查找策略 |
|---|---|---|
| 本地文件包含 | #include "filename" | 现在源文件所在的目录下查找,如果未找到,编译器就像查找库函数头文件一样在准确位置查找头文件;找不到,提示错误 |
| 库文件包含 | #include | 直接去库目录下查找 |
#ifdef __自定义名称【推荐头文件名称大写——H】__
#define __自定义名称【推荐头文件名称大写——H】__
/*
头文件内容
*/
#endif
#pragma once
/*
头文件内容
*/

