c 宏应用举例

1.概要

#include <iostream>

//变量可以直接使用

#define fun() a =100;

//用变量计算可以

#define fun2(a) a*2;

//用变量替换可以

#define fun3(a) d[a] = a;

//##链接的作用,一般用于链接变量名

#define fun4(type,name) type name##_s = 4;

//#的作用是转换成字符串

#define fun5(a) #a;

2.代码

#include <iostream>
//变量可以直接使用
#define fun() a =100;
//用变量计算可以
#define fun2(a) a*2;
//用变量替换可以
#define fun3(a) d[a] = a;
//##链接的作用,一般用于链接变量名
#define fun4(type,name) type name##_s = 4;
//#的作用是转换成字符串
#define fun5(a) #a;

int main()
{
    int a;
    fun();
    std::cout << a << "\n";

    int b = 5;
    int c = fun2(b);//b*2
    std::cout << c << "\n";

    int d[3];
    for (size_t i = 0; i < 3; i++)
    {
        fun3(i);// d[i] = i
    }
    for (size_t i = 0; i < 3; i++)
    {
        std::cout << d[i] << "\n";
    }

    fun4(int, ta);//int ta_s = 5;
    std::cout << ta_s << "\n";

    int e = 5;
    const char* f = fun5(e);
    std::cout << f << "\n";
    
    std::cout << "Hello World!\n";
}

3.运行结果

100

10

0

1

2

4

e

Hello World!

4.附加

4.1c 宏的返回值如何确定

在C语言中,宏(macros)本身并不返回值,因为宏是预处理器(preprocessor)在编译前进行文本替换的工具。当你在代码中调用一个宏时,预处理器会简单地将宏名替换为其定义的内容。

但是,我们可以说宏"展开"后的代码可以产生返回值,这取决于宏是如何定义的以及它是如何被使用的。

例如,一个简单的宏可能定义如下:

|---|---------------------------------------------|
| | #define MAX(a, b) ((a) > (b) ? (a) : (b)) |

这个宏用于比较两个值并返回较大的一个。但是,请注意,这并不是宏本身"返回"值,而是宏展开后的代码(即条件运算符表达式)在运行时返回值。

要确定宏"展开"后的代码如何返回值,你需要查看宏的定义并理解它是如何工作的。在上述例子中,MAX宏使用了条件运算符(?:),该运算符在C语言中用于根据条件返回两个值中的一个。

注意:由于宏只是文本替换,所以在使用宏时需要特别小心,以避免意外的副作用或错误。例如,在上面的MAX宏中,如果参数是复杂的表达式或具有副作用的表达式(如自增或自减),则可能会导致问题。为了避免这些问题,通常建议使用括号将宏的参数括起来,如上例所示。

4.2 #的作用

在C和C++编程中,# 符号在预处理器指令中有特殊的作用,但它本身并不是宏的一部分。预处理器指令是以 # 开头的特殊指令,它们在编译器的实际编译过程之前由预处理器处理。

以下是一些常见的以 # 开头的预处理器指令:

  1. #include:用于包含其他文件的内容。

    |---|----------------------------------------|
    | | #include <stdio.h> // 包含标准输入输出头文件 |
    | | #include "myheader.h" // 包含当前项目中的头文件 |

  2. #define:用于定义宏。

    |---|--------------------------------------------------------------------|
    | | #define PI 3.14159 // 定义一个名为PI的宏,其值为3.14159 |
    | | #define MAX(a, b) ((a) > (b) ? (a) : (b)) // 定义一个名为MAX的宏,用于比较两个数 |

  3. #undef:用于取消已定义的宏。

    |---|-------------------------|
    | | #undef PI // 取消PI宏的定义 |

  4. #if#elif#else#endif:用于条件编译。

    |---|--------------|
    | | #if DEBUG |
    | | // 调试代码 |
    | | #else |
    | | // 非调试代码 |
    | | #endif |

  5. #ifdef#ifndef:用于检查某个宏是否已定义。

    |---|------------------------------------|
    | | #ifdef SOME_MACRO |
    | | // 如果SOME_MACRO已定义,则包含此代码 |
    | | #endif |
    | | |
    | | #ifndef SOME_OTHER_MACRO |
    | | // 如果SOME_OTHER_MACRO未定义,则包含此代码 |
    | | #endif |

  6. #line:用于改变预处理器对行号和文件名的跟踪。

    |---|----------------------------------------------------------|
    | | #line 100 "newfile.c" // 将接下来的代码视为在newfile.c文件的第100行开始 |

  7. #error#warning:用于在编译时生成错误或警告消息。

    |---|-----------------------------------------------|
    | | #if SOME_CONDITION_THAT_SHOULD_NOT_HAPPEN |
    | | #error "This condition should not happen!" |
    | | #endif |
    | | |
    | | #warning "This is a warning message." |

  8. #pragma:这是一个特定于编译器的指令,用于提供编译指示,如优化设置、警告控制等。

    |---|-----------------------------------------|
    | | #pragma once // 一些编译器支持此指令来防止头文件被多次包含 |

请注意,虽然 # 符号在预处理器指令中很重要,但它本身并不是C或C++语言的一部分。在C或C++的代码中,你不能直接使用 # 符号(除了作为预处理器指令的一部分)来执行任何操作或定义变量。

4.3 # 在宏表达式内的作用

在宏表达式内部,# 符号具有特殊的作用,当它与 ## 一起使用时,它们是 C/C++ 预处理器的运算符,分别被称为"字符串化"运算符和"标记连接"运算符。

  1. 字符串化运算符 #

    # 运算符用于宏参数时,它会把该参数转换为一个用双引号括起来的字符串。例如:

    |---|-----------------------------------------------------------------------------------------------|
    | | #define TO_STRING(x) #x |
    | | int main() { |
    | | printf("%s\n", TO_STRING(hello)); // 输出 "hello" |
    | | printf("%s = %d\n", TO_STRING(PI), PI); // 假设 PI 是一个宏,输出 "PI = 3.14159"(假设 PI 定义为 3.14159) |
    | | return 0; |
    | | } |

    在这个例子中,TO_STRING(hello) 会被替换为 "hello",而 TO_STRING(PI) 会被替换为 "PI"

  2. 标记连接运算符 ##
    ## 运算符用于连接两个标记(token),通常用于连接宏参数和其他标记以形成新的标记。这在编写可重用和灵活的宏时特别有用。例如:

    |---|-----------------------------------------------------------|
    | | #define CONCAT(x, y) x ## y |
    | | int xy = CONCAT(x, y); // 这将声明一个名为 xy 的变量 |
    | | |
    | | // 或者更复杂的用法,例如与类型一起使用 |
    | | #define TYPED_VAR(type, name) type name |
    | | TYPED_VAR(int, myVar); // 这将声明一个名为 myVar 的 int 类型变量 |
    | | |
    | | // 使用 `##` 可以让类型也是动态的 |
    | | #define TYPED_NAMED_VAR(type, name) type name ## _var |
    | | TYPED_NAMED_VAR(int, my); // 这将声明一个名为 my_var 的 int 类型变量 |

    需要注意的是,## 运算符不能用于连接两个字符串字面量或其他类型的表达式;它只能用于连接标记。

在宏表达式中,### 通常一起使用或单独使用,以提供宏的额外功能。然而,它们的使用需要谨慎,因为不正确的使用可能会导致编译错误或不可预测的行为。

相关推荐
&岁月不待人&21 分钟前
Kotlin by lazy和lateinit的使用及区别
android·开发语言·kotlin
StayInLove24 分钟前
G1垃圾回收器日志详解
java·开发语言
TeYiToKu26 分钟前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
无尽的大道32 分钟前
Java字符串深度解析:String的实现、常量池与性能优化
java·开发语言·性能优化
互联网打工人no133 分钟前
每日一题——第一百二十四题
c语言
爱吃生蚝的于勒36 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~39 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
binishuaio1 小时前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE1 小时前
【Java SE】StringBuffer
java·开发语言
就是有点傻1 小时前
WPF中的依赖属性
开发语言·wpf