预处理

在前面的学习中经常遇到用 #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)

大家都在看

  • 30道关于linux的基础命令小题,先练练手

    1.修改主机名为yuanlai0224命令是: 2.切换⽬录到/yuchao01/data/,再创建脚本/my_website/scripts/start.sh。 绝对路径、相对路…

    Linux 2023年5月27日
    0105
  • 快速删除指定文件夹下的.DS_Store文件

    清除当前文件夹中 .DS_store 文件: find "." -name .DS_Store find "." -name .DS_Sto…

    Linux 2023年6月14日
    078
  • 【设计模式】Java设计模式-适配器模式

    【设计模式】Java设计模式 – 适配器模式 😄 不断学习才是王道🔥 继续踏上学习之路,学之分享笔记👊 总有一天我也能像各位大佬一样🏆原创作品,更多关注我CSDN: 一…

    Linux 2023年6月6日
    0178
  • phpcms如何在前台文章列表显示来源

    phpcms的文章来源分为两种,一种是在后台来源中添加完成的,这种”来源”的相关数据存放于数据库的copyfrom表中,通过id和news_data表相关联…

    Linux 2023年6月13日
    088
  • Power Outage

    由于Covid-19的原因一直是work from home, 在几天前家里的电表有问题需要人来维修,在这期间会停电大概半小时操作,虽然是wfh,但是还是要保障工作的正常进行,保守…

    Linux 2023年6月7日
    0112
  • 视图层

    目录 虚拟环境 django路由层版本区别 视图函数返回值 JsonResponse对象 上传文件 request方法 FBV与CBV CBV源剖析 模板语法传值 内容 虚拟环境 …

    Linux 2023年6月7日
    078
  • 007.iSCSI服务器CHAP双向认证配置

    targetcli shell version 2.1.53 Copyright 2011-2013 by Datera, Inc and others. For help on …

    Linux 2023年6月7日
    0121
  • 一文带你掌握Spring Web异常处理方式

    一、前言 最近从单位离职了,离开了五年多来朝朝夕夕皆灯火辉煌的某网,激情也好悲凉也罢,觥筹场上屡屡物是人非,调转过事业部以为能换种情绪,岂料和下了周五的班的前同事兼好朋友,匆匆赶往…

    Linux 2023年6月6日
    0100
  • 干货速看!同行盆友来稿:一文带你搭建K8S高可用集群,以及在上面搭建Prometheus和Grafana。

    写在开篇 kubeadm工具快速部署k8s集群实现故障自动发现、转移及修复,集群中部署prometheus+grafan可实现自动收集集群的各项新性能指标数据,可视化界面提升客户对…

    Linux 2023年6月7日
    099
  • Typora+gitee+picgo搭建本地博客环境

    前言 现在现成的博客平台数不胜数,如果选择服务器+自建博客也有很多方案,可是本人对相片、博客等信息数据总是有本地和互联网各存储一遍才放心的习惯,所以作者本人选择了csdn、博客园、…

    Linux 2023年6月7日
    0125
  • Redis 主从复制

    Redis主从复制的原理 当建立主从关系时,slave配置slaveof 当redis生成dump.rdb文件时,工作过程如下 redis主进程fork一个子进程 fork出来的子…

    Linux 2023年5月28日
    0100
  • 编写一个简单的linux kernel rootkit

    一、前言 linux kernel rootkit跟普通的应用层rootkit个人感觉不大,个人感觉区别在于一个运行在用户空间中,一个运行在内核空间中;另一个则是编写时调用的API…

    Linux 2023年6月8日
    0115
  • 2020年12月-第01阶段-前端基础-HTML CSS 项目阶段(三)

    品优购项目(三) 1. 首页制作 1). 楼层区 floor 注意这个floor 一个大盒子 包含, 不要给高度,内容有多少,算多少 2). 家用电器模块 这个模块 简单 不需要写…

    Linux 2023年6月8日
    078
  • TCP/IP方法安装打印机

    在计算机安装打印机驱动和添加打印机 (TCP/IP 方法 ) 一:打印机的型号和驱动。 1.1 :如何知道自己的打印机是什么品牌和型号?: 通过观察打印机表面的logo 或者其他文…

    Linux 2023年6月13日
    0126
  • 利用prometheus 客户端采集磁盘容量脚本

    点击查看代码 #!/bin/bash #date: 20220621 #author:bin >/tmp/node_dmz.txt >/tmp/node_err.txt…

    Linux 2023年6月14日
    0101
  • MySQL启动报:[ERROR] The server quit without updating PID file

    修改配置后 MySQL启动不了,报错: 看见这个不要惊慌,先把刚才修改的配置注释掉,看是不是配置有误!大部分是手误造成。 如果不行,再尝试一下方法: 解决方法 : 给予权限,执行 …

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