提问人:deppep 提问时间:2/8/2023 最后编辑:deppep 更新时间:2/8/2023 访问量:285
队列的静态初始化
static initialization of queue
问:
我正在研究嵌入式系统算法的高依赖性实现。
在: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.不得不不知道;和 2.不使用 malloc 进行初始化,而是静态地进行。main.c
Queue
queue_buffer_
这意味着理想情况下是:main.c
//.. in some function
Queue *queue = queue_init(something_eventually);
do_things_with_queue(queue);
//.. in some function
是否可以在 C99 中进行修改以实现此目的?如果是这样,最好的方法是什么?queue_init
queue.c
暂定解决方案
我知道这篇文章中讨论的技术,但如果不使用 .我确信我最多会同时有 4 个队列。这让我想到我可以将队列的内存池声明为大小为 4 的队列的静态全局数组。在这种情况下可以使用全局变量吗?malloc
@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
谢谢。
答:
我通常会做:
// queue.h
#define QUEUE_INIT(data, len) { .len = len, .data = data }
#define QUEUE_INIT_ON_STACK(len) QUEUE_INIT((char[len]){0}, len)
// main.c
static Queue queue = QUEUE_INIT_ON_STACK(QUEUE_LEN + 1);
至于 PIMPL 习语,它很容易用描述符实现,就像 LINUX 中的文件描述符一样,尤其是当计数是静态的时。
// queue.h
typedef Queue int;
void do_things_with_queue(Queue);
// queue.c
struct RealQueue { stuff; };
static struct RealQeueue arr[4] = { stuff };
static struct RealQeueue *get_RealQueue(Queue i) {
assert(0 <= i && i < sizeof(arr)/sizeof(*arr));
return &arr[i];
}
void do_things_with_queue(Queue i) {
struct RealQueue *queue = get_RealQueue(i);
}
// main.c
static Queue queue = 1;
// etc.
或者,您可以打破所有地狱并同步源文件和头文件之间的对齐方式:
// queue.h
struct Queue {
// This has to be adjusted __for each compiler and environment__
alignas(60) char data[123];
};
#define QUEUE_INIT() { 0xAA, 0xBB, etc.. constant precomputed data }
// queue.c
struct RealQeueue { stuff; };
static_assert(alingof(struct RealQueue) == alignof(struct Queue), "");
static_assert(sizeof(struct RealQueue) == sizeof(struct Queue), "");
void do_things_with_queue(Queue *i) {
struct RealQueue *queue = (struct RealQueue*)i->data;
}
执行此操作的最佳方法是将缓冲区及其大小传递给 init 函数,就像您已经拥有的那样。
担心调用函数而不是在编译时修复数据是一个非常糟糕的主意。像这样的微小初始化的执行时间和代码大小都可以忽略不计。仅仅为了在启动时保存一些指令而使代码界面变得笨拙,这不仅是浪费精力,还会使代码难以维护,并有引入错误的风险。
有许多嵌入式系统或库提供了一个宏,该宏一次性声明存储阵列和控制结构,并为其指定一个只有库知道的名称,然后每次访问项目时都必须使用宏生成名称。例如,您可以查看 CMSIS-OS 中的 osMailQDef。不过,我真的不推荐这种方法。它太容易出错了,而以通常的方式做很容易阅读,任何审稿人都能立即发现错误。
评论
我想要 1.不得不不知道;和 2.不用于初始化,而是静态地进行。
main.c
Queue
malloc
queue_buffer_
这意味着理想情况下,main.c 将是:
//.. in some function Queue queue = queue_init(something_eventually); do_things_with_queue(&queue); //.. in some function
不,您的目标并不意味着所描述的解决方案。不能在类型定义不可见的任何位置声明或使用该类型的对象。这直接来自语言的规则,但如果你想要一个更有意义的理由,那么考虑到即使没有访问 的任何成员,它仍然需要定义来知道要为一个保留多少空间。Queue
main
Queue
对我来说,使类型不透明(或任何地方)是否有用并不明显,但如果这是您想要的,那么在该范围内,您可以转发声明它,从不定义它,并且只使用指向它的指针:Queue
main.c
typedef struct queue Queue;
// ...
Queue *queue = queue_init(something_eventually);
do_things_with_queue(queue);
要使它在没有动态内存分配的情况下工作,指向的对象必须具有静态存储持续时间,但这并不意味着它们必须是全局的 - 无论是在通过具有外部链接的名称访问的意义上,还是在文件范围内声明的意义上。Queue
此外,您还可以选择自动分配数组,如示例代码中所示,以便在不使用内存时不会将内存占用在队列中。如果您愿意,可以将其包含在一两个宏中,以增加易用性(作为练习)。data
例如,
queue.h
typedef struct queue Queue;
Queue *queue_init(int queue_size, int queue_data[]);
void queue_release(Queue *queue);
queue.c
#include "queue.h"
struct queue {
int head;
int tail;
int len;
int *data;
};
Queue *queue_init(int queue_len, int queue_data[]) {
// queue_pool has static storage duration and no linkage
static Queue queue_pool[4] = {{0}};
// Find an available Queue, judging by the data pointers
for (Queue *queue = queue_pool;
queue < queue_pool + sizeof(queue_pool) / sizeof(*queue_pool);
queue++) {
if (queue->data == NULL) {
// This one will do. Initialize it and return a pointer to it.
queue->head = 0;
queue->tail = 0;
queue->len = queue_len;
queue->data = queue_data;
return queue;
}
}
// no available Queue
return NULL;
}
void queue_release(Queue *queue) {
if (queue) {
queue->data = NULL;
}
}
main.c
// ... in some function
int queue_data[SOME_QUEUE_LENGTH];
Queue *queue = queue_init(SOME_QUEUE_LENGTH, queue_data);
do_things_with_queue(queue);
queue_release(queue);
// ...
当然,如果您愿意,可以将队列数据直接放入队列结构中,就像在暂定解决方案中一样,并且可以在那里提供一个标志来指示队列当前是否正在使用中。这将减轻用户提供存储的任何需求,但代价是在整个程序期间占用所有队列的所有元素的存储。
评论
main.c
Queue *
Queue
Queue
Queue
static
static
评论
Queue
queue.c
Queue queue = queue_init
not use malloc for intializing queue_buffer_
but rather do it statically