浅谈C语言预定义中的预定义符号,#define,以及符号#,##的相关运用
求个赞求个赞求个赞求个赞 谢谢

先赞后看好习惯 打字不容易,这都是很用心做的,希望得到支持你 大家的点赞和支持对于我来说是一种非常重要的动力 看完之后别忘记关注我哦!️️️
本章节对于初学者来说,是比较简单的,而且我个人认为也非常的有趣,我相信大部分的小伙伴都可以理解

强烈建议本篇收藏后食用~

预定义符号的介绍

一下这些符号是不需要#define来进行定义的,是本身就定义好我们可以直接使用的符号。

__FILE__;进行编译的源文件
__LINE__;文件当前的行号
__DATE__;文件被编译的日期
__TIME__;文件被编译的时间
__STDC__;如果编译器遵循ANSI C,则其值为1,否则未定义
__FUNCTION__;当前运行的函数

我们可以输入程序打印出来看看

int main()
{
printf("%s\n", __FILE__);
printf("%d\n", __LINE__);
printf("%s\n", __DATE__);
printf("%s\n", __TIME__);
printf("%s\n", __FUNCTION__);
return 0;
}

我们可以依次看到,文件的位置,当前的行数,编译的日期,时间,当前的函数
有了这些,我们就可以在写代码的同时写日志了

int main()
{
int i = 0;
FILE* pf = fopen("log.txt", "a+");//追加的形式输入
if (pf == NULL)//判断文件是否打开成功
{
perror("fopen\n");
return 1;
}
for (i = 0; i < 10; i++)
{
fprintf(pf, "%s %d %s %s %d\n", __FILE__, __LINE__, __DATE__, __TIME__, i);
//将我们的写代码时的信息存入log.txt这个文件里面
}
return 0;
}


这样,我们就可以把我们想要记录的信息存放到文件中了

#define的使用

#define定义符号

只要学过程序环境以及编译过程的伙伴,对#define所执行的方式都不陌生
#define所定义的符号,在预处理过程中就会被直接替换到函数里面。

#define M 1000;
#define reg register
#define do_forever for(;;)
int main()
{
reg int num = 0;
do_forever;//替换,变成了死循环了
int m = M;///在预处理过程,M已经被替换成1000了
printf("%d\n",m);
return 0;
}

#define因为是直接替换,所以利用#define来定义常量在实现一些游戏程序的时候是很方便的,比如,我们写一个三子棋,(或者扫雷),我们需要定义二维数组,此时,我们可以直接定义char board[3][3]={0};,如果这样创建数组,以后每一次我们使用这个数组定义新函数的时候,我们都要输入这个3和3,如果有一天我想要把三子棋改成五子棋,那么我们修改的工作量就会比较大。
因此,我们可以使用#define语句来定义常量

#define ROW 3
#define COL 3
int main()
{
char board[ROW][COL]={0};
//.....实现过程
return 0;
}

这样我们以后修改的时候,只需要修改#define时候的数字就可以了

#define定义宏

我们先来看一个简单的用法

#define SQUARE(X) X*X
int main()
{
int a=3;
int ret=SQUARE(a);
printf("%d",a);
return 0;
}

这样我们得到的结果是9,这种用法其实也是直接替换,将X*X替换成SQUARE(X),所以答案是9

注意:#define定义的宏是直接的替换,不会做任何的运算,下面我们来看两个例子

#define SQUARE(X) X*X
#define DOUBLE(X) (X)+(X)
int main()
{
printf("%d\n", SQUARE(3+1));
//注意:宏是直接完全替换的,而不会去搞计算
//上面这一句等同于下面这一句--完全的直接的替换
printf("%d\n", 3 + 1 * 3 + 1);
//所以结果是7
//替换是在预处理阶段就完成的,而计算结果是在运行的时候
//想要得到16,我们可以这样写
//#define SQUARE(X) ((X)*(X))
//这样写才是最严谨的
//写宏的的时候,括号很重要,但是有时候括号也会带来问题
printf("%d\n", 10 * DOUBLE(4));//这里是得不到八十的
printf("%d\n", 10 * (4) + (4));
return 0;
}

很多小伙伴都误认为第一题是16,第二题是80,通过我写的注释我们可以得知,这个替换是直接进行的,不会进行运算
因此第一题变成了3+1*3+1,结果当然不是16,第二题同理。

以下也是一种用法

#define MAX(X,Y) ((X)>(Y)?(X):(Y))
int main()
{
int max = MAX(101, 100);
//跟函数传参完全不同,这里是替换进去
return 0;
} //注意:宏参数和#define定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归
// 当预处理器搜索#define定义的符号的时候,字符串常量的内容不被搜索

#undef的使用

作用:取消定义

#define M 100
int main()
{
int a = M;
#undef M
//取消定义M
printf("%d\n", M);//这里就编译不出来了
return 0;
}

条件编译

条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的
因为我们有条件编译指令
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译

基本格式#(条件编译类型)...代码...#endif
每一种类型的条件编译后面的要跟上一个#endif,表示条件编译的范围

#ifdef和#undef

#ifdef:判断是否被定义
#ifndef:判断是否不被定义

我们看一段代码就明白了

#define PRINT
int main()
{
#ifdef PRINT//如果PRINT被定义了,才会编译,否则不会
printf("hehe\n");//所以这里是会编译的
#endif
//如果ifdef为假,在预处理阶段就删掉了
#ifndef HEHE
printf("hehe\n");//因为ifndef没有被定义,所以此语句会被编译
#endif
return 0;
}

同样,以下这种写法和上面这种是等价的

#if defined(TEST)//这种跟第一种是一样的
printf("test2\n");
#endif
#if !defined(HEHE)
printf("hehe2\n");
#endif
return 0;
}

#if,#elif,#else的使用

这些语句的运用和我们在语句中的if,else if ,else是一样的,原理非常简单,我们看一段代码就明白了

//常见的条件编译指令
int main()
{
#if 0//如果#if后面的表达式为真,就编译
printf("hehe\n");
#endif
return 0;
}
//多个分支的条件编译
int main()
{
#if 1==1
printf("hehe\n");
#elif 1==2
printf("haha\n");
#else
printf("heihei\n");
#endif
return 0;
}

#符号的使用

我们先来看以下这个场景

int main()
{
int a=10;
int b=20;
int c=30;
//现在想要打印三句话
printf("the value of a is %d\n",a);
printf("the value of b is %d\n",b);
printf("the value of c is %d\n",c);
//这样写的话我们的the value of...is这些语句就重复写了三次
return 0;
}

这样写的话我们的the value
of…is这些语句就重复写了三次,那么有没有什么更简单的方法?这时候,很多小伙伴都会想,我们可以使用函数,传参不就好了?但事实上操作起来,函数实现是非常麻烦的,因为the
value of后面这个字符是会变的,不相信的小伙伴可以使用函数做一些尝试。 因此这个时候我们就可以利用#这个操作符来帮助我们完成

#define PRINT(X) printf("the value of "#X" is %d\n",X);
// ^此#非前面的#
// ^此处如果没有#,就会被替换,代码就会出错了
//但是如果加上#,就不是替换了,#X就会变成X的内容所对应的字符串
int main()
{
int a = 10;
PRINT(a);//这一句跟下面这一句是完全等价的
printf("the value of ""a"" is %d\n", a);
int b = 20;
PRINT(b);
printf("the value of ""b"" is %d\n", b);
int c = 30;
PRINT(c);
printf("the value of ""c"" is %d\n", c);
return 0;
}

通过这种方式,我们就可以只是用三个PRINT()就可以完成我们的任务了
注意:定义宏的时候,两个字符串中间的X如果不加上#,就会被直接替换,这样的话代码是会出问题的
但是,如果我们加上#,就不是直接替换了,#X就会变成X的内容所对应的字符串

这样,我们就能很好的实现我们的功能
如果有不同类型的数据,我们还可以稍微优化一下我们的代码

//我们还可以把上面的代码稍微优化一下
//#X这个#只能在宏上面用
#define PRINT(X,FORMAT) printf("the value of "#X" is "FORMAT"\n",X)
int main()
{
int a = 10;
PRINT(a, "%d");
int b = 20;
PRINT(b, "%d");
int c = 30;
PRINT(c, "%d");
float f = 5.5f;
PRINT(f, "%f");
return 0;
}

注意,#X只能在宏上使用

##符号的使用

个人认为,这个符号完成的算是个比较有趣的功能
它可以把两个符号连成一个符号

#define CAT(X,Y) X##Y
//可以把两个符号连成一个符号
int main()
{
int grade9 = 100;
printf("%d\n", CAT(grad, e9));
//这里的CAT是直接把grad和e9连起来,所以打印的当然是100
return 0;
}

尾声

看到这里,相信你对预定义中的与定义符号,#define,以及符号#,##的相关运用已经有了一定认识,如果你感觉这期博客对你有帮助的话,请不要吝啬你们的点赞收藏和关注哦

【预定义】C语言预定义代码(宏、条件编译等)内容介绍【最全的保姆级别教程】的更多相关文章

  1. 预定义宏,C语言预定义的宏详解

    1.预定义宏 对于预定义宏,相信大家并不陌生.为了方便处理一些有用的信息,预处理器定义了一些预处理标识符,也就是预定义宏.预定义宏的名称都是以"__"(两条下划线)开头和结尾的,如 ...

  2. visual c++中预定义的宏

    一.主要目标 (由于visual studio通常包含很多开发环境,通常将其中c/c++的ide称为visual c++ 20xx) 整理下visual c++ 2010下预定义的宏.做一下备忘和了解 ...

  3. VS2013 预定义的宏

    Visual Studio 2013 预定义的宏 https://msdn.microsoft.com/zh-cn/library/b0084kay(v=vs.120).aspx 列出预定义的 ANS ...

  4. 2019-8-31-dotnet-新项目格式与对应框架预定义的宏

    title author date CreateTime categories dotnet 新项目格式与对应框架预定义的宏 lindexi 2019-08-31 16:55:58 +0800 201 ...

  5. dotnet 新项目格式与对应框架预定义的宏

    在 sdk style 的项目格式支持使用多框架开发,此时需要在代码里面通过宏判断,在编译的时候执行不同的代码.本文告诉大家在框架里面对应的预定义的条件编译符有哪些 在让一个 csproj 项目指定多 ...

  6. gcc中预定义的宏__GNUC__

    转载:gcc中预定义的宏__GNUC__ - Cccarl - 博客园 (cnblogs.com) 今天在看Linux系统编程这本书的代码的时候看到了__GNUC__,不太清楚这个宏所以去查了一下,以 ...

  7. C标准中一些预定义的宏

    C标准中指定了一些预定义的宏,对于编程经常会用到.下面这个表中就是一些常常用到的预定义宏. 宏(双下滑线) 意义 __DATE__ 进行预处理的日期(“Mmm dd yyyy”形式的字符串文字) __ ...

  8. C标准中一些预定义的宏,如__FILE__,__func__等

    C标准中一些预定义的宏 C标准中指定了一些预定义的宏,对于编程经常会用到.下面这个表中就是一些常常用到的预定义宏. 宏 意义 __DATE__ 进行预处理的日期(“Mmm dd yyyy”形式的字符串 ...

  9. ARM编译器中预定义的宏

    arm系列目前支持三大主流的工具链,realview的armcc,iar ewarm的iccarm,gnu的gcc,编译器在编译的时候会预定义一些宏,这些宏在工程中起到不可或缺的作用. 例如 /* d ...

  10. C语言:预定义的宏

    预定义宏就是已经预先定义好的宏,我们可以直接使用,无需再重新定义.ANSI C 规定了以下几个预定义宏,它们在各个编译器下都可以使用: __LINE__:表示当前源代码的行号: __FILE__:表示 ...

随机推荐

  1. asp.net 程序员常用工具

    1  写作工具: Typora 2. 远程工具 ToDesk 3. 思维整理工具 XimindZen 4. 数据库客户端工具 Navicat Premium 15 5. 录音工具 楼月免费MP3录音软 ...

  2. zzuli 1908

    ***做的时候判断当前位置为.的上下左右是否为*,如果全是改位置就改为*,如果四周中有为.,再DFS一下,其实就相当于把判断化为更小的子问题*** #include<iostream> # ...

  3. 每天学五分钟 Liunx 100 | 存储篇:磁盘分区

    这一节主要介绍 Liunx 是怎么用磁盘的. 磁盘分区 在 Liunx 中一切皆文件,磁盘在 Liunx 中也是文件,包括 /dev/hd[a-d](以 IDE 为接口) 和 /dev/sd[a-p] ...

  4. 项目使用 GlobalExceptionHandler 与 @RestControllerAdvice自定义异常 二

    未经博主允许不得转载: 自定义异常,不仅需要定义符合自己业务的异常状态码,也需要定义自己项目中的异常封装.记录下自己手敲代码中的异常封装: 1.定义一个枚举类,枚举类中定义状态码及状态码描述,再定义一 ...

  5. Nacos源码 (6) Grpc概述与Nacos集成

    Nacos 2.x版本增加了GRPC服务接口和客户端,极大的提升了Nacos的性能,本文将简单介绍grpc-java的使用方式以及Nacos中集成GRPC的方式. grpc-java GRPC是goo ...

  6. Scan Synthesis Practice

    不同上升沿触发器如何进行scan chain DFT实例 Synopsys 工具文档 Mentor DFT脚本 add_clocks 0 clk - 0表示上升沿 Synopsys DFT脚本 更改n ...

  7. Go-错误-error

  8. Linux-进程动态监控-top

  9. nginx 进行目录浏览的简单配置

    1. 公司网络安全不让用vsftpd的匿名网络访问了, 没办法 只能够使用 nginx 通过http协议来处理. 2. 最简单的办法就是另外开一个nginx进程简单设置一下nginx的配置文件 wor ...

  10. 银河麒麟安装LLDB的方法以及调试 dump 文件 (未完成)

    今天同事要进行 lldb进行调试dotnet的bug 本来在x86 上面进行相应的处理 但是发现报错. 没办法 正好有一台借来的arm服务器就搞了一下. 简单记录一下安装方法 1. 安装 apt的so ...