当前位置: 首页 > news >正文

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*2std::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 类型变量

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

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

http://www.lryc.cn/news/369508.html

相关文章:

  • 微信公众号(公众平台) 和 微信开放平台的scope的差异
  • 基于pytorch实现的DenseUnet医学图像分割(腹部多脏器)
  • 富格林:正规策划实现安全做单
  • 02. 异常捕捉和处理
  • Oracle和mysql中插入时间字段
  • 注册小程序
  • 【YOLOv8改进[CONV]】使用MSBlock二次创新C2f模块实现轻量化 + 含全部代码和详细修改方式 + 手撕结构图 + 轻量化 + 涨点
  • three.js使用环境贴图或者加载hdr图
  • GPT-4o多模态大模型的架构设计
  • Facebook:社交世界的引领者
  • qt 加载字体 c++
  • Linux ldd和ldconfig
  • Python 学习flask创建项目
  • .NET集成DeveloperSharp实现图片的裁剪、缩放、与加水印
  • 阿里发布最强开源大模型通义千问Qwen2,国产最好用的LLM
  • 探索风电机组:关键软件工具全解析
  • HOW - CSS 常见效果实现
  • EI/CPCI/Scopus会议论文是啥?
  • 【递归、搜索与回溯】穷举vs暴搜vs深搜vs回溯vs剪枝
  • celery-redbeat方案(动态定时任务、异步任务)
  • js解析成语法树以及还原
  • 基于python可伸缩JSON格式列表实现
  • h5相机功能
  • IDEA | 安装通义灵码插件,开启智能编码旅程
  • 技术人员如何克服在使用行列视(RCV)过程中遇到的挑战?
  • 手把手教你安装 Vivado2019.2(附安装包)
  • Sql-labs的第一关
  • 10_1 Linunx Web服务管理
  • 苹果WWDC 2024:十三大亮点公布,一切都有关AI|TodayAI
  • Nginx访问日志