我正在为一个嵌入式系统设计一个高可靠性的算法。
在main.c
中:
//.. in main()
int queue_buffer[QUEUE_LEN + 1] = { 0 };
Queue queue;
queue_init(&queue, QUEUE_LEN, queue_buffer);
do_things_on_queue(&queue);
//.. in main()
在queue.c
中:
void queue_init(Queue *q, int len, int *data) {
q->head = 0;
q->tail = 0;
q->len = len;
q->data = data; // an array of length `len + 1`
}
在queue.h
中:
typedef struct queue {
int head;
int tail;
int len;
int *data;
} Queue;
我想1。有main.c
不知道Queue
;以及2. * 不 * 使用malloc来初始化queue_buffer_
,而是静态地进行。
这意味着理想的main.c
将是:
//.. in some function
Queue *queue = queue_init(something_eventually);
do_things_with_queue(queue);
//.. in some function
是否可以在queue.c
中修改queue_init
以在C99中实现这一点?如果可以,最佳方法是什么?
暂定溶液
我知道在post中讨论的技术,但是如果不使用malloc
,它们似乎是不可行的。我确信我最多将同时拥有4个队列。这使我想到我可以将队列的内存池声明为大小为4的静态全局队列数组。在这种情况下使用全局变量可以吗?
@KamilKuk建议只使用queue_init
返回结构本身,因为QUEUE_LEN
在编译时是已知的。
在queue.c
中:
Queue queue_init(void) {
Queue q;
q.head = 0;
q.tail = 0;
q.len = QUEUE_LEN;
for (int i=0; i < QUEUE_LEN; i++)
q.data[i] = 0;
return q;
}
在queue.h
中:
typedef struct queue {
int head;
int tail;
int len;
int data[QUEUE_LEN];
} Queue;
Queue queue_init(void);
这似乎大大简化了结构的初始化,但是这并没有解决隐私问题,因为main.c
应该知道Queue
来初始化这个结构。
谢谢你。
3条答案
按热度按时间ckocjqey1#
我想1。有
main.c
不知道Queue
;以及2.不使用malloc
来初始化queue_buffer_
,而是静态地进行初始化。这意味着理想的main. c将是:
不,您的目标 * 不 * 暗示所描述的解决方案。您不能在类型
Queue
的定义不可见的任何地方声明或使用该类型的对象。这直接遵循语言的规则,但如果您想要更有意义的理由,请考虑即使main
不访问Queue
的任何成员,它仍然需要定义来简单地知道为一个保留多少空间。在
main.c
(或任何地方)中使类型Queue
不透明对我来说不是很明显,但如果这是你想要的,那么在那个作用域中你可以向前声明它,从不定义它,只使用指向它的指针:为了在没有动态内存分配的情况下工作,所指向的
Queue
对象必须具有静态存储持续时间,但这并不意味着它们必须是全局的--无论是在通过具有外部链接的名称可访问的意义上,还是在文件范围内声明的意义上。此外,您可以选择自动分配
data
数组,如示例代码所示,这样就不会在队列不使用时占用内存。如果愿意,可以将其封装在一两个宏中,以增加易用性(作为练习)。例如,
当然,如果您愿意,也可以将队列数据直接放入队列结构中,就像您的尝试性解决方案一样,并且可以在那里提供一个标志来指示队列当前是否正在使用中,这将减轻用户提供存储的任何需要,代价是在整个程序期间占用所有队列的所有元素的存储。
euoag5mw2#
最好的方法是将缓冲区及其大小传递给init函数,就像您已经做的那样。
担心调用函数而不是在编译时修复数据是一个非常糟糕的主意。像这样的小型初始化的执行时间和代码大小都可以忽略不计。仅仅为了在启动时节省一些指令就让代码界面变得笨拙不仅仅是浪费精力,还会使代码难以维护,并有引入bug的风险。
有许多嵌入式系统或库提供宏,该宏一次声明存储阵列和控制结构,并给它们一个只有库知道的名称,然后你必须使用一个宏来生成名字,你可以在CMSIS-OS中查看osMailQDef的例子,但是我并不推荐这种方法。它太容易出错了,而用通常的方法做则很容易阅读,任何审阅者都能马上发现错误。
js5cn81o3#
我通常会这样做:
至于PIMPL习惯用法,它很容易用描述符实现,就像LINUX中的文件描述符一样,特别是当计数是静态的时候。
或者你可以打破所有地狱和同步对齐源文件和头文件: