预定义

  • 一、预定义符号
  • 二、#define
    • 1.#define定义标识符
    • 2.#define定义宏
    • 3.#define替换规则
    • 4.#和##
      • 4.1 #号
      • 4.2 ##号
  • 三、函数与宏
    • 1.代码长度
    • 2.执行速度
    • 3.操作符的优先级
    • 4.参数类型
    • 5.调试
    • 6.递归
    • 7.带有副作用的参数
  • 四、条件编译

一、预定义符号

预定义符号是C标准定义的宏定义符号
例如 FILE LINE DATE TIME STDC(如果编译器遵循ASCI C就返回1,否则未定义。)

int main()
{printf("%s\n",__FILE__);printf("%d\n",__LINE__);printf("%s\n",__DATE__);printf("%s\n",__TIME__);//printf("%s\n__STDC__);
}

二、#define

1.#define定义标识符

语法:
define name stuff

(而这个stuff可以是不同的类型)

1.#define MAX 100
2.#define reg register(给register起一个短一点的名字)
3.#define arr “hello world.”
我们也可以利用这个来写一个死循环

#define do_forever for(; ;)

当然在写代码的时候会有这样一个疑问,如果要写的stuff过长怎么办?这个时候我们可以分行写,但是除了最后一行外,每一行都要加一个反斜杠(续航符)

#define fad printf("name:%s\tage:%s\t \tele:%s\taddre:%s\t",\name,age\tele,addre)

2.#define定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏 (macro)或者定义宏(define macro)。

宏的申明方式
#define name(parament-list) stuff
其中parament-list是一个由逗号隔开的符号表,有可能会出现在后面的stuff中。
注意:参数列表的左括号必须与name紧邻,不能有空格或者空白,否则会被认为是stuff的一部分。

在定义宏时要合理使用括号,防止运算符的优先级问题

#define SQUARE(x) x*xint main()
{int a = 4;printf("%d", SQUARE(a));return 0;
}


但如果换成SQUARE(4+1)呢

#define SQUARE(x) x*xint main()
{//int a = 4;printf("%d", SQUARE(4+1));return 0;
}


我们可以看到结果是9,那是因为替换方式

#define SQUARE(x) 4+1*4+1

这样就提醒我们在写的时候最好带上括号

#define SQUARE(x) (x)*(x)int main()
{//int a = 4;printf("%d", SQUARE(4+1));return 0;
}


但是这样写任然不够严密,在有些时候还是会出现错误

将上述代码中printf一部分换成
printf(“%f”, 1.0/SQUARE(4+1))


答案显然不对,按上述的替换法方式就变成了
#define SQUARE(x) 1.0/(4+1)*(4+1)
那么这样得出错误的结果也就不奇怪了
为了达到最初的目的,我们应该这样

#define SQUARE(x) ((x)*(x))int main()
{//int a = 4;printf("%f", 1.0/SQUARE(4+1));return 0;
}

当然还有一个特殊的地方就是不要在定义标识符或者定义宏时加上分号(;),这样很容易在使用时出错

#define SQUARE(x) ((x)*(x));int main()
{if(1)int a=SQUARE(4);printf("%d", a);return 0;
}

编译预处理完,替换之后就变成了

if(1)
>int a=((4)*(4));;

if后面没有大括号跟两条语句是不对的

所以我们在定义标识符或者定义宏时不要加上分号,避免上述错误。

3.#define替换规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。

1.在调用宏时,首先多参数进行检查,看看是否包括任何由#define定义的符号,如果是他们首先被替换。
2.替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的只所替换。
3.最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
在这里有几个需要注意的地方
1.宏参数和#define定义中可以出现其他#define定义的符号,但是对于宏,不能出现递归。
2.当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

4.#和##

4.1 #号

如何把参数插入到字符串中?
首先我们要知道字符串是有自动连接的功能的的特点的。
比如:

int main()
{printf("hello""world");return 0;
}


而#号加在宏参数前时,可以将这个参数转化为字符串。

int main()
{int a = 10;printf("the value of a is %d\n", a);int b = 20;printf("the value of b is %d\n", b);float c = 30.0f;printf("the value of c is %f\n", c);return 0;
}

可以看出只有a,b,c和%d,%f不同,这个时候可以用宏来写

#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");float c = 30.0f;PRINT(c, "%f");return 0;
}

4.2 ##号

##的作用是将两个字符合成一个字符。

#define A(x,y) printf("%d\n",x##y)
int main()
{int ab = 20;A(a, b);return 0;
}

三、函数与宏

1.代码长度

宏:每次使用时在编译的预处理阶段宏的代码就会插入到程序中,如果宏的代码量很大的话,那么整体的代码量也会大大增加。
函数:函数的代码只有一份,每次调用都从这里调用。

2.执行速度

宏比函数在速度和规模上都要更胜一筹。

3.操作符的优先级

宏可能会带来操作符的优先级的问题。
比如上面已经提过的:

#define SQUARE(x) x*xint main()
{//int a = 4;printf("%d", SQUARE(4+1));return 0;
}

执行答案是9,但是我们的目的是算(4+1)*(4+1)

4.参数类型

宏:参数与类型无关,只要参数合法就可以使用
函数:只能在类型合适的表达式上使用,即参数必须声明为特定的类型。

当然,因为函数与参数无关也就不够严谨。
比如,对于比较大小,宏可以比较不同类型的大小(整形,浮点型等等)

#define MAX(x,y) ((x)>(y)?(x):(y))

5.调试

宏:由于宏是在编译预处理直接替换的,所以无法调试
函数:可以调试在vs2019下,按F10是逐过程,F11是逐语句(可以进入函数内部调试)。

6.递归

宏是不能递归的
函数可以,比如用递归写斐波那契数列

int fib(int n)
{if (n == 0 || n == 1)return n;elsereturn fib(n - 1) + fib(n - 2);
}

7.带有副作用的参数

宏:宏参数可能被替换到多个位置,更容易出现问题
函数:只在传参时计算一次,结果不容易出错
比如:

#define MAX(x,y)  printf("%d",((x)>(y)?(x):(y)))
int main()
{int a = 1;int b = 2;MAX(++a, ++b);return 0;

我们这里想要比较a++和b++ 的大小,也就是2和3的大小
我们想要的答案应该是3才对
但事实上

这是因为经过替换后
#define MAX(x,y) printf(“%d”,((++a)>(++b)?(++a):(++b)))
所以答案为4

这也是提醒我们在使用宏时不要使用带有副作用的参数

四、条件编译

在编译一个程序的时候,可以用条件编译来选择将一条语句编译或者放弃。

#include <stdio.h>
#define __DEBUG__
int main()
{int i = 0;int arr[10] = { 0 };for (i = 0; i < 10; i++){arr[i] = i;
#ifdef __DEBUG__//判断是否已经定义,如果定义了就执行下面的语句printf("%d\n", arr[i]);
#endif }return 0;
}

1.
#if 常量表达式
//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif
2.多个分支的条件编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
3.判断是否被定义
#if defined(symbol) //等于       #ifdef symbol
#if !defined(symbol)//       #ifndef symbol4.嵌套指令(与if嵌套相似)
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif

【C语言进阶】预定义详解相关推荐

  1. 【原创-更新完毕】|日历拼图游戏的解决方案(C语言-进阶应用)-详解连载2

    [原创]|日历拼图游戏的解决方案(C语言-进阶应用)-详解连载1_zhuyi8120的博客-CSDN博客 [原创]|日历拼图游戏的解决方案(C语言-进阶应用)-详解连载3_zhuyi8120的博客-C ...

  2. 【原创-更新完毕】|日历拼图游戏的解决方案(C语言-进阶应用)-详解连载1

    [原创]|日历拼图游戏的解决方案(C语言-进阶应用)-详解连载2_zhuyi8120的博客-CSDN博客 [原创]|日历拼图游戏的解决方案(C语言-进阶应用)-详解连载3_zhuyi8120的博客-C ...

  3. python 拼音库_python有没有拼音库python进阶之socket详解

    Socket的英文原义是"孔"或"插座".作为BSD UNIX的进程通信机制,通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句 ...

  4. c语言菜单选择如何用字符形式,【创客天地】计算机二级C语言、VB考试详解分析...

    原标题:[创客天地]计算机二级C语言.VB考试详解分析 01 马上就要迎来计算机二级考试了,你准备好了吗?今天助手君准备了一点C语言干货,希望对即将考试的你有所帮助.(上期刚刚推了office,有需要 ...

  5. 攻防世界web进阶区ics-05详解

    攻防世界web进阶区ics-05详解 题目 解法 preg_replace ctype_alnum strpos X-Forwarded-For 题目 我们只能点击一个地方 解法 御剑扫描有一个css ...

  6. 程序人生 | C语言字节对齐问题详解 - 对齐/字节序/位序/网络序等(上)

    本文首发于 2014-07-21 15:32:28 1. 引言 考虑下面的结构体定义: typedef struct{char c1;short s; char c2; int i; }T_FOO; ...

  7. C语言字节对齐问题详解

    转载原文连接:https://www.cnblogs.com/clover-toeic/p/3853132.html C语言字节对齐问题详解 引言 考虑下面的结构体定义: 1 typedef stru ...

  8. 嵌入式c语言为什么变量定义在前面,嵌入式C语言数据类型和变量详解

    原标题:嵌入式C语言数据类型和变量详解 一般来讲,标准的C语言类型在嵌入式编译器中是合法的.但由于嵌入式控制器的受限环境.嵌入式c语言的变量和数据类型具有新的特征,这些特征体现在如下方面. 嵌入式C语 ...

  9. 用数据结构c语言写成绩排序,C语言数据结构 快速排序实例详解

    C语言数据结构 快速排序实例详解 一.快速排序简介 快速排序采用分治的思想,第一趟先将一串数字分为两部分,第一部分的数值都比第二部分要小,然后按照这种方法,依次对两边的数据进行排序. 二.代码实现 # ...

  10. C语言解决约瑟夫问题详解的代码

    C语言解决约瑟夫问题详解的代码 参考文章: (1)C语言解决约瑟夫问题详解的代码 (2)https://www.cnblogs.com/odsxe/p/10791049.html (3)https:/ ...

最新文章

  1. 企业里实现代码自动部署、回滚的解决方案——Caphub
  2. 机器学习内卷化:博士数量激增,本硕毕业生有点慌 | reddit热议
  3. Hibernate 连接池的三种配置方式
  4. Linux疑难杂症解决方案100篇(十二)-ubuntu中安装ftp服务器
  5. Python中的正则表达式(特征匹配)
  6. Java多线程(十)之ReentrantReadWriteLock深入分析
  7. C++内存分配与对象构造的分离
  8. P1373-小a和uim之大逃离【dp】
  9. 在使用静态构造函数的时候应该注意几点
  10. 【CCCC】L3-003 社交集群 (30分),并查集模板,map排序
  11. Spring Batch流程介绍
  12. 远程监督在关系抽取中的应用
  13. podspec文件介绍
  14. django -orm操作总结
  15. 电脑qq音乐显示无法代理服务器,电脑QQ音乐软件无法登录如何解决
  16. matlab画散点图
  17. Android 退出登录功能
  18. nexus开机启动设置
  19. Unity3D中2D图片动画进行帧动画播放
  20. React学习资源汇总

热门文章

  1. 双硬盘双系统win10+manjaro-kde搭建
  2. 初识c语言day07(五子棋)
  3. [Excel知识技能] Excel数据类型
  4. html读取指定excel,怎样用html读取excel数据
  5. java人机猜拳_Java实现人机猜拳小游戏
  6. CDH大数据平台搭建之HADOOP分布式集群搭建
  7. 【操作系统】30天自制操作系统--(26)LDT与库
  8. android绘制立方体带坐标,Android: 直接在bitmap上绘制一个立方体
  9. 有些路,只能一个人走。
  10. java生成docx文件、pdf文件、docx转pdf、docx转图片 pdf转图片工具