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

news2024/12/25 8:52:20

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

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

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

相关文章

MySql实战--一条SQL查询语句是如何执行的?

平时我们使用数据库&#xff0c;看到的通常都是一个整体。比如&#xff0c;你有个最简单的表&#xff0c;表里只有一个ID字段&#xff0c;在执行下面这个查询语句时&#xff1a; select * from T where ID10&#xff1b; 我们看到的只是输入一条语句&#xff0c;返回一个结果…

Java学习笔记 | JavaSE基础语法 | 04 | 数组

文章目录 0.前言1.数组2.数组声明2.1 数组定义2.2 数组初始化1.静态初始化2.动态初始化3.区别4.数组的默认初始化值&#xff1a; 2.3 数组名 3.访问数组3.1 索引3.2 访问数组3.3 length属性 4.数组常见问题5.数组内存分析5.1 内存分配5.2 数组内存分配 6.数组的练习练习1&#…

重磅!一起做个淘宝的简易布局!(超详细)

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端程序媛。 因为之前的学习内容&#xff0c;今天&#xff0c;我们可以来综合运用一下标签和 CSS 样式&#xff0c;做一个简易的淘宝网页大体布局了&#xff0c;如图。 咱们今天要做成这样子&#xff01; 里面…

19.严丝合缝的文明——模板方法模式详解

“项目评审的节点又快到了&#xff0c;PPT你写了没&#xff1f;” “Oops&#xff0c;忘了&#xff0c;有模板没&#xff1f;给我一份” 概述 模板&#xff0c;一个频繁出现在办公室各类角色口中的词&#xff0c;它通常意味着统一、高效、经验和优质。各项汇报因为PPT的模板变…

C语言栈和队列(个人笔记)

栈和队列 栈1.1栈的概念和结构1.2栈的实现 队列2.1队列的概念及结构2.2队列的实现2.3循环队列 栈和队列笔试题3.1[有效的括号](https://leetcode.cn/problems/valid-parentheses/submissions/516297357/)3.2[用队列实现栈](https://leetcode.cn/problems/implement-stack-using…

HCIA实验

实验目的&#xff1a; 1、R6为ISP&#xff0c;接口IP地址均为公有地址&#xff0c;该设备只能配置IP地址&#xff0c;之后不能再对其进行任何配置&#xff1b; 2、R1-R5为局域网&#xff0c;私有IP地址192.168.1.0/24&#xff0c;请合理分配&#xff1b; 3、R1、R2、R4&#x…

阿里云服务器一年多少钱?2024最新活动价格表整理分享

2024年阿里云服务器优惠价格表&#xff0c;一张表整理阿里云服务器最新报价&#xff0c;阿里云服务器网整理云服务器ECS和轻量应用服务器详细CPU内存、公网带宽和系统盘详细配置报价单&#xff0c;大家也可以直接移步到阿里云CLUB中心查看 aliyun.club 当前最新的云服务器优惠券…

C语言 青蛙跳台阶问题

1.问题描述 一只青蛙可以一次跳一级台阶&#xff0c;也可以一次跳两级台阶&#xff0c;如果青蛙要跳上n级台阶有多少种跳法&#xff1f; 2.问题分析 当台阶只有一级时&#xff0c;只能跳一级&#xff0c;所以只有一种跳法 当台阶有两级时&#xff0c;可以先跳一级&#xff…

HDFS集群环境配置

HDFS集群环境配置 环境如下三台服务器&#xff1a; 192.168.32.101 node1192.168.32.102 node2192.168.32.103 node3 一、Hadoop安装包下载​​​​​​​ 点此官网下载​​​​​​​ 二、Hadoop HDFS的角色包含&#xff1a; NameNode&#xff0c;主节点管理者DataNode&am…

kubernetes最小调度单元Pod概述

Pod概述 一.Pod的概念1.Pod是什么2.Pod网络共享实现方式3.Pod存储共享方式4.创建Pod的流程 二.使用YAML文件定义Pod资源1.Pod资源清单YAML文件书写技巧1.YAML语法格式&#xff1a;2.配置Linux tab缩进两个空格3.使用kubectl explain帮助命令 2.创建Pod及Pod常用命令1.创建Pod资…

MySQL-1.数据库的基本操作

1. 数据库的基本操作 show databases; information_schema&#xff1a;信息图式&#xff0c;存储服务器管理数据库的信息 mysql&#xff1a;存放系统信息&#xff0c;用户名密码等 performance_schema&#xff1a;性能图式 sys&#xff1a;系统文件 1.1 创建数据库-studen…

瑞吉外卖实战学习--项目搭建

瑞吉外卖实战学习 前言1、创建springBoot 项目&#xff0c;并引用相关依赖2、配置数据库3、通过注解检测项目是否可以启动成功4、配置前端页面的静态映射4.1 前端文件放置的位置4.2 由于存放的位置并不是默认的文件中&#xff0c;需要将这些文件静态映射4.3 检测静态文件是否可…

003- AutoCoder 使用Web版大模型,性感的Human As Model 模式

这是下面这篇文章的继续。 002- 用 AutoCoder 添加和修改代码 前面我们提到&#xff0c;如何解决你没有API版大模型&#xff0c;或者你的API版大模型太弱&#xff0c;而你只有Web版本的诸如 Kimi/GPT4 的情况下&#xff0c;改如何让AutoCoder帮助你完成编程&#xff1f; 我们有…

数据结构(五)——树与二叉树的应用

5.5 树与二叉树的应用 5.5.1 哈夫曼树 结点的权&#xff1a;有某种现实含义的数值。 结点的带权路径长度&#xff1a;从树的根到该结点的路径长度&#xff08;经过的边数&#xff09;与该结点上权值的乘积。 树的带权路径长度&#xff1a;树中所有叶结点的带权路径长度之和…

Linux——进程信号(二)

目录 1、阻塞信号 1.1、信号其他相关常见概念 1.2、在内核中的表示 1.3、sigset_t 1.4、信号集操作函数 2、捕捉信号 2.1、内核如何捕捉信号 5.2、sigaction 1、阻塞信号 1.1、信号其他相关常见概念 实际执行信号的处理动作被称为信号递达&#xff08;Delivery&#x…

电脑桌面便签,怎么在电脑桌面上设置便签

在数字化时代&#xff0c;电脑已成为我们日常生活不可或缺的一部分。在我们使用电脑进行各种工作和学习的过程中&#xff0c;经常会遇到需要记录临时信息或提醒自己的情况。这时&#xff0c;设置便签在电脑桌面上就成为了一种非常便捷的方法。那么有一个问题&#xff0c;电脑桌…

(一)基于IDEA的JAVA基础8

使用多重if选择结构 多个if条件进行判断: 语法: if(条件1){ 执行语句1&#xff1b; }else if(条件2){ 执行语句2&#xff1b; }else if(条件3){ 执行语句3&#xff1b; }else if (条件4)…… 流程图: 我们来写个好玩的&#xff0c;对暗号: public class Test01 { …

web前端之罗盘时钟、不一样的补零方式、LED字体、padStart

MENU 效果图htmlJavaScriptstyle 效果图 html <div class"clock"><div class"second-box"></div><div class"minute-box"></div><div class"hour-box"></div><div class"day-box&…

atoi函数的使用和模拟实现

1.atoi函数简介 (1).atoi函数原型 &#xff1a;int atoi (const char * str); (2).头文件&#xff1a;<stdlib.h> 用法&#xff1a;将字符串里的数字字符转化为整形数。返回整形值。 注意&#xff1a; 转化时跳过前面的空格字符&#xff0c;直到遇上数字或正负符号才开…

C++中的lambda表达式

引入: 首先来看一个例子 struct fruit {double _price;int _evalute;string _name;fruit(const char* str, int a, double price):_name(str),_evalute(a),_price(price){} }; struct ComparePriceGreater {bool operator()(const fruit& g1, const fruit& gr){return…