文章目录

  • 预处理
    • 宏定义
          • 数值宏常量
          • 字符串宏常量
          • 使用宏充当注释
            • 去注释和宏替换谁先进行
          • 用define宏定义表达式
            • 在if判断语句之后多条语句没有{}导致出错
          • 定义不能带空格
        • #undef
    • 条件编译
            • `#ifdef` `ifndef`:判定的是宏是否被定义
            • `#if-#endif`:宏定义是否为真
            • 宏定义进行多条件筛选
            • `#ifdef #if defined() #ifndef #if !defined()`
            • 条件编译支持嵌套
            • 多条件检测宏是否被定义
          • 为什么要有条件编译
    • 文件包含
            • 什么叫头文件展开?
            • 重复包含是错误吗?
    • #error
    • #line
    • #pragma
    • #运算符
    • ##运算符

预处理

宏定义

数值宏常量

define PI 3.14

字符串宏常量

define PATH "C:\\Users\\yuanwei\\Desktop"

结论:宏定义代表字符串的时候,一定要带上双引号,可以用\续行 ,双\\避免转义字符的存在.

使用宏充当注释
//程序翻译过程://预处理-E :头文件展开,去注释,宏替换->.i//编译-S : 将干净的C语言,编译成为汇编语言->.s//汇编-c :将汇编翻译成为目标二进制文件->.o//链接 :将目标二进制文件与相关库链接,形成可执行程序
去注释和宏替换谁先进行
  • 先进行去注释然后宏替换.

用define宏定义表达式

在if判断语句之后多条语句没有{}导致出错
  • 简单宏替换出现编译报错

  • 宏替换多条语句封装进do{}while(0)句式之中(先执行再判断,执行一次就行)
#include#define INIT_VAL(a,b) do{a=0;b=1;}while(0)int main(){int x=10;int y=20;int flag=0;scanf("%d",&flag);if(flag)INIT_VAL(x,y);else{x=100;y=200;}printf("%d-%d\n",x,y);return 0;}
定义不能带空格
#include #define INC(a) ((a)++) //定义不能带空格# defineNUM 10 //define前面可以加空格(但是不建议) int main(){int i = 0;INC (i); //使用可以带空格,但是严重不推荐(不要处处显得自己不一样哦)printf("%d\n", i);}

#undef

宏可以在代码的任何地方定义.作用范围是从定义处往后都可.

  • 源文件的任何地方,宏都可以定义,与是否在函数内外没关系.

  • 宏替换是在函数调用之前的.

after #undef,取消宏或者宏的作用范围限制结尾处.

int main(){ #define X 3#define Y X*2#undef X#define X 2int z = Y;printf("%d\n", z);//4return 0;}

条件编译

宏是否被定义 VS 宏是否为真or假

#define DEBUG#define DEBUG 1#define DEBUG 0
#ifdef ifndef:判定的是宏是否被定义

#if-#endif:宏定义是否为真

宏定义进行多条件筛选
#include2 int main()3 {4 #if VERSION==15 printf("hello VERSION==1\n");6 #elif VERSION==27 printf("hello VERSION==2\n");8 #elif VERSION==39 printf("hello VERSION==3\n"); 10 #else 11 printf("hello default version\n"); 12 #endif13 return 0; 14 }

#ifdef #if defined() #ifndef #if !defined()
#include #define DEBUGint main(){ #if defined(DEBUG)printf("hello debug\n");#elseprintf("hello release\n");#endifreturn 0;} //演示#if模拟#ifndef#include //#define DEBUGint main(){ #if !defined(DEBUG)printf("hello debug\n");#elseprintf("hello release\n");#endifreturn 0;}//code1#include #define C#define CPPint main(){ #if defined(C) && defined(CPP)//#if (defined(C) && defined(CPP))printf("hello c&&cpp\n");#elseprintf("hello other\n");#endifreturn 0;} //Code 2#include #define C#define CPPint main(){ #if defined(C) || defined(CPP)//#if (defined(C) || defined(CPP))//#if !(defined(C) && defined(CPP))printf("hello c&&cpp\n");#elseprintf("hello other\n");#endifreturn 0;}
条件编译支持嵌套
int main(){ #if defined(C)#if defined (CPP)printf("hello CPP\n");#endifprintf("hello C\n");#elseprintf("hello other\n");#endifreturn 0;}
多条件检测宏是否被定义
#include //#define C//#define CPPint main(){ #if defined(C)printf("hello C\n");#elif defined (CPP)printf("hello CPP\n");#elseprintf("hello other\n");#endifreturn 0;}
为什么要有条件编译
  • 通过裁剪代码,快速实现某种目的(版本维护),功能裁剪,跨平台性.

本质认识:条件编译,其实就是编译器根据实际情况,对代码进行裁剪。而这里“实际情况”,取决于运行平台,代码本身的业务逻辑等。可以认为有两个好处:

  1. 可以只保留当前最需要的代码逻辑,其他去掉。可以减少生成的代码大小
  2. 可以写出跨平台的代码,让一个具体的业务,在不同平台编译的时候,可以有同样的表现

举一个例子吧
我们经常听说过,某某版代码是完全版/精简版,某某版代码是商用版/校园版,某某软件是基础版/扩展版等。其实这些软件在公司内部都是项目,而项目本质是有多个源文件构成的。所以,所谓的不同版本,本质其实就是功能的有无,在技术层面上,公司为了好维护,可以维护多种版本,当然,也可以使用条件编译,你想用哪个版本,就使用哪种条件进行裁剪就行。
著名的Linux内核,功能上,其实也是使用条件编译进行功能裁剪的,来满足不同平台的软件。

文件包含

#pragma once& 条件编译的方式

第一次宏未定义,会立即定义.第二次之后因为#ifndef不再执行.

什么叫头文件展开” />重复包含是错误吗?

主要是编译的效率问题.那么,重复包含一定报错吗??不会!
重复包含,会引起多次拷贝,主要会影响编译效率!同时,也可能引起一些未定义错误,但是特别少。

#error

核心作用是可以进行自定义编译报错.编译时直接中止.

#include //#define __cplusplusint main(){ //#error 你好啊#ifndef __cplusplus#error 老铁,你用的不是C++的编译器哦#endifsystem("pause");return 0;}

#line

本质其实是可以定制化你的文件名称和代码行号,很少使用.

#pragma

以编译通过的方式让编译器打出信息.

//#pragma message()作用:可以用来进行对代码中特定的符号(比如其他宏定义)进行是否存在进行编译时消息提醒#include #define M 10int main(){ #ifdef M#pragma message("M宏已经被定义了")#endifsystem("pause");return 0;}

#运算符

  • 将参数符号s对应的文本内容转化为字符串.
  • 相邻字符串(“”)具有自动连接特性.

//利用特性实现int->const char* #define TOSTRING(x) #xint main(){char* str[1024] = {0};strcpy(str,TOSTRING(123456));//是预处理阶段处理int a=123;strcpy(str,TOSTRING(a));//a//变量已经是运行之后才知道是啥数,所以只能打印出aprintf("%s\n",str);return 0;}

##运算符

将左侧符号和右侧符号链接起来构成一个新符号.

  • 无效符号

  • 有效符号