预处理

在前面的学习中经常遇到用 #define命令定义符号常量的情况,其实使用 #define命令就是要定义一个可替换的宏。

宏定义是预处理命令的一种,它提供了一种可以替换源代码中字符串的机制。

根据宏定义中是否有参数,可以将宏定义分为不带参数的宏定义和带参数的宏定义两种,下面分别进行介绍。

使用 #define进行宏定义的好处是需要改变一个常量时只需改变 #define命令行,整个程序的常量都会改变,大大提高了程序的灵活性。

宏名要简单且意义明确,一般习惯用大写字母表示以便与变量名相区别。 注意 : 宏定义不是C语句,不需要在行末加分号。

不带参

#define 宏命 字符串
  • #:表示这是一条预处理命令
  • 宏命是一个标识符,必须符合 C 语言标识符的规范
  • 字符串可以是常数、表达式、格式字符串等
#define PI 3.14159

该语句的作用是在该程序中用 PI替代 3.14159,在 编译预处理时,每当在源程序中遇到 PI就自动用 3.14159代替。

带参宏定义

带参宏定义不是简单的字符串替换,还要进行参数替换,其一般形式如下:

#define 宏名(参数列表) 宏体
  • 对两个参数实现混合运算 test1.c
#define MIX(x, y) ((x)*(y)+(y))
#include "stdio.h"
int main() {
    int a = 5, b = 9;
    printf("the mix num is:%d", MIX(a, b));
}

对上述代码执行 预处理命令

gcc -E test1.c

输出为(省略其他部分):

int main() {
    int a = 5, b = 9;
    printf("the mix num is:%d", ((a)*(b)+(b)));
}

可见在预处理阶段,对源码中的宏名实现了参数的代入和宏体的替换。

用宏替换代替实在的函数的一个好处是宏替换增加了代码的速度,因为不存在函数调用。但增加速度也有代价:由于重复编码而增加了程序长度。

#include "stdio.h"

#define MIX(x, y) ((x)*(y)+y)
#define MIX_1(x, y) (x*y+y)

int main() {
    int a = 5, b = 9;
    // 这行输出是没问题的
    printf("the mix num is : %d", MIX(a, b));
    // 这行是有问题的
    printf("the mix num is : %d", MIX_1(2 + 4, 1 + 7));
    // 这行是没问题的
    printf("the mix num is : %d", MIX_1((2 + 4), (1 + 7)));
}

同样执行预处理命令后的输出为:


int main() {
    int a = 5, b = 9;

    printf("the mix num is : %d", ((a)*(b)+b));

    printf("the mix num is : %d", (2 + 4*1 + 7 +1 + 7));

    printf("the mix num is : %d", ((2 + 4)*(1 + 7)+(1 + 7)));
}

对于宏 #define MIX_1(x, y) (x*y+y)来说,在使用的时候,给参数加上括号,也是可以是计算满足预期,但这不是好的方式。所以建议在定义宏体的时候,就给参数加上括号: #define MIX(x, y) ((x)*(y)+y)

  • 2、宏扩展必须使用括号来保护表达式中低优先级的操作符,以确保调用时达到想要的效果。
  • 3、对带参数的宏的展开,只是将 _语句中的宏名后面括号内的实参字符串_代替 #define命令行中的形参。
  • 4、在宏定义时,宏名与带参数的括号之间不可以加空格,否则会将空格以后的字符都作为替代字符串的一部分。
  • 5、在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。

在一个源文件中使用#include指令可以将另一个源文件的全部内容包含进来,也就是将另外的文件包含到本文件之中。#include使编译程序将另一源文件嵌入带有#include的源文件,被读入的源文件必须用双引号或尖括号括起来。例如:

#include "stdio.h"
#include

这两行代码均使用C编译程序读入并编译,用于处理磁盘文件库的子程序。

上面给出了 双引号尖括号的形式,这两者之间的区别是

  • 用尖括号时,系统到存放C库函数头文件所在的目录中寻找要包含的文件,这为标准方式;
  • 用双引号时,系统先在用户当前目录中寻找要包含的文件,若找不到,再到存放C库函数头文件所在的目录中寻找要包含的文件。

通常情况下,如果为调用库函数用#include命令来包含相关的头文件,则用 尖括号可以节省查找时间,如果要包含用户自己编写的文件,一般使用 双引号,用户自己编写的文件通常在当前目录中。如果不在,则双引号要给出文件的路径。

经常用在文件头部的被包含的文件称为”标题文件”或”头部文件”,一般以.h为后缀,如本实例中的f1.h。

一般情况下将如下内容放到.h文件中:

  • 宏定义。
  • 结构、联合和枚举声明。
  • typedef声明。
  • 外部函数声明。
  • 全局变量声明。

关于”文件包含”有以下几点需要注意。

  • 一个#include命令只能指定一个被包含的文件。
  • 文件包含是可以嵌套的,即在一个被包含文件中还可以包含另一个被包含文件。
  • 若file1.c中包含文件file2.h,那么在预编译后就成为一个文件而不是两个文件,这时如果file2.h中有全局静态变量,则该全局变量在file1.c文件中也有效,这时不需要再用extern声明。

预处理器提供了条件编译功能,一般情况下,源程序中所有的行都参加编译,但是有时希望只对其中一部分内容在满足一定条件时才进行编译,这时就需要使用到一些条件编译命令。使用条件编译可方便地处理程序的调试版本和正式版本,同时还会增强程序的可移植性。

#if的基本含义是:如果 #if命令后的参数表达式为真,则编译 #if#endif之间的程序段,否则跳过这段程序。 #endif命令用来表示 #if段的结束。

#if命令的一般形式如下:

#if 常数表达式
    语句段
#else
#if 常数表达式
    语句段
#endif
#endif

如果 常数表达式为真,则该段程序被编译,否则跳过不编译。

Original: https://www.cnblogs.com/burner/p/yu-chu-li.html
Author: 浪客禅心
Title: 预处理

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/607439/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 白话TCP/IP原理

    TCP/IP(Transmission-Control-Protocol/Internet-Protocol),中文译名为传输控制协议/因特网互联协议,又名网络通讯协议,是Inte…

    Linux 2023年6月14日
    070
  • nginx配置文件单独创建和管理

    在nginx主配置文件nginx.conf的http模块下引入配置文件夹(注意路径的正确性) 1、nginx主配置文件备份后编辑(nginx配置存放位置:/usr/local/ng…

    Linux 2023年6月6日
    079
  • 【微服务】Nacos初体验

    SpringCloud – Nacos初体验 😄生命不息,写作不止🔥 继续踏上学习之路,学之分享笔记👊 总有一天我也能像各位大佬一样🏆 一个有梦有戏的人 @怒放吧德德🌝…

    Linux 2023年6月6日
    0102
  • 使用 Docker 安装 Zabbix,并配置自定义监控项

    镜像下载、域名解析、时间同步请点击阿里云开源镜像站 一、Zabbix 简介 Zabbix 可以用来监控各种网络参数,来保证服务器和系统的安全运行。并且 Zabbix 还提供了灵活的…

    Linux 2023年5月27日
    0130
  • Ubuntu20.04桌面版 使用root账号登录

    sodu -i 执行命令,输入当前用户密码,临时切换到root身份 执行passwd命令,修改root登录密码 passwd 修改50-ubuntu.conf文件 执行如下命令: …

    Linux 2023年6月13日
    083
  • CNN卷积神经网络的构建

    1.卷积神经网络由输入层,卷积层,激活函数,池化层,全连接层组成. input(输入层)–conv(卷积层)–relu(激活函数)–pool(池…

    Linux 2023年6月6日
    080
  • [云计算]TCA云架构-思维导图

    博客园 :当前访问的博文已被密码保护 请输入阅读密码: Original: https://www.cnblogs.com/Skybiubiu/p/15962992.htmlAut…

    Linux 2023年6月13日
    0124
  • Linux 网络分析必备技能:tcpdump 实战详解

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月7日
    082
  • 20 年老程序员告诉你的 20 条编码原则

    我从 1999 年就开始了编程生涯,到今年已经有 20 多年了。我先是从 Basic 开始,很快转到了 Pascal 和 C 语言,然后又学习了面向对象编程语言 Delphi 和 …

    Linux 2023年6月8日
    091
  • 07-MyBatis中的动态标签

    MyBatis中的动态标签 1、if标签 if标签是为了判断传入的值是否符合某种条件,比如是否不为空 2、where标签 where标签可以用来做动态拼接查询条件,当和 if标签配…

    Linux 2023年6月7日
    090
  • MIT6.828(Step0)——实验环境配置

    实验环境配置 VirtualBox虚拟机为载体,安装Ubuntu $ uname -a Linux eliot-VirtualBox 5.11.0-36-generic #40~2…

    Linux 2023年5月27日
    091
  • CentOS7内置Realtek网卡驱动r8169降级r8168

    前几天装了几台服务器测试,在使用的过程中发现,每次重启系统,登录界面会弹出网卡提示 “r8169 0000:02:00 eth0 Invalid ocp reg 177…

    Linux 2023年6月6日
    0111
  • CentOS7 源码安装Nginx及Nginx基本管理设置

    CentOS7 安装 参考文档 CentOS7最小安装后初始化安装工具 1:yum install net-tools 参考文档 2:源码安装wget 参考文档 或者执行 yum …

    Linux 2023年5月27日
    0111
  • Linux ARMv7架构通用中断流程(1)【转】

    一、ARMv7 Cortex-A系列处理器寄存器组介绍及其功能介绍 1. ARMv7 Cortex-A处理器一般共有37寄存器,其中包括: (1) 31个通用寄存器,包括PC(程序…

    Linux 2023年6月8日
    072
  • 我写的 Python 代码,同事都说好

    人生苦短,我用 Python。 程序员的追求就是不写代码,早日财务自由。不对,一不小心把实话说出来了,应该是将代码写得简洁,优雅。 Python 程序员的追求则是 Pythonic…

    Linux 2023年6月7日
    094
  • Linux嵌套目录权限的比较探究

    在/tmp目录下新建一个嵌套目录,名字分别为test_0、test_1、test_2。在test_2目录下新建普通文件,名为tryme。设置test_0和test_2的权限为777…

    Linux 2023年6月7日
    076
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球