C语言程序与设计——预处理命令

在C语言中宏有三种形式:

  1. 定义符号常量
  2. 定义傻瓜表达式
  3. 定义代码段
    在使用宏的过程中需要注意的是,宏的作用仅仅是在预处理阶段对代码进行替换,而非进行运算,所以在使用时,如果出现了我们预期之外的结果,很有可能是宏没有处理好。下面就挨个演示一下。

定义符号常量

着一种形式比较简单只是单纯的替换,这里对该种情况不做过多介绍

#include<stdio.h>
#define PI 3.1415926535int main(){int r = 5;printf("[r] = %d [S] = %f", r, PI * r * r);
}

定义傻瓜表达式

可以通过宏封装一个简易的表达式,但是为什么叫傻瓜表达式呢,正如之前所说,宏只是进行替换,而非进行计算,所以在没有考虑周全的情况下,很容易出错。下面我们以封装一个比较函数为例,返回大的值。
分别用到下面五组示例

#include<stdio.h>
#define MAX(a, b) a > b ? a : b
#define P(func) printf("%s = %d\n", #func, func);int main(){int a = 7;P(MAX(2, 3));P(5 + MAX(2, 3));P(MAX(2 ,MAX(2, 3)));P(MAX(2, 3 > 4 ? 3 : 4));P(MAX(a++, 3));
}

先解释一下代码,主函数部分是我们的测试用例不需要赘述。封装的MAX(a,b)表达式就是我们想要实现的功能。下面的P宏是我为了方便实现的打印的宏,其中#func这个参数的意义就是字符串话,可以使得运行结果更容易观察。

运行结果:
在这里插入图片描述
可以看到除了第一个测试用例基本上这些都不是正确答案。为了探究原因我们就需要看到把宏展开,也就是预处理之后的样子。使用gcc -E filename命令即可。
在这里插入图片描述
可以看到他们宏仅仅是做了替换,实际的运算顺序与我们预期的不符合,所以我们就需要使用小括号来使得运算顺序满足我们的预期。

#include<stdio.h>
#define MAX(a, b) ((a) > (b) ? a : b)
#define P(func) printf("%s = %d\n", #func, func);int main(){int a = 7;P(MAX(2, 3));P(5 + MAX(2, 3));P(MAX(2 ,MAX(2, 3)));P(MAX(2, 3 > 4 ? 3 : 4));P(MAX(a++, 3));
}

在这里插入图片描述
可以看到前四个结果已经达到我们预期的结果了。但是第五个结果与预期不符合。可以从上面预处理之后的代码中可以看出,它的错误并不是计算顺序的原因,而是我们对a++访问之后自增1,而后进行返回的原因,若想修复这个bug我们需要一个中间变量来记住增1前的a值

定义代码段

#include<stdio.h>//#define MAX(a, b)(((a) > (b) ? a : b))#define MAX(a, b)({\__typeof(a) _a = a;\__typeof(b) _b = b;\_a > _b ? _a : _b;\
})#define P(func) printf("%s = %d\n", #func, func);int main(){int a = 7;P(MAX(2,3));P(5 + MAX(2,3));P(MAX(2,MAX(2,3)));P(MAX(2,3 > 4 ? 3 : 4));P(MAX(a++,6));
}

运行结果:
在这里插入图片描述
这里解释一下新出现的t__typeof,功能就是提取变量类型的关键字,然后进行定义,也就是说 __typeof(a) _a = a;改行代码与 int _a = a;相同。当宏有多行的时候,需要使用\来链接。

内置的宏

说明
__ DATE __日期Mmm dd yyy
__ FILE __文件名
__ LINE __行号
__ TIME __时间:hh:mm:ss
__ func __函数名(非标准)
__ Func __函数名(非标准)
__ PRETTY_FUNCTION __更详细的的函数信息(非标准)

其中非标准的含义是指不同的环境下可能会出现不同的结果,或者是不存在该宏。比如我是在ubantu系统下运行的,没有__Func__的宏
在这里插入图片描述

#include<stdio.h>
#define P(a) printf("[%s] = %s\n", #a, a);int main(){P(__DATE__);P(__FILE__);printf("[__LINE__] = %d", __LINE__);P(__TIME__);P(__func__);P(__PRETTY_FUNCTION__);
}

在这里插入图片描述

条件式编译

在宏当中也可以实现条件分支的功能实现代码剪裁

函数说明
#ifdef DEBUG是否定义了DEBUG
#ifndef DEBUG是否没定义了DEBUG
#if MAX_N ==5宏MAX_N 是否等于5
#ieif MAX_N ==4否则宏MAX_N 是否等于4
#else
#endif
打印日志信息

编写一个程序,通过宏实现一个log()功能打印,打印所在文件,行号,函数,以及内容。当需要DEBUG时log()可以使用,不需要DEBUG时,则log()函数无任何作用

#include<stdio.h>#define DEBUG
#ifdef DEBUG
#define log(frm, args...){\printf("[FILE]:%s [LINE]:%d [func]:%s [content]:", __FILE__, __LINE__,__func__);\printf(#frm, ##args);\printf("\n");\
}
#else
#define log(frm, args...)
#endifint add(int a, int b){return a + b;
}int main(){log(add(1 , 5));
}输出结果:
>> [FILE]:log.c [LINE]:24 [func]:main [content]:add(1 , 5)

"#"和”##“的作用:

  • ”#“是将后面的内容字符串化,保证输出
  • ”##“是连接作用,可以连接两个变量名,在这里是保证当log传入参数只有一个时,不会报错(int a, b, ab; a##b 是与ab等价的)

泛型宏(Generic macro)

#include<stdio.h>
#define TYPE(a) _Generic((a), \int : "%d\n",\double: "%lf\n", \char * : "%s\n"\
)int main(){int a = 1;double b = 4.0;char string[] = "hello";printf(TYPE(a),a);printf(TYPE(b), b);printf(TYPE(string), string);
}运行结果:
>> 1
4.000000
hello

在这里插入图片描述

从上图可以看到除了泛型宏以外,自定义的函数在主函数之外也可以运行,而且会先于主函数运行。这是因为我们在函数上面为其添加了属性使其可以能够单独运行。尽管函数可以独立运行,但是一个程序是不可以没有主函数的,否则会有编译错误。

补充:字符串

在预处理阶段,不只是宏,还有头文件也会被展开,在这里简单介绍一下一个比较重要的头文件<string.h>,该头文件包含对于字符串的操作比较方便。

函数说明
strlen(str)计算字符串长度,以\0作为结束符
strcmp(str1, str2)字符串比较
strcpy(dest, src)字符串拷贝
strcmp(str1, str2, n)安全字符串比较
strcpy(dest, src, n)安全字符串拷贝
memcpy(str1, str2, n)内存拷贝
memcmp(str1, str2, n)内存比较
memset(str,c, n)内存设置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/770130.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring IoC DI(1)

IoC & DI入门 Spring 通过前面的学习, 我们知道了Spring是一个开源框架, 它让我们的开发更加简单. 它支持广泛的应用场景, 有着活跃且庞大的社区, 这就是Spring能够长久不衰的原因. 但是这个概念还是比较抽象. 可以用更具体的话描述Spring, 那就是: Spring是包含了众多…

Jakarta项目介绍

概述 在升级Spring Boot到3.0版本以后&#xff0c;或升级Spring到6.0版本以上&#xff0c;会发现应用编译失败或启动失败等问题。 经过排查不难得知&#xff0c;Spring 6或Spring Boot 3&#xff08;实际上依赖于Spring 6&#xff09;不再支持javax.开头的一系列依赖包&#…

人工智能的迷惑行为:AI世界的隐秘角落

人工智能迷惑行为大赏 在当今数字化时代&#xff0c;人工智能技术的飞速发展给我们的生活带来了诸多便利和可能性&#xff0c;但同时也伴随着一些令人困惑的现象和行为。本文将深入探讨人工智能的迷惑行为&#xff0c;揭示AI世界中的隐秘角落&#xff0c;让我们一同探寻这个充…

《深入浅出LLM 》(二):大模型基础知识

&#x1f389;AI学习星球推荐&#xff1a; GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系的学习资料&#xff0c;配有全面而有深度的专栏内容&#xff0c;包括不限于 前沿论文解读、…

CPU缓存行及伪共享

CPU Cache概述 随着CPU的频率不断提升&#xff0c;而内存的访问速度却没有质的突破&#xff0c;为了弥补访问内存的速度慢&#xff0c;充分发挥CPU的计算资源&#xff0c;提高CPU整体吞吐量&#xff0c;在CPU与内存之间引入了一级Cache。随着热点数据体积越来越大&#xff0c;…

代码随想录算法训练营第三十二天 | 122.买卖股票的最佳时机II ,55. 跳跃游戏 , 45.跳跃游戏II

贪心&#xff1a;只要把每一个上升区间都吃到手&#xff0c;就能一直赚 class Solution { public:int maxProfit(vector<int>& prices) {int res 0;for(int i 1;i< prices.size();i){int diff prices[i] - prices[i-1];if(prices[i] > prices[i-1]){res d…

蓝桥杯练习题总结(二)dfs题、飞机降落、全球变暖

目录 一、飞机降落 二、全球变暖 初始化和输入 确定岛屿 DFS搜索判断岛屿是否会被淹没 计算被淹没的岛屿数量 三、军训排队 一、飞机降落 问题描述&#xff1a; N架飞机准备降落到某个只有一条跑道的机场。其中第 i 架飞机在 时刻到达机场上空&#xff0c;到达时它的剩余…

[Linux]文件缓冲区

文件fd 输出重定向除了用dup2()改变数组下标外&#xff0c;还可以用命令来完成 所有的命令执行&#xff0c;都必须有操作系统将其运行起来变成进程&#xff0c;然后根据>>, <<来判断是输入重定向&#xff0c;还是输出重定向。 缓冲区 之所以有缓冲区&#xff0…

JavaScript 基础、内置对象、BOM 和 DOM 常用英文单词总结

一提到编程、软件、代码。对于英语不是很熟悉的同学望而却步。其实没有想像中的难么难&#xff0c;反复练习加上自己的思考、总结&#xff0c;会形成肌肉记忆。整理一下&#xff0c;初学者每天30遍。 1、JavaScript 基础语法 break&#xff1a;中断循环或 switch 语句的执行。…

安卓转鸿蒙竟如此丝滑

随着鸿蒙的爆火&#xff0c;大家都想知道鸿蒙能不能搞&#xff1f; 相信大家搞开发的&#xff0c;都多多少少的了解过鸿蒙。近几个月鸿蒙的大动作也不少&#xff0c;如&#xff1a;重庆市近20个垂域应用与鸿蒙原生合作、深圳制定鸿蒙《行动计划》、阿里再次与鸿蒙展开合作&…

微信小程序开发技巧:canvas实现电子签名

在微信小程序中实现电子签名功能方式很多,本文采用canvas绘制的方式实现。具体实现步骤如下: 在页面中添加canvas元素 <view class"container"><canvas canvas-id"signCanvas" class"canvas" disable-scrolltrue touchstart"sta…

跨时钟域学习记录(一)

亚稳态 亚稳态是电平介于高低电平之间的一种暂时状态。在同步系统中&#xff0c;当寄存器的信号无法满足建立时间和保持时间时&#xff0c;输出端的信号就可能出现亚稳态。在异步系统中&#xff0c;亚稳态并非一定在建立时间和保持时间无法满足时出现。   受噪声、温度、电压…

【vue3.0】实现导出的PDF文件内容是红头文件格式

效果图: 编写文件里面的主要内容 <main><div id"report-box"><p>线索描述</p><p class"label"><span>线索发现时间:</span> <span>{{ detailInfoVal?.problem.createdDate }}</span></p><…

MyBatis3源码深度解析(二十一)动态SQL实现原理(二)动态SQL解析过程、#{}和${}的区别

文章目录 前言8.5 动态SQL解析过程8.5.1 SQL配置转换为SqlSource对象8.5.2 SqlSource转换为静态SQL语句 8.6 #{}和${}的区别8.7 小结 前言 在【MyBatis3源码深度解析(二十)动态SQL实现原理(一)动态SQL的核心组件】中研究了MyBatis动态SQL相关的组件&#xff0c;如SqlSource用于…

AAC相关知识

一、AAC音频格式种类有哪些 AAC音频格式是一种由MPEG-4标准定义的有损音频压缩格式。AAC包含两种封装格式 ADIF&#xff08;Audio Data Interchange Format音频数据交换格式&#xff09;和ADTS&#xff08;Audio Data transport Stream音频数据传输流&#xff09;。 ADIF 特点…

promethus的安装使用

1、# 软件下载地址 https://prometheus.io/download/ https://grafana.com/grafana/download https://prometheus.io/download/ Prometheus是一套开源的监控&报警&时间序列数据库的组合,起始是由SoundCloud公司开发的。 Prometheus 的优点 1、非常少的外部依赖,安装…

OceanBase4.2.2.1 单机集群在ArmX86安装(自测记录)

OceanBase OceanBase就不必多加介绍了&#xff0c;本次主要是分享对于它的安装使用&#xff0c;先说说背景&#xff0c;首先接触是因为信创国产化的要求&#xff0c;为满足支持国产化&#xff0c;安装了Arm架构下版本4.0.0&#xff0c;满足支持通过。后来项目实际使用&#xff…

由浅到深认识Java语言(20):包装类

该文章Github地址&#xff1a;https://github.com/AntonyCheng/java-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.c…

leetcode刷题日记-外观数组

题目描述 解题思路 初始化字符串 init 为 “1”&#xff0c;作为外观数列的第一项。 通过循环迭代生成外观数列的下一项&#xff0c;循环次数为 n-1&#xff0c;因为已经初始化了第一项。 在每次迭代中&#xff0c;通过两个指针 pos 和 start 来遍历当前项 init&#xff0c;po…