如何使两个原本相同的指针类型不兼容 [英] How to make two otherwise identical pointer types incompatible

查看:109
本文介绍了如何使两个原本相同的指针类型不兼容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在某些体系结构上,对于其他相同的对象,可能需要具有不同的指针类型.特别是对于哈佛架构的CPU,您可能需要以下内容:

uint8_t const ram* data1;
uint8_t const rom* data2;

尤其是在PIC的MPLAB C18(现已停产)中,这是指向ROM/RAM的指针的样子.它甚至可以定义以下内容:

char const rom* ram* ram strdptr;

这意味着RAM中的指针指向RAM中指向ROM中的字符串的指针(不必使用ram,因为默认情况下,此编译器将其放在RAM中,为清楚起见仅添加了所有内容).

这种语法的好处是,当您尝试以不兼容的方式进行分配时,例如ROM位置的地址指向RAM的指针(例如data1 = data2;之类的东西,或者通过使用RAM指针指向函数的ROM指针会产生错误.)

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

在某些情况下,这种类型的安全性对于捕获编程错误非常有帮助.

是否有某种方法可以以某种方式(例如通过预处理程序,扩展到可以模仿此行为的方式)向指针添加类似的修饰符以实现此目的?甚至是警告不当访问的内容? (如果是avr-gcc,则尝试在不使用ROM访问功能的情况下获取值)

解决方案

由于我收到了多个答案,这些答案在提供解决方案时提供了不同的折衷方案,因此我决定将它们合并为一个,概述每个方案的优缺点.因此,您可以选择最适合您的特定情况的

命名地址空间

对于解决此问题的特定问题,只有在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;

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

(Olaf的原始答案)

结构封装,指针在内部

此方法旨在通过将指针包装在结构中来加强指针的键入.预期的用途是您将结构本身跨接口传递,编译器可以通过这些结构对它们进行类型检查.

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

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的原始答案)

结构封装,指针在外部

类似于上面的方法,它旨在通过将指针包装在结构中来加强指针的键入,但是以不同的方式提供了更强大的约束.要指向的数据类型是封装的.

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

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读取). /p>

  • 请记住,有些人可能不喜欢这两种方法,因此,如果您正在一个小组中工作,或者正在创建可能被已知方重用的代码,请首先与他们讨论此事.

  • 应该很明显,但是请记住,假设没有使用命名地址空间,封装并不能解决需要特殊访问代码的问题(例如AVR-8上的pgmspace.h宏).连同方法.如果您尝试通过在与地址所指向的地址空间不同的地址上运行的函数来使用指针,则它仅提供一种产生编译错误的方法.

感谢您的所有答案!

On certain architectures it may be necessary to have different pointer types for otherwise identical objects. Particularly for a Harvard architecture CPU, you may need something like:

uint8_t const ram* data1;
uint8_t const rom* data2;

Particularly this is how the definition of pointers to ROM / RAM looked like in MPLAB C18 (now discontinued) for PICs. It could define even things like:

char const rom* ram* ram strdptr;

Which means a pointer in RAM to pointers in RAM pointing to strings in ROM (using ram is not necessary as by default things are in RAM by this compiler, just added all for clarity).

The good thing in this syntax is that the compiler is capable to alert you when you try to assign in an incompatible manner, like the address of a ROM location to a pointer to RAM (so something like data1 = data2;, or passing a ROM pointer to a function using a RAM pointer would generate an error).

Contrary to this, in avr-gcc for the AVR-8, there is no such type safety as it rather provides functions to access ROM data. There is no way to distinguish a pointer to RAM from a pointer to ROM.

There are situations where this kind of type safety would be very beneficial to catch programming errors.

Is there some way to add similar modifiers to pointers in some manner (such as by preprocessor, expanding to something which could mimic this behavior) to serve this purpose? Or even something which warns on improper access? (in case of avr-gcc, trying to fetch values without using the ROM access functions)

解决方案

Since I received several answers which offer different compromises on providing a solution, I decided to merge them in one, outlining the benefits and drawbacks of each. So you can choose the most appropriate for your particular situation

Named Address Spaces

For the particular problem of solving this, and only this case of ROM and RAM pointers on an AVR-8 micro, the most appropriate solution is this.

This was a proposal for C11 which didn't make it into the final standard, however there are C compilers which support it, including avr-gcc used for 8 bit AVRs.

The related documentation can be accessed here (part of the online GCC manual, also including other architectures using this extension). It is recommendable over other solutions (such as function-like macros in pgmspace.h for the AVR-8) as with this, the compiler can make the appropriate checks, while otherwise accessing the data pointed by remains clear and simple.

In particular, if you have a similar problem of porting something from a compiler which offered some sort of named address spaces, like MPLAB C18, this is likely the fastest and cleanest way to do it.

The ported pointers from above would look like as follows:

uint8_t const* data1;
uint8_t const __flash* data2;
char const __flash** strdptr;

(If possible, one could simplify the process using appropriate preprocessor definitions)

(Original answer by Olaf)

Struct encapsulation, pointer inside

This method aims to strenghten typing of pointers by wrapping them in structures. The intended usage is that you pass the structures themselves across interfaces, by which the compiler can perform type checks on them.

A "pointer" type to byte data could look like this:

typedef struct{
    uint8_t* ptr;
}bytebuffer_ptr;

The pointed data can be accessed as follows:

bytebuffer_ptr bbuf;
(...)
bbuf.ptr = allocate_bbuf();
(...)
bbuf.ptr[index] = value;

A function prototype accepting such a type and returning one could look like as follows:

bytebuffer_ptr encode_buffer(bytebuffer_ptr inbuf, size_t len);

(Original answer by dvhh)

Struct encapsulation, pointer outside

Similar to the method above, it aims to strenghten typing of pointers by wrapping them in structures, but in a different manner, providing a more robust constraint. The data type to be pointed to is which is encapsulated.

A "pointer" type to byte data could look like this:

typedef struct{
    uint8_t val;
}byte_data;

The pointed data can be accessed as follows:

byte_data* bbuf;
(...)
bbuf = allocate_bbuf();
(...)
bbuf[index].val = value;

A function prototype accepting such a type and returning one could look like as follows:

byte_data* encode_buffer(byte_data* inbuf, size_t len);

(Original answer by Lundin)

Which should I use?

Named Address Spaces in this regard don't need much discussion: They are the most appropriate solution if you only want to deal with a pecularity of your target handling address spaces. The compiler will provide you the compile-time checks you need, and you don't have to try to invent anything further.

If, however for other reasons you are interested in structure wrapping, these are matters which you may want to consider:

  • Both methods can be optimized just fine: at least GCC will generate identical code from either to using plain pointers. So you don't really have to consider performance: they should work.

  • Pointer inside is useful if you have either third-party interfaces to serve which demand pointers, or maybe if you are refactoring something so large which you can't do in one pass.

  • Pointer outside provides more robust type safety as you reinforce the pointed type itself with it: you have a true distinct type which you can't easily (accidentally) convert (implicit cast).

  • Pointer outside allows you to use modifiers on the pointer, such as adding const, which is important for creating robust interfaces (you can make data intended to be read only by a function const).

  • Keep in mind that some people might not like either of these, so if you are working in a group, or are creating code which might be reused by known parties, discuss the matter with them first.

  • Should be obvious, but keep in mind that encapsulating doesn't solve the problem of requiring special access code (such as by the pgmspace.h macros on an AVR-8), assuming no Named Address Spaces are used alongside with the method. It only provides a method to produce a compile error if you try to use a pointer by functions operating on a different address space than what it intends to point into.

Thank you for all the answers!

这篇关于如何使两个原本相同的指针类型不兼容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆