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

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

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

定义符号常量

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

#include<stdio.h>
#define PI 3.1415926535

int 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...)
#endif

int 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)内存设置

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux Load AVG linux 平均负载是什么? 简单解释说明

linux 命令基础汇总 命令&基础描述地址linux curl命令行直接发送 http 请求Linux curl 类似 postman 直接发送 get/post 请求linux ln创建链接&#xff08;link&#xff09;的命令创建链接&#xff08;link&#xff09;的命令linux linklinux 软链接介绍linux 软链接介绍l…

Java代码基础算法练习-搬砖问题-2024.03.25

任务描述&#xff1a; m块砖&#xff0c;n人搬&#xff0c;男搬4&#xff0c;女搬3&#xff0c;两个小孩抬一砖&#xff0c;要求一次全搬完&#xff0c;问男、 女、小孩各若干&#xff1f; 任务要求&#xff1a; 代码示例&#xff1a; package M0317_0331;import java.util.S…

VUE:内置组件<Teleport>妙用

一、<Teleport>简介 <Teleport>能将其插槽内容渲染到 DOM 中的另一个位置。也就是移动这个dom。 我们可以这么使用它: 将class为boxB的盒子移动到class为boxA的容器中。 <Teleport to".boxA"><div class"boxB"></div> &…

RuoYi-Vue若依框架-新增子模块启动后,前端页面报接口404

如何新建子模块可以参考RuoYi-Vue若依框架-如何新增子模块 我在新增依赖的时候提过版本号的问题&#xff0c;如果不是按照我的博客走的&#xff0c;然后接口报了404&#xff0c;可以选择添加父版本号&#xff0c;官方的参考文档是没写的&#xff0c;但添加了确实能解决这个问题…

数字芯片retention cell

​低功耗设计一直是芯片设计的重中之重&#xff0c;景芯SoC训练营采用的低功耗技术有&#xff1a; 1、Clk gating&#xff0c;关掉不工作模块的时钟信号&#xff1b; 2、Power gating&#xff0c;关掉不工作模块的电源&#xff1b; 由于省去了leakage power&#xff0c;Powe…

蓝桥杯第十五届抱佛脚(三)枚举法与尺取法

蓝桥杯第十五届抱佛脚&#xff08;三&#xff09;枚举法与尺取法 很多人将蓝桥杯戏称为“暴力杯”&#xff0c;因为蓝桥杯采用了OI的赛制。在这种赛制下&#xff0c;即使对于某些大题答案不清楚的情况下&#xff0c;也可以通过暴力方法获得部分分数。一提到暴力&#xff0c;人…

Java代码基础算法练习-字符串反转-2024.03.25

任务描述&#xff1a; 输入一个字符串&#xff0c;然后将此字符串反转&#xff08;字符串最长不超过25个字符&#xff09; 任务要求&#xff1a; 代码示例&#xff1a; package M0317_0331;import java.util.Scanner;public class m240325_1 {public static void main(String…

STM32之HAL开发——串口配置(CubeMX)

串口引脚初始化&#xff08;CubeMX&#xff09; 选择RCC时钟来源 选择时钟频率&#xff0c;配置为最高频率72MHZ 将单片机调试模式打开 SW模式 选择窗口一配置为异步通信模式 点击IO口设置页面&#xff0c;可以看到当前使用的串口一的引脚。如果想使用复用功能&#xff0c;只需…

MySQL之基本操作与用户授权

一 基本操作 1 SQL分类 数据库&#xff1a;database 表&#xff1a;table&#xff0c;行&#xff1a;row 列&#xff1a;column 索引&#xff1a;index 视图&#xff1a;view 存储过程&#xff1a;procedure 存储函数&#xff1a;function 触发器&#xff1a;trigger 事…

【JAVA】建立一个图书管理系统

在建立一个图书管理系统的时候我们首先需要构造一个书类 public class Book {private String name;private String author;private int price;private String type;private boolean isBorrwed;public Book(String name, String author, int price, String type) {this.name n…

【自我提升】计算机领域相关证书

目录 计算机技术与软件专业资格&#xff08;水平&#xff09;考试证书&#xff08;软考&#xff09;Oracle认证Cisco认证微软认证红帽认证AWS认证 计算机技术与软件专业资格&#xff08;水平&#xff09;考试证书&#xff08;软考&#xff09; 计算机技术与软件专业技术资格&a…

坚果N1S Pro和当贝X5 Pro哪款品质最高?当贝X5 Pro质价比更高

家用投影发展至今已经越来越卷&#xff0c;4K、激光等技术也经常被用在家用投影仪上&#xff0c;4K激光投影这类高端投影也出现了不少产品&#xff0c;其中被讨论最多的也是市面上销量比较好的&#xff0c;就是当贝X5 Pro和坚果N1S Pro这两款&#xff1b;很多人想知道这两台家用…

2024年大模型面试准备(三):聊一聊大模型的幻觉问题

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学&#xff0c;针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何备战、面试常考点分享等热门话题进行了深入的讨论。 合集在这…

SuperGluePretrainedNetwork 详细解读

目录结构展示了SuperGluePretrainedNetwork项目的简化版布局。这是一个关于使用SuperGlue算法进行图像配对的深度学习项目&#xff0c;主要包括预训练的模型和执行配对的脚本。 demo_superglue.py demo_superglue.py脚本的主要作用是展示SuperGlue预训练网络在图像对上进行特征…

Linux下命令行方式导入Mysql数据库

一. 正常Mysql导入 直接登录mysql进行导入即可 mysql -u用户名 -p密码 要导入的数据库名 < 数据库名.sql mysql -u用户名 -p密码 db_doc < 20210616.sql二. Docker镜像方式Mysql导入 1. docker启动mysql docker exec -it mysal bash 2. 登录mysql mysql -uroot -p密…

软件设计师21--操作系统章节回顾

软件设计师21--操作系统章节回顾 章节重要内容考情分析 章节重要内容 考情分析

消息队列八股

RabbitMQ 确保消息不丢失 重复消费问题 延迟队列 消息堆积 高可用 很少使用 Kafka 如何保证消息不丢失 回调接口保证生产者发送到brocker消息不丢失 保证消息顺序性 高可用机制 数据清理机制 实现高性能的设计

公平锁和非公平锁,为什么要“非公平”?

公平锁和非公平锁&#xff0c;为什么要“非公平”&#xff1f; 主要讲一讲公平锁和非公平锁&#xff0c;以及为什么要“非公平”&#xff1f; 什么是公平和非公平 首先&#xff0c;我们来看下什么是公平锁和非公平锁&#xff0c;公平锁指的是按照线程请求的顺序&#xff0c;…

欧科云链OKLink:坎昆升级后,Layer2项目是否更具竞争力?

在坎昆升级激活之际&#xff0c;OKLink 上线以太坊坎昆升级 Dencun 专题页 &#x1f449; 从专业链上数据分析角度&#xff0c;带来一场充实且即时的 Layer2 数据盛宴。 在近日由 137Labs 发起&#xff0c;Cointime 主持的 Layer2 生态专场讨论中&#xff0c;OKLink 产品…

AISD智能安全配电装置--智能监测、远程监控

安科瑞薛瑶瑶18701709087 AISD100单相、AISD300三相智能安全配电装置是安科瑞专为低压配电侧开发的一款智能安全配电产品。主要针对低压配电系统人身触电、线路老化、短路、漏电等原因引起电气安全问题而设计。 产品主要应用于学校、加油站、医院、银行、疗养院、康复中心、敬…