由于我收到了几个答案,它们在提供解决方案时提供了不同的折衷方案,因此我决定将它们合并为一个,概述每个答案的优缺点。因此,您可以选择最适合您的特定情况的
命名地址空间
对于解决这个问题的特殊问题,只有 AVR-8 微控制器上的 ROM 和 RAM 指针这种情况,最合适的解决方案是这个。
这是一个针对 C11 的提案,它没有成为最终标准,但是有 C 编译器支持它,包括用于 8 位 AVR 的 avr-gcc。
可以在此处访问相关文档(在线 GCC 手册的一部分,还包括使用此扩展的其他架构)。它优于其他解决方案(例如 AVR-8 的 pgmspace.h 中的类似函数的宏),因为这样,编译器可以进行适当的检查,否则访问指向的数据仍然清晰和简单。
特别是,如果您在从提供某种命名地址空间的编译器(如 MPLAB C18)中移植某些东西时遇到类似的问题,这可能是最快和最干净的方法。
从上面移植的指针如下所示:
uint8_t const* data1;
uint8_t const __flash* data2;
char const __flash** strdptr;
(如果可能,可以使用适当的预处理器定义来简化该过程)
(奥拉夫的原始答案)
struct封装,里面有指针
此方法旨在通过将指针包装在结构中来增强指针的类型。预期用途是跨接口传递结构本身,编译器可以通过这些接口对它们执行类型检查。
指向字节数据的“指针”类型可能如下所示:
typedef struct{
uint8_t* ptr;
}bytebuffer_ptr;
可以按如下方式访问指向的数据:
bytebuffer_ptr bbuf;
(...)
bbuf.ptr = allocate_bbuf();
(...)
bbuf.ptr[index] = value;
接受这种类型并返回一个的函数原型可能如下所示:
bytebuffer_ptr encode_buffer(bytebuffer_ptr inbuf, size_t len);
(dvhh的原始答案)
struct封装,指针外接
与上面的方法类似,它旨在通过将指针包装在结构中来加强指针的类型化,但以不同的方式,提供更健壮的约束。要指向的数据类型是封装的。
指向字节数据的“指针”类型可能如下所示:
typedef struct{
uint8_t val;
}byte_data;
可以按如下方式访问指向的数据:
byte_data* bbuf;
(...)
bbuf = allocate_bbuf();
(...)
bbuf[index].val = value;
接受这种类型并返回一个的函数原型可能如下所示:
byte_data* encode_buffer(byte_data* inbuf, size_t len);
(伦丁的原始答案)
我应该使用哪个?
在这方面,命名地址空间不需要太多讨论:如果您只想处理目标处理地址空间的特殊性,它们是最合适的解决方案。编译器将为您提供所需的编译时检查,您不必尝试进一步发明任何东西。
但是,如果由于其他原因您对结构包装感兴趣,那么您可能需要考虑以下事项:
两种方法都可以优化得很好:至少 GCC 会从使用普通指针生成相同的代码。因此,您实际上不必考虑性能:它们应该可以工作。
如果您有第三方接口来服务哪些需求指针,或者如果您正在重构无法一次性完成的大项目,则内部指针很有用。
外部指针提供了更强大的类型安全性,因为您使用它来加强指向类型本身:您拥有一个真正的独特类型,您不能轻易(意外地)转换(隐式转换)。
外部指针允许您在指针上使用修饰符,例如添加const
,这对于创建健壮的接口很重要(您可以使数据仅供函数读取const
)。
请记住,有些人可能不喜欢其中任何一个,所以如果您在一个小组中工作,或者正在创建可能被已知方重用的代码,请先与他们讨论此事。
应该很明显,但请记住,假设没有与方法一起使用命名地址空间,封装并不能解决需要特殊访问代码(例如通过 AVR-8 上的 pgmspace.h 宏)的问题。它仅提供一种方法来产生编译错误,如果您尝试通过在与它打算指向的地址空间不同的地址空间上操作的函数使用指针。
谢谢大家的回答!