📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨
📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】
📢:文章若有幸对你有帮助,可点赞 👍 收藏 ⭐不迷路🙉
📢:内容若有错误,敬请留言 📝指正!原创文,转载请注明出处
引言:为什么使用宏定义?
✨C语言中,一般都不会直接使用常量,而是先将其定义为一个宏,然后在程序中使用该宏名,进而调用了这个常量。
✨修改方便。等我们需要修改这个常数时候,只需要在宏定义出修改一次即可。而不用到代码中到处去寻找,看哪里都用过这个常数。
一、宏定义的定义
在C语言中,宏定义是一种预处理指令,用于在编译之前将标识符替换为特定的值或代码片段。下面是几种常见的宏定义方式:
1. 简单宏定义
#define 宏名 值
示例:
#define PI 3.14159
#define N (321)
定义N等于321。N在使用过程中会被321完全代替。注意不需要加“;”。其次,赋值的数值最好加上小括号。
2. 参数化宏定义
#define 宏名(参数列表) 值或代码片段
示例:
#define MAX(a, b) ((a) > (b) ? (a) : (b))
3. 字符串化宏定义
#define STRINGIFY(x) #x
示例:
printf("宏名: %s\n", STRINGIFY(MAX));
// 输出: 宏名: MAX
4. 连接宏定义
#define CONCAT(a, b) a##b
示例:
int CONCAT(a, b) = 10;
// 相当于 int ab = 10;
5. 条件宏定义
#ifdef 宏名
#ifndef 宏名
#endif
示例:
#define DEBUG
#ifdef DEBUG
printf("调试模式\n");
#endif
在这段代码中,#define DEBUG 表示定义了一个名为 DEBUG 的宏。#ifdef DEBUG 是一个条件编译指令,用于检查 DEBUG 宏是否已经定义。如果 DEBUG 宏已经定义,那么 printf(“调试模式\n”); 这行代码将会被编译,否则将被忽略。
在使用条件编译时,通过定义或取消定义宏,我们可以在编译时根据需要选择性地包含或排除代码。这对于在调试程序时输出额外的信息非常有用,以及在发布版本时禁用调试代码以提高性能。
如果在包含上述代码的文件的开头定义了 #define DEBUG
,那么在编译时将会输出 “调试模式”。反之,如果没有定义 DEBUG
宏,那么这行代码将被编译器忽略,不会产生任何输出。
注意,#ifdef
是一个条件编译指令的开头,#endif
是条件编译指令的结尾,之间的代码将根据宏的定义情况进行编译或忽略。
因此条件宏的使用跟if else的选择结构语法类似,不同的是#ifndef或是#ifdef 后面跟的必须是一个定义了或是没有定义的宏;其次,它们还可以在函数内外都可以使用。
if defined
#ifdef的使用和#if defined()的用法一致
#ifndef又和#if !defined()的用法一致
7.特殊条件的宏定义
当 #if 后的条件为0时,对应的代码段会被预处理器忽略,相当于被注释掉。
当 #if 后的条件为1时,对应的代码段会被预处理器包含,相当于被保留下来用于编译和执行。
8.宏定义嵌套宏定义
#define M (N+5)
#define S(r) (PI*r*r)
宏定义的分类:对象宏和函数宏(函数宏顾名思义,就是行为类似函数)
例如:
#define a 3.14
#define N (a*a)
9.宏定义没有值
宏定义可以没有具体的值,这种情况下宏名只作为一个标识符存在。在定义宏时,可以省略值部分,也就是不使用等号(=)或表达式。这样的宏定义通常用于条件编译或特定的标记。
例如,以下是一个没有具体值的宏定义的示例:
#define DEBUG_MODE
在上面的示例中,DEBUG_MODE 是一个宏名,它没有与之对应的具体值。这种宏定义通常用于在代码中插入调试相关的语句或区块。在编译过程中,可以通过检查宏名的定义与否来选择性地编译或执行相应的代码片段。
需要注意的是,没有具体值的宏定义在替换过程中,宏名会被直接替换为空。因此,如果在代码中使用该宏名,它将被替换为空,可能会导致语法错误或意想不到的结果。因此,在使用没有具体值的宏定义时需要谨慎。
二、宏定义需要注意的问题
📢1.宏定义一般在函数的外面。
📢2.宏定义必须要先定义,再使用宏。如果先使用就会编译报错。
📢3.宏定义中宏名一般使用大写,便于区分变量名。不是语法规定的,是一般约定俗成的。
三、宏定义中为什么要加do和while(0)?
目的:在宏定义中添加
do{...}while(0)
结构的目的是为了将多个语句作为宏的一个整体,使其在代码中的使用更加灵活和安全。
宏展开后的代码会替代宏的调用部分,如果不使用do{...}while(0)
结构,而是直接用花括号{...}
包裹展开的代码,可能会导致在某些场景下产生语法错误或意想不到的行为。
下面是一些理由解释为什么要使用do{...}while(0)
结构:
1.语法问题:
do{...}while(0)
结构保证了展开的代码作为一个完整的语句块存在。如果不添加这样的结构,并且将展开的代码用花括号包裹,可能会在某些情况下引起语法错误。使用do{...}while(0)
结构能够确保展开的代码符合语法要求。
2.宏的调用位置:在一些特定的上下文中,宏的调用可能需要像普通的语句一样出现。例如,将宏用作函数调用的参数,或者将多个宏调用放在一个复合语句的条件部分。使用
do{...}while(0)
结构可以让宏的展开代码在这些情况下保持一致的行为。
3.语句的结束符号:由于宏展开后生成的代码可能包含多个语句,因此在每个语句的末尾都需要添加结束符号。使用
do{...}while(0)
结构可以确保在每个语句之后都添加了;
分号,以避免语法错误。
总之,使用do{...}while(0)
结构可以确保宏展开后的代码在各种上下文中具有一致的行为,避免语法错误,并且能够被作为单个语句来使用。这是一种常见的宏定义的惯用写法。
四、宏定义和typedef的相同作用
以下两种具有相同的作用
typedef unsigned short U16;
#define U16 unsigned short
编程规范:应尽可能使用函数代替宏
📢:常量建议使用 const 定义代替宏。
说明:宏对比函数,有一些明显的缺点:
1、宏缺乏类型检查,不如函数调用检查严格。
2、宏展开可能会产生意想不到的副作用,如#define SQUARE(a) (a) * (a)这样的定义,如果是 SQUARE(i++),
就会导致 i 被加两次;如果是函数调用 double square(double a) {reurn a * a ;}则不会有此副作用。
3、以宏形式写的代码难以调试难以打断点,不利于定位问题。
4、宏如果调用的很多,会造成代码空间的浪费,不如函数空间效率高。文章来源:https://www.toymoban.com/news/detail-657820.html
文章来源地址https://www.toymoban.com/news/detail-657820.html
到了这里,关于【C语言基础】宏定义的用法详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!