C++编程:从标准库扩展到C代码应用
在C++编程领域,有许多重要的技术和概念值得深入探讨。本文将围绕C++标准库扩展、独立实现模式,以及在C++项目中使用C语言代码等方面展开详细介绍。
1. C++标准库扩展与独立实现
C++标准将编译器实现分为托管式(hosted)和独立式(freestanding)两种。托管式实现能提供完整的语言和库支持,涵盖所有指定的头文件;而独立式实现支持的库函数和头文件相对较少。
目前,有持续的努力在尝试扩展独立式实现的范围,使其包含更多的强制头文件和库函数。在实际编程中,一种经过精心挑选的、高效且节省资源的库函数集得到了广泛应用,该集合在很大程度上与这一扩展方向相符。
一些开发者认为,独立式实现非常适合资源受限的领域。例如,那些对某些“重型”语言元素(如堆的使用、运行时类型信息(RTTI)、异常处理、线程、浮点函数支持等)可能带来的开销较为敏感的应用。因此,独立式实现或其未来的类似解释,可能是使用C++进行裸机实时嵌入式微控制器编程的理想选择。
2. 在C++中使用C语言代码
在C++项目中访问C语言代码是很常见的需求,尤其是在嵌入式微控制器编程领域。下面以循环冗余校验(CRC)计算为例,详细介绍如何在C++中使用C语言代码。
2.1 访问C语言代码
假设有一个用C语言编写的函数
FunctionInC
,存储在C文件中:
// This is in a C file.
void FunctionInC(void)
{
// ...
}
若要在C++文件中调用该函数,需使用
extern "C"
声明,以告知C++编译器该函数具有C链接:
// This is in a C++ file.
// Tell the C++ compiler about the C function.
extern "C" void FunctionInC(void);
void do_something()
{
// Now the C++ compiler can access the C function.
FunctionInC();
}
extern "C"
声明会使C++编译器停用该函数的名称修饰,从而使
FunctionInC
在C和C++中都可访问。
2.2 现有的C语言CRC库
在嵌入式微控制器系统中,CRC计算常用于验证一个或多个字节流数据的完整性。AUTOSAR 4.3标准规定了至少四种CRC算法,包括8位、16位、32位和64位。以下是这些CRC算法的主要特性:
| CRC名称 | 位数 | 多项式 | CRC(0x31 … 0x39) |
| ---- | ---- | ---- | ---- |
| CRC8/AUTOSAR | 8 | 0x2F | 0xDF |
| CRC16/CCITT-false | 16 | 0x1021 | 0x29B1 |
| CRC32/AUTOSAR | 32 | 0xF4ACFB13 | 0x1697D06A |
| CRC64/XZ | 64 | 0x42F0E1EBA9EA3693 | 0x995DC9BBDF1939FA |
以8位CRC为例,其C语言接口包含初始化、字节处理和最终化等函数:
void Crc08_Initialize(Crc8_Context_Type* Crc_Context);
void Crc08_ProcessBytes(const uint8_t* DataIn, const size_t DataLength, Crc8_Context_Type* Crc_Context);
void Crc8_Finalize(Crc08_Context_Type* Crc_Context);
使用这个C语言的CRC库很简单,以下是一个计算ASCII字符1到9的CRC8的示例:
// This is in a C file.
#include <stdint.h>
#include <math/checksums/crc/Crc08.h>
static const uint8_t test_data[9U] =
{
0x31U, 0x32U, 0x33U, 0x34U,
0x35U, 0x36U, 0x37U, 0x38U,
0x39
};
void do_something(void)
{
// Perform the Crc08 with the traditional
// C-language interface.
Crc08_Context_Type CrcContext;
Crc08_Initialize(&CrcContext);
Crc08_ProcessBytes(&test_data[0U], sizeof(test_data), &CrcContext);
Crc08_Finalize(&CrcContext);
// The result is 0xDF.
const uint8_t CrcResult = CrcContext.Crc_Value;
(void) CrcResult;
}
2.3 用C++类包装C语言CRC库
可以将现有的C语言CRC库用C++类进行包装,以实现面向对象的编程。以下是一个抽象基类
cpp_crc_base
的定义:
class cpp_crc_base
{
public:
virtual ~cpp_crc_base() = default;
void initialize();
void process_bytes(const std::uint8_t*, const std::size_t);
void finalize();
cpp_crc_base& operator=(const cpp_crc_base&);
template<typename value_type>
value_type get_result();
template<typename value_type>
value_type checksum(const std::uint8_t*, const std::size_t);
};
基于这个基类,可以派生出对应四种AUTOSAR CRC类型的类,例如64位版本的
cpp_crc64
:
class cpp_crc64 final : public cpp_crc_base
{
public:
cpp_crc64();
cpp_crc64(const cpp_crc64&);
virtual ~cpp_crc64() = default;
// ...
};
在应用层,可以使用基类指针容器来计算和测试8位、16位、32位和64位的校验和:
// Calculate and verify the 8-bit, 16-bit,
// 32-bit and 64-bit CRC.
std::for_each(checksums.begin(), checksums.end(), [](local_crc_base_type* my_crc)
{
// ...
my_crc->initialize();
my_crc->process_bytes(...);
my_crc->finalize();
// ...
});
3. CRC计算的效率和优化
对CRC计算进行CPU运行时间测量是很有必要的,可使用微控制器I/O端口和数字示波器进行测量。以下是不同CRC算法的代码大小和运行时间:
| CRC名称 | 代码大小(字节) | 运行时间(µs) |
| ---- | ---- | ---- |
| CRC8/AUTOSAR | 300 | 15 |
| CRC16/CCITT-false | 600 | 20 |
| CRC32/AUTOSAR | 1200 | 30 |
| CRC64/XZ | 2300 | 90 |
对比CRC32/AUTOSAR和CRC32/MPEG - 2的效率特性,虽然两者都是32位的CRC算法,但运行时间和代码大小差异很大:
| CRC名称 | 算法 | 代码大小(字节) | 运行时间(µs) |
| ---- | ---- | ---- | ---- |
| CRC32/AUTOSAR | Table[256], 8 - 位 | 1200 | 30 |
| CRC32/MPEG - 2 | Table[16], 4 - 位 | 320 | 300 |
这是因为CRC32/AUTOSAR使用了一个包含256个条目的更大数据表和8位逐字节的数据处理算法,而CRC32/MPEG - 2使用的是一个只有16个条目的数据表和基于4位半字节的数据处理算法。在8位微控制器上,使用更大的数据表和更快的逐字节算法虽然需要更多的存储空间,但能显著缩短运行时间。这是实时嵌入式C++编程中常见的空间与速度的权衡。
4. 相关知识拓展
对于想要深入学习C、现代C++编程、C++标准库和STL、C++编码规范、软件设计、嵌入式系统工具链以及微控制器软硬件等方面知识的读者,可以参考以下内容:
-
C语言
:C89、C99和C11版本的规范,以及原始C标准库的详细文档。
-
C++语言
:C++98、C++03、C++11、C++14和C++17的正式语言规范,C++核心语言、面向对象技术和有效使用STL的综合信息,C++标准库扩展(TR1)的详细描述等。
-
编程实践
:如何有效使用C++11和C++14的书籍,C++性能报告,STL容器和算法的深入介绍,C++模板和模板元编程的知识等。
-
硬件相关
:微控制器板设计、工具、启动、处理器架构和内存拓扑的讨论,电子学的广泛实用概览,ARDUINO®开源微控制器板项目的详细信息等。
通过对上述内容的学习和实践,开发者可以更好地掌握C++编程技术,提高编程效率和代码质量。
C++编程:从标准库扩展到C代码应用
5. 独立式实现的优势与应用场景
独立式实现因其对资源的高效利用,在特定领域展现出独特优势。以下是其优势和适用场景的详细分析:
-
优势
-
资源节省
:独立式实现支持较少的库函数和头文件,能有效减少程序的内存占用和代码大小,适合资源受限的设备。
-
灵活性
:可根据具体需求选择所需的库函数和头文件,定制化程度高。
-
应用场景
-
嵌入式系统
:在嵌入式微控制器编程中,设备的内存和处理能力有限,独立式实现能避免“重型”语言元素带来的开销,确保程序的高效运行。
-
实时系统
:对于对实时性要求较高的系统,独立式实现可减少不必要的开销,保证系统的响应速度。
下面通过一个简单的流程图展示独立式实现的应用流程:
graph TD;
A[开始] --> B[选择独立式实现];
B --> C[确定所需库函数和头文件];
C --> D[编写代码];
D --> E[编译和调试];
E --> F[部署到目标设备];
F --> G[运行程序];
G --> H[结束];
6. 在C++中使用C语言代码的最佳实践
在C++项目中使用C语言代码时,需要遵循一些最佳实践,以确保代码的正确性和可维护性。以下是一些建议:
-
使用
extern "C"
声明
:在C++文件中调用C语言函数时,使用
extern "C"
声明,避免名称修饰问题。
-
封装C语言代码
:将C语言代码封装在C++类中,实现面向对象的编程,提高代码的可维护性。
-
进行充分测试
:在使用C语言代码之前,进行充分的测试,确保其功能的正确性。
下面是一个使用
extern "C"
声明的示例:
// This is in a C++ file.
// Tell the C++ compiler about the C function.
extern "C" void FunctionInC(void);
void do_something()
{
// Now the C++ compiler can access the C function.
FunctionInC();
}
7. CRC计算的性能优化策略
为了提高CRC计算的性能,可以采用以下优化策略:
-
选择合适的算法
:根据具体需求选择合适的CRC算法,如CRC32/AUTOSAR使用的8位逐字节算法在运行时间上具有优势。
-
优化数据表
:使用更大的数据表可以提高计算速度,但会增加内存占用,需要在空间和速度之间进行权衡。
-
并行计算
:在多核处理器上,可以采用并行计算的方式提高CRC计算的效率。
以下是不同CRC算法的性能对比表格:
| CRC名称 | 代码大小(字节) | 运行时间(µs) | 算法特点 |
| ---- | ---- | ---- | ---- |
| CRC8/AUTOSAR | 300 | 15 | 简单高效,适用于对性能要求不高的场景 |
| CRC16/CCITT-false | 600 | 20 | 中等复杂度,性能适中 |
| CRC32/AUTOSAR | 1200 | 30 | 使用8位逐字节算法,运行时间短 |
| CRC64/XZ | 2300 | 90 | 复杂度高,适用于对数据完整性要求较高的场景 |
8. 总结
本文围绕C++编程的多个方面进行了介绍,包括C++标准库扩展、独立式实现、在C++中使用C语言代码、CRC计算的效率和优化等。通过对这些内容的学习,开发者可以更好地掌握C++编程技术,提高编程效率和代码质量。在实际应用中,需要根据具体需求选择合适的技术和算法,在资源利用和性能之间找到平衡。同时,不断学习和实践,积累经验,才能在C++编程领域取得更好的成果。
希望本文能为广大C++开发者提供有价值的参考,帮助他们在编程之路上不断前进。
超级会员免费看

被折叠的 条评论
为什么被折叠?



