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

C++沉思:预处理和编译

预处理和编译

  • 条件编译源代码
    • 使用方式
    • 典型示例
    • 原理
  • 使用static_assert执行编译时断言检查
    • 使用方式
    • 原理

在C++中,编译是将源代码转换为机器代码并组织在目标文件中,然后将目标文件链接在一起生成可执行文件的过程。编译器实际上一次只处理一个文件,这个文件是由预处理器(编译器中处理预处理指令的部分)从单个源文件及其包含的所有头文件中生成的。

条件编译源代码

条件编译使开发人员能够维护单个代码库,但只考虑编译代码的某些部分,以生成不同的可执行文件(通常是为了在不同的平台或硬件上运行,或依赖于不同的库或版本)。

使用方式

若要条件编译部分代码,可以使用#if、#ifdef和#ifndef指令(与#elif、#else和#endif指令一起)。条件编译的一般形式如下:

#if condition1text1
#elif condition2text2
#elif condition3text3
#elsetext4
#endif

要定义用于条件编译的宏,可以使用以下方法之一:

  1. 在源代码中使用#define指令:

    #define VERBOSE_PRINTS
    #define VERBOSITY_LEVEL 5
    
  2. 使用编译器命令行选项
    对于g++而言:-D是一个选项,用于在编译时定义宏

    main.cpp

    #include <iostream>int main() {std::cout << "Hello, World!" << std::endl;#ifdef DEBUGstd::cout << "Debug mode is enabled." << std::endl;#elsestd::cout << "Debug mode is disabled." << std::endl;#endifreturn 0;
    }
    

    编译指令,定义宏DEBUG

    g++ -DDEBUG main.cpp -o main
    

    输出Debug mode is enabled.
    普通编译

    g++ main.cpp -o main
    

    输出Debug mode is disabled.

典型示例

  1. 头文件保护避免重复定义
    现在更多的使用:pragma once

    #ifndef HEADER_NAME
    #define HEADER_NAMEclass A {}
    #endif
    

    两者都可以实现头文件的防重复包含,但前者适用于所有遵循C/C++预处理器约定的编译器,后者则依赖于编译器的支持。

  2. 针对跨平台应用程序
    将带有编译器名称的消息打印到控制台

    void show_compiler() {
    #if defined _MSC_VERcout << "VC++\n";
    #elif defined __clang__cout << "Clang\n";
    #elif defined __GNUG__cout << "GCC\n";
    #elsecout << "unknown compiler\n";
    #endif
    }
    
  3. 针对多个架构的目标特定代码

    void show_architecture()
    {
    #if defined _MSC_VER#if defined _M_X64std::cout << "AMD64";#elif defined _M_IX86std::cout << "INTEL x86";#elif defined _M_ARMstd::cout << "ARM";#elsestd::cout << "unknown";#endif
    #elif defined __clang__ || __GNUC__#if defined __amd64__std::cout << "AMD64";#elif defined __i386__std::cout << "INTEL x86";#elif defined __arm__std::cout << "ARM";#elsestd::cout << "unknown";#endif
    #else#error Unknown compiler
    #endif
    }
    
  4. 特定于配置的代码
    有条件地编译调试和发布版本

    void show_configuration() {
    #ifdef _DEBUGcout << "debug\n";
    #else cout << "release\n";
    #endif
    }
    

原理

当使用预处理指令#if、#ifndef、#ifdef、#elif、#else和#endif时,编译器将至多选择一个分支,其主体将包含在编译单元中。这些指令的主体可以是任何文本,包括其他预处理指令。适用规则如下:

  1. #if、#ifdef和#ifndef必须用#endif匹配。
  2. #if指令可以有多条#elif指令,但只有一条#else指令,且#else必须是#endif之前的最后一条。
  3. #if、#ifdef、#ifndef、#elif、#else和#endif可以嵌套。
  4. #if指令需要一个常量表达式,而#ifdef和#ifndef则需要一个标识符。
  5. defined操作符可以用于预处理器常量表达式,但只能在#if和#elif指令中使用。
  6. defined(identifier)在定义identifier时为true,否则,它被认为是false。
  7. 定义为空文本的标识符被认为是有定义的。
  8. #ifdef identifier等价于#if defined(identifier)。
  9. #ifndef identifier等价于#if !defined(identifier)。
  10. defined(identifier)和defined identifier是等价的。
  11. 宏的名称在整个应用程序中必须是唯一的,否则,将只编译使用宏的第一个头文件中的代码

使用static_assert执行编译时断言检查

C++可以同时执行运行时和编译时断言检查。注意:C++版本需要高于>=C++11才会出现编译错误

  1. 运行时断言只有在程序运行时并且只有在控制流到达它们时才会被验证。当条件依赖于运行时数据时只能选择运行时断言。
  2. 编译时断言(条件可以在编译时求值)能够在开发阶段的早期通知某个特定条件未被满足。在C++11中,编译时断言是通过static_assert执行的。
  3. 静态断言检查常在模板元编程中用于验证模板类型的先决条件是否满足(例如类型是否为POD类型、可复制构造类型、引用类型等)。另一个典型用例是确保类型(或对象)具有预期的大小。

使用方式

使用 static_assert 声明来确保满足以下作用域中的条件:

  1. 命名空间作用域,本例验证item类的大小总是16

    struct alignas(8) item
    {int     id;bool    active;double  value;/* data */
    };static_assert(sizeof(item) == 16,"size of item must be 16 types");
    
  2. 类作用域,本例验证pod_wrapper只能与POD类型一起使用

    is_standard_layout_v 是 C++ 标准库 <type_traits> 中的一个模板元函数,它用于在编译时判断一个类型是否是标准布局类型。标准布局类型需要满足一系列规则,这些规则包括:

    • 没有虚函数(包括虚析构函数)
    • 没有虚基类
    • 所有非静态成员都是标准布局类型
    • 非静态成员之间没有相同的名称
    • 类的继承关系中没有相同的基类
    • 满足其他一些与对齐和大小相关的规则
    template <typename T>
    class pod_wrapper {static_assert(std::is_standard_layout_v<T>,"POD type expected!");T value;
    };struct point
    {int x;int y;/* data */
    };
    pod_wrapper<int>            w1;	//ok
    pod_wrapper<point>          w2;	//ok
    pod_wrapper<std::string>    w3;	//err
    
  3. 函数块作用域,本例验证函数模板是否只有整型参数

    #include <type_traits>template <typename T>
    auto mul(T const a, T const b) {static_assert(std::is_integral_v<T>::value,"Integral type expected");return a * b;
    }
    auto v1 = mul(1, 2);	//ok
    auto v2 = mul(1.2, 3.4);	//err
    

原理

static_assert是一个声明,但它没有引入新名称。这些声明的形式如下:

static_assert(condition, message);

该条件必须在编译时转换为布尔值,且消息必须是字符串字面量。在C++17中,该消息是可选的。当static_assert声明中的条件计算结果为true时,什么都不会发生;当条件的计算结果为false时,编译器生成一个包含指定消息(如果有的话)的错误。

相关文章:

C++沉思:预处理和编译

预处理和编译 条件编译源代码使用方式典型示例原理 使用static_assert执行编译时断言检查使用方式原理 在C中&#xff0c;编译是将源代码转换为机器代码并组织在目标文件中&#xff0c;然后将目标文件链接在一起生成可执行文件的过程。编译器实际上一次只处理一个文件&#xff…...

交通数据处理-计算途径某些路段的车辆数

根据车辆的运行轨迹&#xff0c;计算先经过某些路段&#xff0c;再经过某些路段的车辆数。 欢迎关注本人公众号--交通数据探索师 如下表&#xff0c; 其中&#xff1a;vehicle: 车辆编号&#xff1b;route: 车辆轨迹。 以第一行为例&#xff0c;车辆car1按顺序经过了路段123…...

从0到1入门系列 | 崖山公开课再加码,三小时带你入门崖山数据库!

对不断更新的技术心生迷茫 不知如何正确的提升自己&#xff1f; 对新兴的国产数据库领域充满好奇 却不知从何入手&#xff1f; 崖山专家团队精心筹备 《从0到1入门》系列直播课 6节课 三小时 助力数据库小白变身技术高手 掌握最前沿的数据库技术 现在开始 开启职场“金…...

Powershell自定义带参数的别名

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、函数二、使用步骤总结 前言 之前写了一篇文章定义别名让powershell尽可能接近Unix风格&#xff0c;增强两者的互操作性&#xff0c;今天给出方法可以定义带…...

文件操作相关的精讲

目录&#xff1a; 思维导图 一. 文件定义 二. 文件的打开和关闭 三. 文件的顺序读写操作 四. 文件的随机读写操作 五. 文本文件和二进制文件 六. 文件读取结束的判断 七.文件缓冲区 思维导图&#xff1a; 一. 文件定义 1.文件定义 C语言中&#xff0c;文件是指一组相…...

05 循环神经网络

目录 1. 基本概念 2. 简单循环网络 2.1 简单循环网络 2.2 长程依赖问题 3. 循环神经网络的模式与参数学习 3.1 循环神经网络的模式 3.2 参数学习 4. 基于门控的循环神经网络 4.1 长短期记忆网络 4.2 LSTM网络的变体网络 4.3 门控循环单元网络 5. 深层循环神经网络…...

C#初级——条件判断语句、循环语句和运算符

条件判断语句 简单的条件判断语句&#xff0c;if()里面进行条件判断&#xff0c;如果条件判断正确就执行语句块1&#xff0c;如果不符合就执行语句块2。 if (条件判断) { 语句块1 } else { 语句块2 } int age 18;if (age < 18){Console.WriteLine("未…...

Laravel路由模型绑定:简化依赖注入的艺术

Laravel路由模型绑定&#xff1a;简化依赖注入的艺术 引言 在现代Web应用开发中&#xff0c;Laravel框架以其优雅和简洁的代码而闻名。Laravel的路由模型绑定&#xff08;Route Model Binding&#xff09;是框架提供的一项强大功能&#xff0c;它允许开发者在路由处理中自动注…...

【vue前端项目实战案例】之Vue仿饿了么App

本文将介绍一款仿“饿了么”商家页面的App。该案例是基于 Vue2.0 Vue Router webpack ES6 等技术栈实现的一款外卖类App&#xff0c;适合初学者进行学习。 项目源码下载链接在文章末尾 1 项目概述 该项目是一款仿“饿了么”商家页面的外卖类App&#xff0c;主要有以下功能…...

冷热分离——Java全栈知识(36)

之前在面试的时候有老师问&#xff1a; 我看你使用了水平分表&#xff0c;但是如果有些 1%的数据占了访问量的 90%&#xff0c;而剩下 99%的数据只占了访问量的 10%。这种情况怎么处理。 1 、冷热分离 1.1、什么是冷热分离 冷热分离指的是在处理数据时将数据库分为冷库和热库…...

了解Selenium中的WebElement

Selenium中到处都使用WebElement来执行各种操作。什么是WebElement&#xff1f;这篇文章将详细讨论WebElement。 Selenium中的WebElement是一个表示网站HTML元素的Java接口。HTML元素包含一个开始标记和一个结束标记&#xff0c;内容位于这两个标记之间。 HTML元素的重命名 …...

OpenCV facedetect 人脸检测官方示例项目配置

运行程序。该程序会自动打开摄像头&#xff0c;识别并定位摄像头前的人脸以及眼睛部位。 输入q或者Q&#xff0c;退出程序。 或进行文本中所包含的图片路径 或 单个图片进行检测&#xff0c;自行修改代码即可 配置环境项目&#xff0c;debug 解决error C4996: ‘fopen’: This…...

自定义Laravel Artisan风格:打造个性化命令行体验

自定义Laravel Artisan风格&#xff1a;打造个性化命令行体验 引言 Laravel的Artisan命令行工具是开发过程中不可或缺的一部分&#xff0c;它提供了一个强大的接口来执行各种开发、维护、测试等任务。除了执行命令&#xff0c;Artisan还允许开发者自定义命令行输出的风格&…...

CTF之网站被黑

简单看一下网页和源码没发现什么明显漏洞 那就扫描一下目录 发现了/shell.php文件&#xff0c;访问一下&#xff0c;发现是一个后台管理登录页面 别无他法只能爆破喽&#xff0c;爆破后发现密码是hack flag{25891d9e9d377f006eda3ca7d4c34c4d}...

Electron学习笔记(一)基础环境

目录 前言 基础环境准备 安装 Node.js 配置项目文件 通过代理服务安装 通过国内仓库安装 一些常见问题&#xff1a; 前言 一个新手学习Electron的笔记&#xff0c;记录为主&#xff0c;仅供参考。 其他文章见专栏目录。 基础环境准备 开发之前先将基础环境搭建好。 …...

【C语言】栈的实现(数据结构)

前言&#xff1a; 还是举一个生活中的例子&#xff0c;大家都玩过积木&#xff0c;当我们把积木叠起来的时候&#xff0c;如果要拿到最底部的积木&#xff0c;我们必须从顶端一个一个打出&#xff0c;最后才能拿到底部的积木&#xff0c;也就是后进先出&#xff08;先进后出&a…...

前端三大主流框架对比

在现代前端开发中&#xff0c;React、Vue和Angular是三大流行的框架/库。它们各自有独特的优缺点&#xff0c;适用于不同的开发需求和项目规模。下面是对这三者的详细比较&#xff1a; 一、 React 简介&#xff1a; 由Facebook开发和维护&#xff0c;是一个用于构建用户界面…...

AOP~面向切面编程介绍

AOP基础 概述 AOP&#xff1a;Aspect Oriented Programming&#xff08;面向切面编程、面向方面编程&#xff09;&#xff0c;面向特定方法的编程。 动态代理是面向切面编程最主流的实现。 SpringAOP是Spring框架的高级技术&#xff0c;旨在管理bean对象的过程中&#xff0c…...

Android SurfaceFlinger——GraphicBuffer的提交(三十三)

在 SurfaceFlinger 中,我们 dequeueBuffer 和 queueBuffer 是 Surface 控制接口中非常重要的两个函数,分别用于从 Surface 的 BufferQueue 中取出缓冲区和向 BufferQueue 提交(队列)缓冲区。这两个函数在生产者和消费者模型中扮演着核心角色,确保了图像数据的高效和有序传…...

创维汽车滁州永通体验中心开业仪式暨超充车型区域上市会圆满成功

2024年7月20日&#xff0c;创维汽车滁州永通体验中心盛大开业&#xff0c;当日&#xff0c;创维汽车市场部经理周世鹏、安徽大区总监王大明等领导参加本次开业盛典&#xff0c;共同见证创维汽车滁州永通体验中心成功落地。 2021年&#xff0c;新能源乘用车高速发展&#xff0c;…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...