我正在为一个嵌入式系统编写软件。
我们使用指针来访问FPGA设备的寄存器。
其中一些寄存器是只读的,而另一些是只写的。
当读取只写寄存器时会产生未定义的值。
我希望定义一个指针类型,使得编译器能够检测到从只写寄存器(也称解除引用)中读取值的操作。
是否可以仅使用C语言语法创建只写指针?
(我们使用C开发第一代原型,但在第二代将转向C++。)
如何在C++中创建一个有效的只写指针? (请记住,这不是跟踪动态内存中的项,而是访问硬件地址。)
在这个嵌入式系统上,安全和质量是最高关注点。
我正在为一个嵌入式系统编写软件。
我们使用指针来访问FPGA设备的寄存器。
其中一些寄存器是只读的,而另一些是只写的。
当读取只写寄存器时会产生未定义的值。
我希望定义一个指针类型,使得编译器能够检测到从只写寄存器(也称解除引用)中读取值的操作。
是否可以仅使用C语言语法创建只写指针?
(我们使用C开发第一代原型,但在第二代将转向C++。)
如何在C++中创建一个有效的只写指针? (请记住,这不是跟踪动态内存中的项,而是访问硬件地址。)
在这个嵌入式系统上,安全和质量是最高关注点。
我可能会为每个类编写一个小包装器类:
template <class T>
class read_only {
T volatile *addr;
public:
read_only(int address) : addr((T *)address) {}
operator T() volatile const { return *addr; }
};
template <class T>
class write_only {
T volatile *addr;
public:
write_only(int address) : addr ((T *)address) {}
// chaining not allowed since it's write only.
void operator=(T const &t) volatile { *addr = t; }
};
假设您的系统有一个合理的编译器,我认为这两者都将被优化,使生成的代码与使用原始指针无异。用法:
read_only<unsigned char> x(0x1234);
write_only<unsigned char> y(0x1235);
y = x + 1; // No problem
x = y; // won't compile
int
而不是 volatile T*
?如果你真的想要一个整数,那么可以使用 intptr_t
。此外,operator T()
可以是 const volatile
。 - Jon PurdyT volatile*
,因为这意味着用户将拥有一个读/写指向寄存器的指针——这正是我们想要避免的。嵌入式编译器通常受到一定限制,因此期望它们包含intptr_t
(在C++11中刚刚添加)是要求较高的。如果必须的话,我会将其作为模板参数。我同意使用const volatile
,已经进行了编辑,谢谢。 - Jerry Coffinvolatile T *
能得到什么好处。我想,如果你愿意,可以创建一些东西来避免显式引用,但与这些模板的关系纯粹是...精神上的--它们都有operator T
和operator =
来进行读写。 - Jerry Coffinfpga_register.h
的文件中,您可以添加以下内容:#define FPGA_READ = 1;
#define FPGA_WRITE = 2;
typedef struct register_t {
char permissions;
} FPGARegister;
FPGARegister* fpga_init(void* address, char permissions);
int fpga_write(FPGARegister* register, void* value);
int fpga_read(FPGARegister* register, void* value);
使用异或运算符来表示权限中的READ和WRITE。
接下来,在fpga_register.c
中,您需要定义一个新的结构体。
typedef struct register_t2 {
char permissions;
void * address;
} FPGARegisterReal;
所以你需要返回指向它的指针,而不是在fpga_init
中返回指向FPGARegister
的指针。
接下来,在fpga_read
和fpga_write
中,你需要检查权限并且:
FPGARegister
强制转换回FPGARegisterReal
,执行所需操作(设置或读取值),并返回成功代码这样,包括头文件在内的任何人都无法访问FPGARegisterReal
结构体,因此它将无法直接访问寄存器地址。显然,有人可能会进行黑客攻击,但我相信这种故意的黑客攻击不是你真正关心的问题。
在C语言中,您可以使用不完整类型的指针来防止所有的解引用:
/* writeonly.h */
typedef struct writeonly *wo_ptr_t;
/* writeonly.c */
#include "writeonly.h"
struct writeonly {
int value
};
/*...*/
FOO_REGISTER->value = 42;
/* someother.c */
#include "writeonly.h"
/*...*/
int x = FOO_REGISTER->value; /* error: deref'ing pointer to incomplete type */
只有writeonly.c
,或者一般来说任何具有定义struct writeonly
的代码,才能解除指针引用。当然,这段代码也可能意外地读取该值,但至少所有其他代码都被阻止了完全解除指针引用,同时能够传递这些指针并将它们存储在变量、数组和结构中。
writeonly.[ch]
可以提供一个写入值的函数。
我认为在C语言中没有一种优雅的方式来实现它。不过,我确实看到了一种方法:
#define DEREF_PTR(type, ptr) type ptr; \
typedef char ptr ## _DEREF_PTR;
#define NO_DEREF_PTR(type, ptr) type ptr; \
#define DEREFERENCE(ptr) \
*ptr; \
{ptr ## _DEREF_PTR \
attempt_to_dereference_pointer_ ## ptr;}
int main(int argc, char *argv[]) {
DEREF_PTR(int*, x)
NO_DEREF_PTR(int*, y);
DEREFERENCE(x);
DEREFERENCE(y); // will throw an error
}
这种方法的好处在于可以提供静态错误检查。当然,使用这种方法,你需要修改所有指针声明以使用宏,这可能不是一件很有趣的事情。
编辑: 如评论中所述。
#define READABLE_PTR(type, ptr) type ptr; \
typedef char ptr ## _READABLE_PTR;
#define NON_READABLE_PTR(type, ptr) type ptr; \
#define GET(ptr) \
*ptr; \
{ptr ## _READABLE_PTR \
attempt_to_dereference_non_readable_pointer_ ## ptr;}
#define SET(ptr, value) \
*ptr = value;
int main(int argc, char *argv[]) {
READABLE_PTR(int*, x)
NON_READABLE_PTR(int*, y);
SET(x, 1);
SET(y, 1);
int foo = GET(x);
int bar = GET(y); // error
}
x
(DEREF_PTR(int*, x)
),预处理器还会在幕后定义一个名为x_DEREF_PTR
的类型。当调用DEREFERENCE
时,它会在单独的作用域中实例化此类型的变量。对于使用NO_DEREF_PTR
定义的指针,该类型不存在,因此会抛出错误。 - Martin Svanbergclass write_only_T{
public:
write_only_T(){}
write_only_T(T const& v) : m(v){}
write_only_T(T&& v) : m(std::move(v)){}
write_only_T& operator=(T const& v){
m = v;
return *this;
}
write_only_T& operator=(T&& v){
m = std::move(v);
return *this;
}
write_only_T(write_only_T const&) = delete;
write_only_T(write_only_T&&) = delete;
write_only_T& operator=(write_only_T const&) = delete;
write_only_T& operator=(write_only_T&&) = delete;
private:
T m;
};
我认为如果你使用了这个,你不需要一个特殊的指针类型,因为只写是值的属性,但我可以想象一个合成的指针类型,跳过值类型。 很可能你需要引入一个只写的引用类型等。
int
而不是std::string
一样需要纪律:实际上没有什么区别。一旦你声明了适当类型的变量,编译器会确保你不会错误地使用它们。 - Konrad Rudolph