当前位置: 首页 > 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;…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...

在 Spring Boot 项目里,MYSQL中json类型字段使用

前言&#xff1a; 因为程序特殊需求导致&#xff0c;需要mysql数据库存储json类型数据&#xff0c;因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...