C++ 编程基础(1)宏定义

news2024/11/6 6:06:43

文章目录

  • 一、宏定义
    • 1.1、宏概念
    • 1.2、宏作用阶段
    • 1.3、宏函数
    • 1.4、宏函数与普通函数的区别
  • 二、宏里面特殊字符的作用
    • 2.1、#(stringizing)字符串化操作符
    • 2.2、@#(charizing)字符化操作符
    • 2.3、##(token-pasting)符号链接操作符
    • 2.4、/行继续操作符
  • 三、可变参数宏与__VA_ARGS__ 宏
    • 3.1、__VA_ARGS__ 宏
    • 3.2、可变参数名称
    • 3.3、无参传入情况
  • 四、条件宏

前言:

在C和C++编程语言中,宏是一种强大的预处理工具,它允许程序员在编译之前对源代码进行文本替换或条件编译。宏通过预处理器指令定义,并在预处理阶段被展开到源代码中。

一、宏定义

1.1、宏概念

宏定义简单点说就是查找替换(文本替换),C中的宏分为两类,对象宏和函数宏。宏定义的一般形式为:

#define  宏名  字符串

注意:

  • 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换
  • 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束

1.2、宏作用阶段

编译一个C语言程序的第一步骤就是预处理阶段,这一阶段就是宏发挥作用的阶段。C预处理器在源代码编译之前对其进行一些文本性质的操作,主要任务包括删除注释、插入被#include进来的文件内容、定义和替换由#define 定义的符号以及确定代码部分内容是否根据条件编译(#if )来进行编译。”文本性质”的操作,就是指只是简单粗暴的进行文本替换,而不考虑其中任何的语义内容。

1.3、宏函数

配合#、##一起使用函数宏可以节省大量的工作量,例如:获取环境变量的一系列操作,可以通过宏函数生成实际的函数定义,如下:

#define ENV_MAX_LEN (256)
#define DEFINE_FUNC_ENV(ENV, default)                                                                  \
    const char *CFG_Get##ENV()                                                                         \
    {                                                                                                  \
        do {                                                                                           \
            static char ENV##Var[ENV_MAX_LEN] = {'\0'};                                                \
            char *value = getenv(#ENV);                                                                \
            if (IS_NULL(value)) {                                                                      \
                ASSERTEX_NO_RET(IS_NOT_NULL(value), LOG_LEVEL_WARN, "env (%s) not set, use value(%s)", \
                     #ENV, default);                                                                   \
                return default;                                                                        \
            }                                                                                          \
            errno_t retCode = strncpy_s(ENV##Var, ENV_MAX_LEN, value, strlen(value));                  \
            ASSERTEX((retCode == EOK), "", LOG_LEVEL_ERR, "strncpy_s(%s) failed(%d).", value, retCode); \
            return ENV##Var;                                                                           \
        } while (0);                                                                                   \
    }


// 定义函数,从环境变量中获取服务基本配置
DEFINE_FUNC_ENV(APPID, NULL)
DEFINE_FUNC_ENV(APPNAME, "")

// 定义函数,从环境变量中获取服务的位置信息
DEFINE_FUNC_ENV(PODNAME, "defaultPod")
DEFINE_FUNC_ENV(NODE_ID, "defaultNode")
DEFINE_FUNC_ENV(NodeID, "defaultNode")
DEFINE_FUNC_ENV(ContainerID, "defaultContainerID")
DEFINE_FUNC_ENV(HOSTING_SERVER_IP, "127.0.0.1")

1.4、宏函数与普通函数的区别

宏函数与普通函数主要区别如下:

  • 宏函数的本质是进行文本替换,并没有实际函数调用(压栈、退栈)带来的开销
  • 宏函数不会检查参数的类型,普通的函数调用会检查参数类型

二、宏里面特殊字符的作用

2.1、#(stringizing)字符串化操作符

将宏定义中的传入的参数名转换成用一对双引号括起来的参数名字符串,例如:

#define ASSERTEX_NO_RET_EQ(a, b) \
    ASSERTEX_NO_RET((a == b), LOG_LEVEL_ERR, "expect " #a "(%d) equals " #b "(%d) failed", a, b)

注意:

  • 其只能用于有传入参数的宏定义中,且在宏定义体中使用#时,#必须位于宏参数前面
  • 忽略传入参数名前后的空格
#define toStr(str) #str
char *str = toStr(  123  );  // 扩展成 char *str = "123"

2.2、@#(charizing)字符化操作符

将传的单字符参数名转换成字符,以一对单引用括起来,如下:

#define makechar(x) #@x
char ch = makechar(b);  // 扩展成 char ch = 'b'

2.3、##(token-pasting)符号链接操作符

将多个宏参数名拼接成一个参数名,是一种分割链接方式,它的作用是先分隔,然后进行强制连接,如下:

#define exampleNum(n) num##n
int num9 = 9;
int num = exampleNum(9);  // 扩展成 int num = num9;


#define TYPE(type, name) type name##_##type##_type
TYPE(int, a); // 扩展成 int a_int_type;

注意:当用##连接形参时,##前后的空格可有可无。如:#define exampleNum(n) num ## n 相当于 #define exampleNum(n) num##n

2.4、/行继续操作符

当定义的宏不能用一行表达完整时,可以用"/"表示下一行继续此宏的定义

// 判断返回值,并打印信息和返回错误码
#define ASSERTEX(expr, ret, level, fmt, va...) \
    do {                                       \
        if (!(expr)) {                         \
            STD_LOG(level, fmt, ##va);         \
            return ret;                        \
        }                                      \
    } while (0)

三、可变参数宏与__VA_ARGS__ 宏

3.1、VA_ARGS

在GNU C中,从C99开始,宏可以接受可变数目的参数,就象可变参数函数一样。和函数一样,宏也用三个点…来表示可变参数。VA_ARGS 宏用来表示可变参数的内容,简单来说就是将左边宏中 … 的内容原样抄写在右边__VA_ARGS__ 所在的位置。如下:

#include <stdio.h>
#define debug(...) printf(__VA_ARGS__)
int main(void)
{
    int year = 2018debug("this year is %d\n", year);  //效果同printf("this year is %d\n", year);
}

3.2、可变参数名称

通过一些语法,你可以给可变参数起一个名字,而不是使用__VA_ARGS__ ,如下例中的args:

#include <stdio.h>
#define debug(format, args...) printf(format, args)
int main(void)
{
    int year = 2018debug("this year is %d\n", year);  //效果同printf("this year is %d\n", year);
}

3.3、无参传入情况

可变参数宏中的可变参数必须至少有一个参数传入,不然会报错,为了解决这个问题,需要一个特殊的“##”操作,如果可变参数被忽略或为空,“##”操作将使预处理器(preprocessor)去除掉它前面的那个逗号。如下例所示:

#include <stdio.h>
#define debug(format, args...) printf(format, ##args)
int main(void)
{
    int year = 2018debug("hello, world");  //只有format参数,没有args可变参数
}

四、条件宏

下面是常用的条件宏,如下:

宏名功能
#ifdef如果宏已经定义,则编译下面代码
#ifndef如果宏没有定义,则编译下面代码
#define定义宏
#endif结束一个#if……#else条件编译块
  • 使用条件宏避免头文件多次包含

在C++编程中,头文件重复包含是一个常见的问题,可能会导致重复定义错误、编译时间增加等问题。为了防止头文件被重复包含,通常使用预处理宏(也称为条件宏)来实现。例如:

// MyHeader.h  
  
#ifndef MYHEADER_H  
#define MYHEADER_H  
  
// 头文件的内容从这里开始  
  
class MyClass {  
public:  
    void doSomething();  
};  
  
// 头文件的内容在这里结束  
  
#endif // MYHEADER_H
  • 使用#pragma once预处理指令

在C++编程中,#pragma once 是一个非标准的但广泛支持的预处理指令,用于防止头文件被多次包含(即“重复包含”)到同一个源文件或翻译单元中。当编译器遇到 #pragma once 时,它会确保当前头文件在当前的编译过程中只被包含一次,无论它在源文件中被 #include 了多少次。例如:

// MyHeader.h  
#pragma once  
  
class MyClass {  
public:  
    void doSomething();  
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2234104.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谍影重重5.0

打开流量包可以发现&#xff0c;流量中含有大量的smb加密通信&#xff0c;并且使用了ntlm v2加密协议进行身份认证 包过滤ntlmssp 认证后smb协议进行了大量的数据传输 取出tom的包内数据 得到以下数据 username:tomdomain: .NTProofStr: ca32f9b5b48c04ccfa96f35213d63d75NT…

qt QAbstractItemModel详解

1. 概述 QAbstractItemModel是Qt框架中的一个核心抽象基类&#xff0c;在Qt的模型/视图架构中扮演着至关重要的角色。这个类提供了一个接口&#xff0c;用于表示和管理数据&#xff0c;但不直接处理数据的存储。它的主要功能是为视图组件&#xff08;如QListView、QTableView和…

华为OD机试 - 无重复字符的元素长度乘积的最大值(Python/JS/C/C++ 2024 C卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

【C++之STL】一文学会使用 string

文章目录 1. STL导读1. 1 什么是STL1. 2 STL的版本1. 3 STL六大组件1. 4 STL的重要性1. 5 STL的学习1. 6 STL系列博客的规划 2. string2. 1 为什么学习string类?2. 2 标准库中的string2. 3 基本构造2. 4 尾插与输出运算符重载2. 5 构造函数2. 6 赋值运算符重载2. 7 容量操作2.…

【分布式】分布式锁设计与Redisson源码解析

分布式锁 分布式锁是一种在分布式计算环境中用于控制多个节点&#xff08;或多个进程&#xff09;对共享资源的访问的机制。在分布式系统中&#xff0c;多个节点可能需要协调对共享资源的访问&#xff0c;以防止数据的不一致性或冲突。分布式锁允许多个节点在竞争访问共享资源…

【实测有效】两个Ubuntu系统通过网线传输文件

基本思路是&#xff1a;连接网线后&#xff0c;通过设置静态IP&#xff0c;将两台电脑配置在同一个局域网内&#xff0c;再进行文件传输。 一、物理连接 使用网线将两台电脑的网口连接起来。 二、静态IP设置 两台电脑均需要对各自的静态IP进行设置。 1、查看电脑的IP地址 1&a…

Android camera2

一、序言 为了对阶段性的知识积累、方便以后调查问题&#xff0c;特做此文档&#xff01; 将以camera app 使用camera2 api进行分析。 (1)、打开相机 openCamera (2)、创建会话 createCaptureSession (3)、开始预览 setRepeatingRequest (4)、停止预览 stopRepeating (5)、关闭…

【Redis_Day3】Redis通用命令

【Redis_Day3】Redis通用命令 redis客户端的三种形态redis的快与慢redis通用命令阅读redis官方文档redis中两个核心命令set命令get命令 redis全局命令keys命令&#xff1a;查询当前服务器上的key生产环境 exists命令&#xff1a;判定key是否存在del命令&#xff1a;删除指定的k…

静态库、动态库、framework、xcframework、use_frameworks!的作用、关联核心SDK工程和测试(主)工程、设备CPU架构

1.1库的概念 库&#xff1a;程序代码的集合&#xff0c;编译好的二进制文件加上头文件供使用&#xff0c;共享程序代码的一种方式。 1.2库的分类 根据开源情况分为&#xff1a;开源库&#xff08;能看到具体实现&#xff09;、闭源库&#xff08;只公开调用的的接口&#xf…

小菜家教平台:基于SpringBoot+Vue打造一站式学习管理系统

前言 现在已经学习了很多与Java相关的知识&#xff0c;但是迟迟没有进行一个完整的实践&#xff08;之前这个项目开发到一半&#xff0c;很多东西没学搁置了&#xff0c;同时原先的项目中也有很多的问题&#xff09;&#xff0c;所以现在准备从零开始做一个基于SpringBootVue的…

【C++、数据结构】哈希表——散列表(一)(概念/总结)

「前言」 &#x1f308;个人主页&#xff1a; 代码探秘者 &#x1f308;C语言专栏&#xff1a;C语言 &#x1f308;C专栏&#xff1a; C / STL使用以及模拟实现 &#x1f308;数据结构专栏&#xff1a; 数据结构 / 十大排序算法 &#x1f308;Linux专栏&#xff1a; Linux系统编…

山东路远生态科技有限公司竣工投产仪式暨产品发布会圆满举行

第二十届三中全会于2024年7月15日至18日在北京举行。全会审议通过了《关于进一步全面深化改革、推进中国式现代化的决定》。其中提到,“要健全因地制宜发展新质生产力体制机制”。 新质生产力是由技术革命性突破、生产要素创新性配置、产业深度转型升级而催生的当代先进生产力…

MD5(Crypto)

解题思路 打开文件发现一串代码&#xff0c;结合题目提示&#xff0c;应该是 MD5 加密。 找个在线的 MD5 解密网站&#xff0c;行云流水得到 flag。 题目设计原理 题目设计&#xff1a;无他&#xff0c;MD5 加密。 题目原理&#xff1a; MD5&#xff08;Message-Digest Algo…

EHOME视频平台EasyCVR萤石设备视频接入平台视频诊断技术可以识别哪些视频质量问题?

EasyCVR视频监控汇聚管理平台是一款针对大中型项目设计的跨区域网络化视频监控集中管理平台。萤石设备视频接入平台EasyCVR不仅具备视频资源管理、设备管理、用户管理、运维管理和安全管理等功能&#xff0c;还支持多种主流标准协议&#xff0c;如GB28181、GB35114、RTSP/Onvif…

QML项目实战:自定义Button

目录 一.添加模块 ​1.QtQuick.Controls 2.1 2.QtGraphicalEffects 1.12 二.自定义Button 1.颜色背景设置 2.设置渐变色背景 3.文本设置 4.点击设置 5.阴影设置 三.效果 1.当enabled为true 2.按钮被点击时 3.当enabled为false 四.代码 一.添加模块 1.QtQuick.Con…

实战攻略 | ClickHouse优化之FINAL查询加速

【本文作者&#xff1a;擎创科技资深研发 禹鼎侯】 查询时为什么要加FINAL 我们在使用ClickHouse存储数据时&#xff0c;通常会有一些去重的需求&#xff0c;这时候我们可以使用ReplacingMergeTree引擎。这个引擎允许你存储重复数据&#xff0c;但是在merge的时候会根据order …

labview学习总结

labview学习总结 安装labview的特点一、图形化编程范式二、并行执行机制三、硬件集成能力四、应用领域优势五、开发效率六、系统集成能力**labview基本组成示意图****常用程序结构图解**结语 基础知识介绍界面前后面板的概念平铺式和层叠式 帧的概念结构类型顺序结构for循环whi…

Linux 服务器使用指南:从入门到登录

&#x1f31f;快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 &#x1f31f; &#x1f6a9;博主致力于用通俗易懂且不失专业性的文字&#xff0c;讲解计算机领域那些看似枯燥的知识点&#x1f6a9; 目录 一…

《AI 大模型:重塑软件开发新未来》

引言 在科技的璀璨星河中&#xff0c;AI 大模型宛如一颗耀眼的新星&#xff0c;正以前所未有的力量改写着软件开发的篇章。随着其技术的持续演进&#xff0c;软件开发流程正经历着翻天覆地的变化。从代码自动生成的神奇魔法&#xff0c;到智能测试的精准洞察&#xff0c;AI 大…

acmessl.cn提供接口API方式申请免费ssl证书

目录 一、前沿 二、API接口文档 1、证书可申请列表 简要描述 请求URL 请求方式 返回参数说明 备注 2、证书申请 简要描述 请求URL 请求方式 业务参数 返回示例 返回参数说明 备注 3、证书查询 简要描述 请求URL 请求方式 业务参数 返回参数说明 备注 4、证…