Real-Time C++ 嵌入式C++ 程序设计(三)

news2024/11/18 20:26:07

翻译自 Real-Time C++ Efficient Object-Oriented and Template Microcontroller Programming 4th Edition - Kormanyos, Christopher,这书涉及了从C++11 到C++20 的内容,主要介绍使用C++ 的模板、面向对象等特性设计嵌入式程序。书里的示例代码都是公开的:https://github.com/ckormanyos/real-time-cpp

第一篇地址,有原书PDF:https://blog.csdn.net/Etberzin/article/details/130867462


三、轻松学习C++ 的一个子集

新接触实时C++的开发人员可能希望在花时间掌握C++语言的所有复杂细节之前快速获得一些有用的例子。本章通过提供一个简单而有效的C++ 语言子集来满足这种需求,该子集专门为那些寻求轻量级和可靠的实时C++ 快速入门的人设计。本章中的C++子集代表了一种明智的选择,它包含了一些最容易做到的C++事情,可以在尽可能广泛的编程情况下使用。这个C++子集的定位如图3.1所示。此外,第3.2节还提供了详细的项目,比如使用筛选法计算质数。

图3.1

3.1 到使用的地方再定义局部变量

在C++ 中,局部变量可以在第一次使用的地方声明,它们不一定需要放在函数的开头。这可以提高代码可读性并促进编译器优化。例如,下面的代码在使用i、j和k前才声明了整型变量。

// chapter03_01-001_declare_locals.cpp
void initialize();
void use_i(const int);
void use_j(const int);
void use_k(const int);

void do_something()
{
	// Initialize someting.
	initialize();
	// Declare i when using it in use_i().
	const int i = 3;
	use_i(i);
	// Declare j when using it in use_j().
	const int j = 7;
	use_j(j);
	// Declare k in the scope of the for-loop.
	for(int k = 0; k < 10; ++k)
	{
		use_k(k);
	}
}

3.2 固定大小的整数类型和质数实例

C++标准库在其<cstdint>头文件中提供了一整套可移植的固定大小整数类型。如第1.8节所述,用户定义的内置整型类型(如my_uint8、my_uint16等)可能会笨拙、难以维护且容易出错,特别是在多个环境中使用时。然而,从C++11 开始,非标准的用户定义类型可以用标准的固定大小整数类型(如std::uint8_t、std::uint16_t、std::uint32_t 等)替换。

例如,下面的代码使用指定宽度的整数变量,如精确16位和至少32位。标准宏UINT8_C()、UINT16_C()、UINT32_C() 以及对应的有符号类型宏也在<cstdint>头文件中定义。

// chapter03_02-001_fixed_size_integer.cpp

#include <cstdint>

// 这个值是固定的16 位
constexpr std::int16_t value16 = INT16_C(0x7FFF);

// 这个值的尺寸至少是32 位
// 4’294’967’295 这个写法中’ 用来任意分隔多位数字,可以按英语习惯,也可以按四位一分,从而提高可读性
constexpr std::uint_least32_t value32 = UINT32_C(4294967295)

正如上面所示,它们可以方便地创建具有指定宽度的数字文字整数值。这些宏也提高了代码的完整性。特别地,它们可能优于常用的后缀,如U、L、UL、LL、ULL等。

考虑如下例子,初始化一个常量10,006,721,这是第664999 个质数:

// chapter03_02-002_prime_number.cpp

#include <cstdint>

// 后缀U 表示unsigned int
constexpr std::uint32_t prime_664999 = 10006721U;

这段代码可能不够健壮或存在可移植性问题。整数文字常量10'006'721U表示第664,999个质数。然而,这种表示依赖于后缀U,表示unsigned int。当将这段代码移植到其他平台时,特别是8 位平台,unsigned int 可能不是32 位宽。它可能只有16 位宽,甚至只有8 位宽。在这种情况下,unsigned int不足以容纳整数值10,006,721,初始化可能会产生潜在的混淆(甚至不正确)。

现在我们将稍微修改prime_664999的初始化,以提高编码完整性。在这种情况下,初始化是明确的、清晰表达的和可移植的:

// chapter03_02-002_prime_number.cpp

#include <cstdint>

// Initialize the 664,999th prime number.
// UINT32_C() 等宏有更强的可移植性性
constexpr std::uint32_t prime_664999 = UINT32_C(10006721);

宏UINT32_C() 保证能够处理无符号32 位整数数据类型的范围,从0 到4,294,967,295。实际上,保持一致使用固定大小的整数类型并不会对功能产生很大贡献,而是一种风格。这通常会让代码在不同平台上获得相似的表现和运行结果,从而提高可移植性。

在一个中等大小的程序的背景下使用固定大小的整数类型在示例chapter03_02 的代码中展示。在这个例子中,使用埃拉托色尼筛法在8位目标微控制器上计算顺序质数。

示例chapter03_02 使用了本书后面描述的几个C++特性。其中包括模板编程(第5章)、在微控制器环境中使用STL(第5.8节)、硬件驱动程序(第9章)、自定义内存管理(如环形分配器,第10.5节)、多任务(第11章)、浮点计算(第12章)等。这个例子在本书的这个早期阶段提供,是为了提供一个完整且内容丰富的示例。

示例chapter03_02 的理论背景始于数学特殊函数,特别是对数积分函数,即:

L i ( x ) = l i ( x ) − l i ( 2 ) = ∫ 2 x d t l o g ( t ) (3.1) Li(x) = li(x) - li(2) = \int^x_2 \frac{dt}{log(t)} \tag{3.1} Li(x)=li(x)li(2)=2xlog(t)dt(3.1)

这里li(x)被称为对数积分函数,Li(x)是偏移对数积分函数。

可以从质数计数函数π(x)在质数定理中获得给定上限以下质数数量的估计。这个非凡且著名的定理通过渐近极限将偏移对数积分函数Li(x)与质数计数函数联系起来:

π ( x ) ∼ L i ( x ) (3.2) \pi(x) \sim Li(x) \tag{3.2} π(x)Li(x)(3.2)

注:师傅,你是干啥的

再次考虑上面的例子:第66499 个质数10,006,721,对于这个素数,式3.1 和式3.2 的结果是:

10 , 006 , 721 66499 ≈ 665335.4... − 1.045... 66499 ≈ 1.000504... ? ~ 1 (3.3) \begin{align} \frac{10,006,721}{66499} &\approx \frac{665335.4... - 1.045... }{66499} \\ &\approx 1.000504... \\ &\utilde{?} 1 \end{align} \tag{3.3} 6649910,006,72166499665335.4...1.045...1.000504... ?1(3.3)

其中, l i ( 2 ) ≈ 1.045 … li(2) ≈ 1.045 … li(2)1.045。在x = 10,006,721 这个特定的数值点上,式3.2 中的比值为1.000504 …,略大于1。这个比值随着x的增加而更接近1,推测在无限大的x时达到1。

在示例chapter03_02 中,目的是计算至少100个质数。为了确定和验证筛选计算的适当限制,我们考虑对数积分函数在x 较大时的一个初级发散渐近级数展开:

l i ( x ) ∼ x log ⁡ x ∑ k = 0 ∞ k ! ( log ⁡ x ) k (3.4) li(x) \sim \frac{x}{\log{x}} \sum^\infty_{k=0} \frac{k!}{(\log{x})^k} \tag{3.4} li(x)logxxk=0(logx)kk!(3.4)

得出渐近数值近似:

π ( x ) ∼ ( x log ⁡ x ∑ k = 0 ∞ k ! ( log ⁡ x ) k ) − l i ( 2 ) (3.5) \pi(x) \sim \left( \frac{x}{\log{x}} \sum^\infty_{k=0} \frac{k!}{(\log{x})^k} \right) - li(2) \tag{3.5} π(x)(logxxk=0(logx)kk!)li(2)(3.5)

其中 x x x 足够大。在软件中实现这个计算时,展开式中的循环应该在求和项达到最小值后终止,此后该点开始增加,因为该级数是发散的。

预先知道第100 个质数是541,讲542 代入式3.5,结果是:

π ( 542 ) ∼ ( 542 log ⁡ 542 ∑ k = 0 7 k ! ( log ⁡ 542 ) k ) − 1.045... ≈ 108 (3.6) \begin{align} \pi(542) &\sim \left( \frac{542}{\log{542}} \sum^7_{k=0} \frac{k!}{(\log{542})^k} \right) - 1.045... \\ &\approx 108 \end{align} \tag{3.6} π(542)(log542542k=07(log542)kk!)1.045...108(3.6)

其中,发散级数从0 到7 展开。这证实了通过将筛选循环的上限设置为 542 的平方根的向下取整值,可以轻松计算前一百个质数。

使用无符号整数值组成的专用容器来保存筛子,每个整数被分成位段。该容器被实现为一个名为dynamic_bitset的模板类。容器中的内存存储使用第 10.5 节中的专用环形分配器。

下面的代码序列显示了模板子程序 compute_primes_via_sieve 的一部分。它直接取自示例 chapter03_02。

template<const std::size_t maximum_value, typename forward_iterator_type, typename alloc>
void compute_primes_via_sieve
(
	forward_iterator_type first)
	{
	// Use a sieve algorithm to generate
	// a table of primes.
	// ...
	// ...
	// Create the sieve of primes.
	// ...
	// ...
	for(local_value_type i = 2U; i < local_value_type(imax);	++i)
	{
			if(sieve.test(i) == false)
			{
				const local_value_type i2 = i * i;
				for(local_value_type j = i2; j < maximum_value;	j += i)
				{
					sieve.set(j);
				}
			}
	}
// ...
// ...
// Fill the prime numbers into the data table
// by extracting them from the sieve of primes.
// ...
// ...
}

注:不知道作者想用这坨鬼东西表达什么,关键的东西全都省略掉,让初学者轻松学习的细节在tm 的哪里

这段代码显示了筛选计算的内部循环。当在筛子中计算上限为 n 的质数时,嵌套循环的计算复杂度大致为 O ( n log ⁡ log ⁡ n ) O(n \log{\log{n}}) O(nloglogn)。有关算法计算复杂度的进一步讨论,请参见第 6.3 节。

筛选计算被初始化并从头到尾完全运行。这是在 app::prime 任务中以约 20 Hz 的频率(即每 50 毫秒左右一次)循环完成的。通过确保计算出的第 100 个质数的值与预期值 541 相符来进行数值验证。

时间测量使用数字示波器,采用第 9.6 节中的方法。在这个特定的例子中,在筛选计算开始和结束时切换 portd.3。每次执行筛选计算的测量运行时间约为 4.3 毫秒,目标硬件为 8 位微控制器。

对质数的研究可以引导人们进入纯数学和应用数学、数论、高性能计算等众多不同领域的迷人主题。有关质数计数函数的更多信息,请参见 [12] 及其中的参考文献。关于计算质数的全面介绍可以在包括 [1] 和 [7] 第二卷等著作中找到。

本书后面,第 16.8 节中的示例 chapter16_08 使用随机选择的 128 位整数来探索质数定理。

注:以下列举的C++ 特性懒得从书里全部搬过来,有个标题当搜索关键词就行了,详细的自己去了解

3.3 bool 类型

3.4 用命名空间组织代码

3.5 基本的类

3.6 基本的模板

3.7 用nullptr 替换NULL

3.8 constexpr 和广义常量表达式

3.9 static_assert

3.10 使用<limits> 头文件

3.11 std::array

3.12 基本的STL 算法

3.13 <numeric> 头文件

3.14 原子操作 atomic_load() 和atomic_store()

3.15 数字分割符

3.16 二进制字面量

从C++14 开始终于引入了二进制字面量,代码中可以用0b0BbB 前缀直接写出二进制整数:

// chapter03_16-001_binary_literals.cpp

constexpr std::uint8_t one = UINT8_C(0b1);
constexpr std::uint8_t seven = UINT8_C(0b00000111);

3.17 用户自定义字面量

3.18 使用alignof 和alignas

3.19 final 标识符

3.20 用using 替换typedef

3.21 用<span> 实现范围操作

3.22 用<random> 生成随机数


注:显然,这书就是一坨垃圾,只能感觉到这作者估计不是工科出身的,他所谓的“practical” 和我们一般人的理解有点不一样,他的“real-time” 大概也不是大伙一般概念中的那个东西。这书能出到第四版,肯定是作者退休了烧钱玩的

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

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

相关文章

ChatGPT报错:Sorry, you have been blocked解决方法

今天打开ChatGPT&#xff0c;发现再一次报错了&#xff01; 又一次出问题了。。。。。。。无语&#xff01; 原因分析 1、内容过滤&#xff1a;某些平台或网站可能使用内容过滤系统&#xff0c;该系统可能将AlI语言模型视为潜在的风险&#xff0c;从而对其进行封锁或限制。这…

传染病学模型 | Matlab实现基于SIS传染病模型模拟城市内人口的互相感染及城市人口流动所造成的传染

文章目录 效果一览基本描述模型介绍程序设计参考资料效果一览 基本描述 传染病学模型 | Matlab实现基于SIS传染病模型模拟城市内人口的互相感染及城市人口流动所造成的传染 模型介绍 SIS模型是一种基本的传染病学模型,用于描述一个人群中某种传染病的传播情况。SIS模型假设每个…

jsonschema networknt json-schema-validator 高级能力 自定义类校验

入参校验产品化 schema_个人渣记录仅为自己搜索用的博客-CSDN博客 自定义的string format可以使用. 详见 fpe的 addFormatValidator ajv 的 addFormat能力 借鉴自chatgpt , 谷歌了半天没答案. Q: "networknt JsonSchemaFactory Keyword " A: 如下 <dependenc…

windows下cplex20.1.0的下载、安装、IDE编程及相关问题解决

其他文章&#xff1a; 通过0-1背包问题看穷举法、贪心算法、启发式算法&#xff08;JAVA) 模拟退火(SA)算法实例介绍&#xff08;JAVA) 遗传算法&#xff08;GA&#xff09;实例介绍&#xff08;JAVA) CPLEX求解器入门案例 java集成Cplex&#xff1a;Cplex下载、IDEA环境搭…

css面试复习

目录 css常用网址: css三种书写样式 css属性 color(如字体颜色) text-decoration(如下划线) text-align(文字对齐) 字体属性 font-size font-family(字体名称) font-weight(字体粗细) font-style(斜体) line-height font缩写属性 css常见选择器 通用选择器 简单…

小黑子—MySQL数据库:第一章 -基础篇

MySQL数据库入门1.0 MySQL基础篇1. MySQL概述1.1 MySQL相关概念1.2 MySQL的安装及启动1.3 数据模型 2. SQL2.1 SQL的通用语法2.2 SQL语句的分类2.3 DDL语句2.3.1 DDL - 数据库操作2.3.2 DDL - 表操作 - 查询2.3.3 DDL - 表操作 - 创建2.3.4 DDL - 表操作 - 数据类型2.3.5 DDL -…

搭建stm32电机控制代码框架(三)——Stm32CubeMx配置ADC采样

电机控制另一个关键的模块就是ADC采样&#xff0c;这个模块配置的好坏决定了采样电流和电压的精准度&#xff0c;因此有必要对其进行深入学习。 简介&#xff1a; STM32 在片上集成的ADC 外设非常强大。STM32F103xC、STM32F103xD 和STM32F103xE增强型产品内嵌3个12位的ADC&am…

JDK8-JDK17中的新特性(var类型推断、模式匹配、Record、密封类)

文章目录 1. 新语法结构1.1 Java的REPL工具&#xff1a; jShell命令1.2 异常处理之try-catch资源关闭1.3 局部变量类型推断1.4 instanceof的模式匹配1.5 switch表达式1.6 文本块1.7 Record1.8 密封类 2. API的变化2.1 Optional类2.2 String存储结构和API变更2.3 JDK17&#xff…

vue-element-admin实践系列(二)初始化系统的页面元素

vue-element-admin实践系列 vue-element-admin实践系列(一)代码部署及运行demovue-element-admin实践系列(二)初始化系统的页面元素 文章目录 vue-element-admin实践系列1、修改默认参数1.1 修改启动端口1.2 修改网页title1.3 修改网站 ico1.4 效果如下 2、自定义左侧导航栏2.…

Fourier分析入门——第9章——Fourier系数的假设检测

目录 第9章 Fourier系数的假设检验 9.1 引言 9.2 回归分析(Regression analysis) 9.3 带限信号(Band-limited signals) 9.4 可信区间(Confidence intervals) 9.5 Fourier系数的多元(或多变量)统计分析(Mulitvariate statistical analysis of Fourier coefficients) 第9章 …

Three.js--》实现3d球形机器人模型展示

目录 项目搭建 初始化three.js基础代码 设置环境纹理 加载机器人模型 添加光阵 今天简单实现一个three.js的小Demo&#xff0c;加强自己对three知识的掌握与学习&#xff0c;只有在项目中才能灵活将所学知识运用起来&#xff0c;话不多说直接开始。 项目搭建 本案例还是…

(学习日记)AD学习 #4

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

C4D R26 渲染学习笔记 建模篇(0):建模常识

往期文章 C4D R26 渲染学习笔记&#xff08;1&#xff09;&#xff1a;C4D版本选择和初始UI框介绍 C4D R26 渲染学习笔记&#xff08;2&#xff09;&#xff1a;渲染流程介绍 C4D R26 渲染学习笔记&#xff08;3&#xff09;&#xff1a;物体基本操作快捷键 C4D如何建模 默认…

TiDB安装简介

文章目录 一、TiDB概述1、简介2、OLAP和OLTP3、与MySQL兼容性 二、架构三、安装1、本地版安装2、单机版集群安装2.1 概述2.2 安装2.3 访问集群 3、配置文件地址 四、使用方式1、基础SQL2、历史数据查询 一、TiDB概述 官网地址 https://docs.pingcap.com/zh/tidb/stable/quick…

(浙大陈越版)数据结构 第三章 树(上) 3.2 二叉树及存储结构

目录 3.2.1 二叉树的定义及性质 定义: 二叉树五种基本形态&#xff1a; 特殊二叉树 二叉树的几个重要性质 二叉树的抽象数据类型定义 操作集&#xff1a; 常用遍历&#xff1a; 3.2.2 二叉树的存储结构 顺序存储结构 数组实现 链表实现 3.2.1 二叉树的定义及性质 …

RK3588平台开发系列讲解(项目篇)YOLOv5部署测试

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、YOLOv5环境安装二、YOLOv5简单使用2.1、获取预训练权重文2.2、YOLOv5简单测试2.3、转换为rknn模型2.4、部署到 RK 板卡三、airockchip/yolov5简单测试3.1、转换成rknn模型并部署到板卡沉淀、分享、成长,让自己和他…

PyTorch-Transforms

目录 1. 基本知识 2. 常见的Transforms 2.1 ToTensor的使用 2.2 Normalize的使用 2.3 Resize的使用 2.4 Compose的使用 2.5 RandomCrop的使用 1. 基本知识 图片引用来自b站up主&#xff1a;我是土堆 tf.py&#xff08;如何使用transforms里面的工具&#xff09; 具体…

1 echarts与map相遇

echarts与map 版本 "echarts": "^5.1.2","echarts-gl": "^2.0.9",效果 基本方法 echarts中地图大多是以地理坐标为主&#xff0c;数据格式使用geojson数据格式。geojson是GIS数据格式中第二常用格式&#xff0c;使用GIS软件都能实现…

英文论文(sci)解读复现【NO.11】一种先进的基于深度学习模型的植物病害检测:近期研究综述

此前出了目标检测算法改进专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读发表高水平学术期刊中的 SCI论文&a…

简单个人电话号码查询系统

系列文章 任务19 简单个人电话号码查询系统 文章目录 系列文章一、实践目的与要求1、目的2、要求 二、课题任务三、总体设计1.存储结构及数据类型定义2.程序结构3.所实现的功能函数 四、小组成员及分工五、 测试文件读取添加联系人删除联系人修改联系人查询联系人退出 六、源代…