C语言 队列静态初始化

6pp0gazn  于 2023-02-11  发布在  其他
关注(0)|答案(3)|浏览(128)

我正在为一个嵌入式系统设计一个高可靠性的算法。
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来初始化这个结构。
谢谢你。

ckocjqey

ckocjqey1#

我想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的定义不可见的任何地方声明或使用该类型的对象。这直接遵循语言的规则,但如果您想要更有意义的理由,请考虑即使main不访问Queue的任何成员,它仍然需要定义来简单地知道为一个保留多少空间。
main.c(或任何地方)中使类型Queue不透明对我来说不是很明显,但如果这是你想要的,那么在那个作用域中你可以向前声明它,从不定义它,只使用指向它的指针:

typedef struct queue Queue;

// ...

    Queue *queue = queue_init(something_eventually);
    do_things_with_queue(queue);

为了在没有动态内存分配的情况下工作,所指向的Queue对象必须具有静态存储持续时间,但这并不意味着它们必须是全局的--无论是在通过具有外部链接的名称可访问的意义上,还是在文件范围内声明的意义上。
此外,您可以选择自动分配data数组,如示例代码所示,这样就不会在队列不使用时占用内存。如果愿意,可以将其封装在一两个宏中,以增加易用性(作为练习)。
例如,

    • 队列. h**
typedef struct queue Queue;

Queue *queue_init(int queue_size, int queue_data[]);
void queue_release(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;
    }
}
    • 主要. 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);

    // ...

当然,如果您愿意,也可以将队列数据直接放入队列结构中,就像您的尝试性解决方案一样,并且可以在那里提供一个标志来指示队列当前是否正在使用中,这将减轻用户提供存储的任何需要,代价是在整个程序期间占用所有队列的所有元素的存储。

euoag5mw

euoag5mw2#

最好的方法是将缓冲区及其大小传递给init函数,就像您已经做的那样。
担心调用函数而不是在编译时修复数据是一个非常糟糕的主意。像这样的小型初始化的执行时间和代码大小都可以忽略不计。仅仅为了在启动时节省一些指令就让代码界面变得笨拙不仅仅是浪费精力,还会使代码难以维护,并有引入bug的风险。
有许多嵌入式系统或库提供宏,该宏一次声明存储阵列和控制结构,并给它们一个只有库知道的名称,然后你必须使用一个宏来生成名字,你可以在CMSIS-OS中查看osMailQDef的例子,但是我并不推荐这种方法。它太容易出错了,而用通常的方法做则很容易阅读,任何审阅者都能马上发现错误。

js5cn81o

js5cn81o3#

我通常会这样做:

// 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;
}

相关问题