IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 第一阶段20 C语言预处理 -> 正文阅读

[C++知识库]第一阶段20 C语言预处理

加入CSDN是出于记录,希望有不足之处多加指正。

1、预处理

C语言程序在编译的时候分为4个步骤:
第一步叫预处理:主要工作把#开头的语句先展开。所以说所有的#号开头的语句并不属于C语言的语法范畴。
1、头文件 #include
2、定义宏 #define
3、取消宏 #undef
4、条件编译 # if ,# ifndef, #ifdef ,#else ,#elif ,# endif
5、显示错误 #error
6、修改文件名及行号 #line , #file
7、向编译器传输指令 #progrma

语法:

  • 每一个逻辑行都只能出现一个预处理的指令,如果需要用到多个物理行可以使用反斜杠来链接 \
  • 预处理是整个编译过程中的第一步:预处理 -->汇编–>链接
  • 可以通过编译选项 -E 来查看预处理的结果
$gcc file.c -o file.i -E  // 通过 -E 选项得到预处理后的文件 enum.i
 

2、宏定义

宏(macro)实际上就是要给字符串,在源码经过预处理后就被替换到文件中。

#define PI 3.14

int main()
{
   printf("%f\n ", PI);
   
}

当前的PI 就是一个宏,一般来说我们习惯使用全大写的字来表示宏,语法上并没有约束必须使用大写。PI 只会出现在你的源代码中,预处理过程中以及被完整地替换掉了。

作用:

  • 提高程序地可读性,使用一个有意义的单词来表示一个无意义的数字/ 一个很长的路径/ 名字。
  • 使得程序迭代/ 修改某些值更加方便,只需修改一个地方,程序中所有用到该宏的地方都会被修改,不需要一个一格的找出来并修改。
  • 提高程序的运行效率,如果使用宏函数来替换比较简单而且重复性的函数可以使得程序的效率提高。

3、无参宏

// 自定义宏
#define PI 3.14
#define LCD_SIZE 800*480*4

int main()
{
   printf("pi:%f\n" , PI);
   mmap(NULL, LCD_SIZE,...... );   // #include <sysmman.h>
   mmap(NULL, 800*480*4,......);
}

处理我们自己定义的宏以外系统也有一些预定义的宏

#define NULL ((void *)0)
#define PORT_READ 0x01;

#define EV_SYN 0x00
#define EV_KEY 0x00
#define EV_REL 0x02
#define EV_ABS 0x03

mmap (NULL , LCD_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, 3 ,0); //预处理前
mmap(((void *)0) 800*480*4 , 0x1 | 0x2 , 0x01, 3 ,0) //预处理后

4、带参宏

带参宏在使用的时候需要传递参数有点类似函数的调用。

#define MAX(a,b) a>b? a:b
#define MIN(a,b) a<b? a:b

int main()
{
   int j = 99, k=244;
   printf("max:%d\n" , MAX (j , k));
   printf("min:%d\n" , MIN (j , k));
   return 0;
}

从以上代码中可以看出来,带参宏无非就是把参数与宏中所对应的参数进行逐一替换并在预处理时把替换的字符串直接替换掉代码中宏调用的位置。

注意:

  • 直接替换文,不做任何的语法判断更不会有任何的运算。
  • 宏在编译的第一阶段(预处理)时就被替换。
  • 宏在所有使用的地方都会被展开,浪费一定代码空间,另一方面可以换来一定的效率(代码运行时不需要额外的切换时间)。

5、带参宏的副作用

由于宏在替换的时候没有任何的语法检查、类型检查、数据运算,因此在用起来的时候会出现一些逻辑的小问题。

#define MAX(a,b) (a)>(b) ? (a) :(b)
#define MAX_1(a,b) a>b ? a : b
//带参宏的缺点展示
printf("max:%d\n" , MAX(j,k == 200 ? 888 : 999));
printf("max_1:%d\n" , MAX_1(j,k == 200 ? 888 : 999));

从以上代码中可以发现,宏在定义的时候有括号和没有括号的运算结果时截然不同,说明我们在使用带参宏的时候为了保证运算结果的正确性/优先级的问题不会出现,在使用带参宏的时候每一个参数都可以使用()括号把他们括起来。

6、无值宏

宏定义不一定需要给该宏赋值,无值宏经常出现在条件编译与头文件中

#ifndef __INPUT_H__
#define __INPUT_H__

7、条件编译

通过一个宏来判断某一段代码是否需要编译

#if MACRO
.....
#endif

实例:

#include <stdio.h>
#define MACRO 0
int main()
{
   printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);
 #if MACRO //非零则真
     printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);
 #else
      printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);
 #endif
 return 0;
}

多路分支:

#define MACRO1 0
#define MACRO2 0
#define MACRO3 1
#define MACRO4 0
int main()
{
    printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);
 #if MACRO1  //非零则真
     printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);
  #elif MACRO2
      printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);
   #elif MACRO3
      printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);   
    #elif MACRO4
      printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);
  #endif
  return 0;
}

8、条件编译的使用场景

控制调试语句:在成语中用宏把条件编译的语句给包裹起来,通过GCC编译选在来随意控制调试代码启用/停用。

gcc test.c -o test -DMACRO

以上语句中 -D 意味着define, MACRO 就是程序中用来做条件编译的判断条件的一个宏,这样就可以完全不需要打开代码/修改就能实现代码中某些模块是否需要启用/关闭。

int main()
{
   #ifdef MACRO  //如果该宏被定义则编译
   
      printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);
   #endif
      printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);
   #ifndef MACRO1 //如果该宏没有被定义则编译
      printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);
   #endif
      printf("%d__%s__%s__\n" , __LINE__, __FILE__ , __FUNCTION__);
      return 0;
}

注意:

  • ifdef 用来判断某个宏是否已经被定义,如果有定义则会编译他所包含的代码块
  • ifndef 用来判断某个宏是否没有被定义,如果没有则会编译他所包含的代码块
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-26 21:57:27  更:2021-12-26 21:58:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:47:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码