首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使两个其他相同的指针类型不兼容

如何使两个其他相同的指针类型不兼容
EN

Stack Overflow用户
提问于 2018-04-09 12:39:10
回答 4查看 529关注 0票数 9

在某些体系结构上,对于其他相同的对象,可能需要有不同的指针类型。特别是对于哈佛架构CPU,您可能需要这样的东西:

代码语言:javascript
复制
uint8_t const ram* data1;
uint8_t const rom* data2;

特别是这是在MPLAB C18 (现在停止使用)中对ROM / RAM指针的定义。它甚至可以定义如下:

代码语言:javascript
复制
char const rom* ram* ram strdptr;

这意味着在RAM中有一个指针指向ROM中的字符串(使用ram是不必要的,因为在默认情况下,这个编译器会在RAM中添加所有内容,只是为了清晰起见)。

这种语法的好处是,当您试图以不兼容的方式分配ROM位置时,编译器能够提醒您,例如将ROM位置的地址分配给指向RAM的指针(因此类似于data1 = data2;,或者使用RAM指针将ROM指针传递给函数将产生错误)。

与此相反,在AVR的AVR-8中,没有这种类型的安全性,因为它提供访问ROM数据的功能。无法区分指向RAM的指针和指向ROM的指针。

在某些情况下,这种类型的安全性将非常有利于捕获编程错误。

是否有办法以某种方式向指针添加类似的修饰符(例如,通过预处理器,扩展到可以模仿这种行为的东西)来达到这个目的?甚至是对不适当的访问发出警告的东西?(在avr-gcc的情况下,尝试在不使用ROM访问功能的情况下获取值)

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-04-23 12:34:11

由于我收到了几个关于提供解决方案的不同妥协的答案,所以我决定将它们合并在一起,概述每种解决方案的优缺点。所以你可以选择最适合你特定情况的

命名地址空间

对于解决这个问题的特殊问题,并且只有在AVR-8微型上的ROM和RAM指针的情况下,最合适的解决方案是这个。

这是一个C11的提议,它没有进入最终的标准,但是有一些C编译器支持它,包括avr用于8位avr。

相关文档可以访问这里 (在线GCC手册的一部分,还包括使用此扩展的其他体系结构)。与其他解决方案(如pgmspace.h中的函数类宏用于AVR-8)相比,它是值得推荐的,因此编译器可以进行适当的检查,同时访问由保留明确和简单的数据所指向的数据。

特别是,如果您有类似的问题从提供某种命名地址空间的编译器移植东西,比如MPLAB C18,这可能是最快和最干净的方法。

上面移植的指针如下所示:

代码语言:javascript
复制
uint8_t const* data1;
uint8_t const __flash* data2;
char const __flash** strdptr;

(如果可能的话,可以使用适当的预处理定义来简化过程)

(Olaf的原答覆)

结构封装,内部指针

这种方法的目的是通过将指针封装在结构中来加强指针的分型。其目的是通过接口传递结构本身,通过接口编译器可以对它们执行类型检查。

指向字节数据的“指针”类型可能如下所示:

代码语言:javascript
复制
typedef struct{
    uint8_t* ptr;
}bytebuffer_ptr;

可按以下方式访问指定的数据:

代码语言:javascript
复制
bytebuffer_ptr bbuf;
(...)
bbuf.ptr = allocate_bbuf();
(...)
bbuf.ptr[index] = value;

接受这种类型并返回该类型的函数原型如下所示:

代码语言:javascript
复制
bytebuffer_ptr encode_buffer(bytebuffer_ptr inbuf, size_t len);

(dvhh的原答覆)

结构封装,外部指针

类似于上面的方法,它的目的是通过将指针封装在结构中来加强指针的类型,但是以一种不同的方式提供了一个更健壮的约束。要指向的数据类型是封装的。

指向字节数据的“指针”类型可能如下所示:

代码语言:javascript
复制
typedef struct{
    uint8_t val;
}byte_data;

可按以下方式访问指定的数据:

代码语言:javascript
复制
byte_data* bbuf;
(...)
bbuf = allocate_bbuf();
(...)
bbuf[index].val = value;

接受这种类型并返回该类型的函数原型如下所示:

代码语言:javascript
复制
byte_data* encode_buffer(byte_data* inbuf, size_t len);

(隆丁的原答覆)

我应该使用哪个

这方面的命名地址空间不需要太多的讨论:如果您只想处理目标处理地址空间的一个频谱,那么它们是最合适的解决方案。编译器将为您提供所需的编译时检查,您不需要进一步发明任何东西。

但是,如果出于其他原因,您对结构包装感兴趣,则可能需要考虑以下事项:

  • 这两种方法都可以很好地优化:至少GCC将生成从两种方法到使用普通指针的相同代码。因此,你不必真正考虑性能:它们应该能工作。
  • 如果您有需要指针的第三方接口,或者如果您重构的东西太大,不能一次完成,那么内部的指针是非常有用的。
  • 指针外部提供了更健壮的类型安全性,因为您用它来增强指定类型本身:您有一个真正的不同类型,您很难(意外地)转换(隐式强制转换)。
  • 指针外部允许在指针上使用修饰符,例如添加const,这对于创建健壮的接口非常重要(您可以使数据仅供函数const读取)。
  • 请记住,有些人可能不喜欢这两种方法,所以如果您在一个小组中工作,或者正在创建可能被已知各方重用的代码,请先与他们讨论这个问题。
  • 应该是显而易见的,但请记住,封装并不能解决需要特殊访问代码的问题(例如,在AVR-8上使用pgmspace.h宏),前提是在该方法的同时不使用任何命名的地址空间。它只提供一种方法来产生编译错误,如果您试图使用指针的不同地址空间上操作的功能,而不是它想要指向的。

谢谢你所有的答案!

票数 2
EN

Stack Overflow用户

发布于 2018-04-09 12:59:00

一个窍门是将指针包装在一个结构中。指向struct的指针比指向基本数据类型的指针具有更好的类型安全性。

代码语言:javascript
复制
typedef struct
{
  uint8_t ptr;
} a_t;

typedef struct
{
  uint8_t ptr;
} b_t;

const volatile a_t* a = (const volatile a_t*)0x1234;
const volatile b_t* b = (const volatile b_t*)0x5678;

a = b; // compiler error
b = a; // compiler error
票数 7
EN

Stack Overflow用户

发布于 2018-04-09 12:57:39

对于RAM和ROM,可以将指针封装在不同的结构中,使类型不兼容,但包含相同类型的值。

代码语言:javascript
复制
struct romPtr {
    void *addr;
};

struct ramPtr {
    void *addr;
};

int main(int argc, char **argv) {
    struct romPtr data1 = {NULL};
    struct romPtr data3 = data1;
    struct ramPtr data2 = data1; // <-- gcc would throw a compilation error here
}

在汇编期间:

代码语言:javascript
复制
$ cc struct_test.c
struct_test.c: In function ‘main’:
struct_test.c:12:24: error: invalid initializer
  struct ramPtr data2 = data1;
                    ^~~~~

当然,为了简洁起见,可以使用typedef的结构。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49733195

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档