#宏_预处理
#开头都是预处理指令,条件表达式必须在预处理里面,所以表达式必须是宏表达式
1 #define宏定义
替换
2 #if...#elif...#else...#endif条件编译
中英文
3 #ifdef...#endif
4 #ifndef...#endif
5 #include文件包含
包含
6
__DATE__
进行预处理的日期(“Mmm dd yyyy”形式的字符串文字)
__FILE__
代表当前源代码文件名的字符串文字
__FUNCTION__
__func__
代表所在函数名
__LINE__
代表当前源代码中的行号的整数常量
__TIME__
源文件编译时间,格式“hh: mm: ss”
1 宏定义
软件工程规定,宏定义用英文大写
define不会进行类型检查,只会替换,所以某些场合会出错。
尽量不用define,用const,const初始化的时候,会自动进行类型转换,会有类型检查
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 #define M 10.0 7 8 main() 9 { 10 const int N = 10.9; 11 12 printf("%d ", M);//define不会进行类型检查,只会替换,所以某些场合会出错。 13 14 printf("%d ", N);//尽量不用define,用const,const初始化的时候,会自动进行类型转换,会有类型检查 15 16 system("pause"); 17 }
宏替换只能替换单独的标识符,连着一起的无法替换,或者字符串内部的也无法替换
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 #define X 10 7 8 main() 9 { 10 printf("%d ", X); 11 12 int XX = 100; 13 14 printf("%d ", XX);//宏替换只能替换单独的标识符,连着一起的无法替换,或者字符串内部的也无法替换 15 16 system("pause"); 17 }
输出结果:
10
100
请按任意键继续. . .
宏的作用域,就是它所在的.c源文件
限定宏定义的作用域 #define M 10
终结宏定义,需要#undef
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 #define M 10 7 8 void out() 9 { 10 printf("%d ", M); 11 } 12 13 #undef M//限定宏定义的作用域 #define M 10 14 //终结宏定义,需要#undef 15 16 main() 17 { 18 out(); 19 20 //printf("%d ", M); 21 22 system("pause"); 23 }
带参数的宏定义
求出两个整数的最小值
面试题,如果取两个数之间最小的数
面试标准答案,看你的逻辑是否严密
min(x,y)宏参数表的参数,不可以加上括号(),但是后面的宏调用可以加上括号()
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 //面试题,如果取两个数之间最小的数 7 //面试标准答案,看你的逻辑是否严密 8 //min(x,y)宏参数表的参数,不可以加上括号(),但是后面的宏调用可以加上括号() 9 10 #define min(x,y) ((x)>(y))?(y):(x) 11 12 main() 13 { 14 printf("%d ", min(1 + 2, 2 + 9)); 15 16 system("pause"); 17 }
传递的参数,自动加上双引号"",让其变成一个字符串
#自动加上双引号""
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 #define go(x) system(#x) 7 //传递的参数,自动加上双引号"",让其变成一个字符串 8 //#自动加上双引号"" 9 10 main() 11 { 12 system("notepad"); 13 14 go(calc); 15 16 system("pause"); 17 }
#define就是替换,没有数据类型,无法安全检查
conse是有数据类型的,可以根据数据类型进行安全检查
发行数据类型不匹配的时候,会发出警告或者转换
能转换的时候就自动进行数据类型转换,不能转换就发出警告
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 #define X 10.0 7 8 const int num = 10.0; 9 10 //#define就是替换,没有数据类型,无法安全检查 11 12 //conse是有数据类型的,可以根据数据类型进行安全检查 13 //发行数据类型不匹配的时候,会发出警告或者转换 14 //能转换的时候就自动进行数据类型转换,不能转换就发出警告 15 16 main() 17 { 18 printf("%d ", sizeof(X)); 19 20 printf("%d ", sizeof(num)); 21 22 system("pause"); 23 }
2 条件编译
#if...#elif...#else...#endif
if...else if...else
功能是一样的,但是时间开销不一样
编译就是翻译成机器码0,1
#if...#elif...#else...#endif只编译符合条件的语句,所以有效减少被编译的语句,缩短源码的长度,达到缩短程序执行时间的目的。#if...#elif...#else...#endif需要判断表达式,所以我们定义宏必须给出值,否则无从判断。
if...else if...else会编译所有的代码,源码会较长,编译时间也会较长,程序体积会大,占内存也会大,运行时间会较长。
什么时候需要条件编译?
例如发布一个产品,如果有多个语言版本,可以有效减少体积。
一份源代码可以同时编译英文或者中文,有效减少体积。
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <windows.h> 6 7 //多分支条件编译,一般用于软件的国际化 8 //有效减少体积,一份源码可以编译很多个版本 9 //if...else if...else也可以实现一样的效果,但是体积太大 10 11 #define language 'e' 12 //c中文,e英文,k韩文,j日本,r俄罗斯文, 13 14 main() 15 { 16 #if language == 'c' 17 MessageBoxA(0, "你好中国", "你好中国", 0); 18 #elif language == 'e' 19 MessageBoxA(0, "hello world", "hello world", 0); 20 #elif language == 'k' 21 MessageBoxA(0, L"안녕하세요 중국", L"안녕하세요 중국", 0);//L宽字符,韩文需要用宽字符,宽字符一般用于软件国际化 22 #elif language == 'j' 23 MessageBoxA(0, "こんにちは中国", "こんにちは中国", 0); 24 #else 25 MessageBoxA(0, "привет Китай", "привет Китай", 0); 26 #endif 27 28 system("pause"); 29 }
3 #ifdef...#endif
ifdef M检测一下一个宏是否定义,定义就执行某个操作
否则就不执行
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 //开启或者关闭某个功能 7 #define M 8 9 main() 10 { 11 //ifdef M检测一下一个宏是否定义,定义就执行某个操作 12 //否则就不执行 13 14 #ifdef M 15 system("title hello"); 16 #endif 17 18 system("pause"); 19 }
4 #ifndef...#endif
这是"if not defined"的简写,是宏定义的一种,它是可以根据是否已经定义了一个变量来进行分支选择,一般用于调试等等。实际上确切的说这应该是预处理功能中三种(宏定义,文件包含和条件编译)中的第三种----条件编译。
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <windows.h> 6 7 main() 8 { 9 #ifndef M 10 MessageBoxA(0, "M没有定义", "M没有定义", 0); 11 #endif 12 13 system("pause"); 14 }
如果有多个头文件,其中一个头文件A定义了整型变量,另外一个头文件B包含了头文件A,同时源文件也包含了以上AB两个头文件,这样将会重复定义整型变量。
解决办法:在A头文件中,定义NUM
1 #ifndef NUM 2 #define NUM 3 4 int num = 10; 5 6 #endif
如果有多个头文件,其中一个头文件A定义了函数,另外一个头文件B包含了头文件A,同时源文件也包含了以上AB两个头文件,这样将会重复定义函数。
解决办法:在A头文件中,定义GO
1 #ifndef GO 2 #define GO 3 4 void go() 5 { 6 7 } 8 9 #endif
如果有多个头文件,其中一个头文件A定义了结构体,另外一个头文件B包含了头文件A,同时源文件也包含了以上AB两个头文件,这样将会重复定义结构体。
解决办法:在A头文件中,定义INFO
1 //规避重复包含 2 #ifndef INFO//检测是否定义INFO,没有定义就执行下面一段 3 #define INFO//定义INFO宏 4 5 struct info 6 { 7 char name[20]; 8 int num; 9 }; 10 11 #endif
5 文件包含
#include <只搜索系统的目录>
#include "先搜索源文件目录,再搜索系统的目录"
6
__DATE__
在源代码中插入当前编译日期〔注意和当前系统日期区别开来〕
__FILE__
在源代码中插入当前源代码文件名
__FUNCTION__
__func__
代表所在函数名
__LINE__
在源代码中插入当前源代码行号
__TIME__
在源代码中插入当前编译时间〔注意和当前系统时间区别开来〕
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 void run() 7 { 8 printf("当前文件名%s ", __FILE__); 9 10 printf("当前语句在%d行 ", __LINE__); 11 12 printf("当前函数名%s ", __FUNCTION__); 13 14 printf("编译日期%s编译时间%s ", __DATE__, __TIME__); 15 } 16 17 //当代码出错的时候,可以提示代码错误在哪一行 18 19 main() 20 { 21 printf("当前文件名%s ", __FILE__); 22 23 printf("当前语句在%d行 ", __LINE__); 24 25 printf("当前函数名%s ", __FUNCTION__); 26 27 printf("编译日期%s编译时间%s ", __DATE__, __TIME__); 28 29 run(); 30 31 system("pause"); 32 }
进行软件测试,还有软件测试,需要定位错误,所以在错误的处理语句中,需要加上宏
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 main() 7 { 8 int num1 = 100, num2 = 0; 9 10 if (num2 == 0) 11 { 12 printf("被除数不能为0,%s,%s,%d ", __FILE__, __FUNCTION__, __LINE__); 13 } 14 15 //进行软件测试,还有软件测试,需要定位错误,所以在错误的处理语句中,需要加上宏 16 17 system("pause"); 18 }
C++调试技能
1 #include <iostream> 2 3 #define N//定义N 4 5 void main() 6 { 7 int num(10); 8 9 #ifdef M//没有定义M,所以没有执行#ifdef...#endif之间的语句 10 static_assert(sizeof(num) >= 4, "代码报错 num"); 11 #endif 12 13 #ifdef N//有定义N,所以执行#ifdef...#endif之间的语句 14 static_assert(sizeof(num) >= 4, "代码报错 num"); 15 #endif 16 17 std::cout << __DATE__ << std::endl;//在源代码中插入当前编译日期〔注意和当前系统日期区别开来〕 18 19 std::cout << __FILE__ << std::endl;//在源代码中插入当前源代码文件名 20 21 std::cout << __FUNCTION__ << std::endl;//代表所在函数名 22 23 std::cout << __func__ << std::endl;//代表所在函数名 24 25 std::cout << __LINE__ << std::endl;//在源代码中插入当前源代码行号 26 27 std::cout << __TIME__ << std::endl; //在源代码中插入当前编译时间〔注意和当前系统时间区别开来〕 28 29 system("pause"); 30 }