什么是 C 中链表的忠实替代品?
What is a faithful alternative to a linked list in C?
这个问题可能太宽泛,或者观点有偏见,但我知道这个网站上有很多经验丰富的程序员,我认为它可能会鼓励进行很好的讨论。
我正在用 C 实现一个嵌入式应用程序,我在其中使用 链表 ,包含结构:
struct my
{
uint16_t x;
uint16_t y;
char *text;
struct my *next;
struct my *prev;
};
它总体上运行良好,但在项目中,我现在正在转向 MISRA-C 编程指南。 MISRA 禁止使用任何动态数据结构,因为它可能会在内存有限的嵌入式系统中导致未指定的行为。
我首先想到的当然是经典的静态数组结构,具有固定大小。这个结构的实例永远不会超过 30 个,而且我们仍然只使用了不到 5% 的可用内存,所以即使没有使用所有这些内存,也不会影响我们的程序性能。像这样:
extern struct my arr[30];
然而,这种方法也有一定的困难,例如,有时我需要从列表中删除一个元素,然后它会留下一个空元素,迫使我通过一个索引重写所有其他元素。
有没有什么干净、优雅的方法可以在不使用链表的情况下实现类似于链表的功能?
您可以在静态数组上使用链表。唯一的区别是,您使用的不是指向动态分配的内存块的指针,而是指向数组内元素的指针。如果您愿意,可以只使用数组索引而不是指针。
当然,实现会有一些不同:
- 添加或删除元素时不必(取消)分配内存
- 相反,您必须自己管理空闲槽,使用另一种结构,例如队列甚至另一个空元素列表
您可以使用数组索引代替指针。然后,您可以构建链接列表并使用索引值来访问下一个或上一个小部件。
您可以使用另一个 bool
数组来标记使用了哪些条目。
有很多现有的 API 提供这种在编译时为最大数量的实体保留内存的功能,并提供分配和释放它们的函数。有关示例,请参阅 Contiki memb
API and implementation。
有很多方法可以实现这一点,但这里有一个:您可以创建一个 struct widget
数组 + 一个布尔值数组来标记它们是否空闲。然后你可以用找到第一个 struct widget
可用的函数替换你的 malloc。这类似于各种内核中的 slab 分配器。
最好使用静态数组方法,而不是指针,您可以访问需要访问的元素的索引。
虽然您会遇到一些常见问题,例如;
- 数组固定长度(最大限制需自行决定)
- 使用内存的数组不会被用于其他用途。
- 它和指针有同样的问题。
优点是;
- 知道位置后更简单/更快,
- 问题检测会很容易,因为内存分配会很方便。
- 列表中的条目更有可能(在内存中)彼此更接近(与 malloc() 不同,在 malloc() 中,您的条目分散在您分配的所有其他内容中),这可以提高性能(缓存位置)。
这个问题可能太宽泛,或者观点有偏见,但我知道这个网站上有很多经验丰富的程序员,我认为它可能会鼓励进行很好的讨论。
我正在用 C 实现一个嵌入式应用程序,我在其中使用 链表 ,包含结构:
struct my
{
uint16_t x;
uint16_t y;
char *text;
struct my *next;
struct my *prev;
};
它总体上运行良好,但在项目中,我现在正在转向 MISRA-C 编程指南。 MISRA 禁止使用任何动态数据结构,因为它可能会在内存有限的嵌入式系统中导致未指定的行为。
我首先想到的当然是经典的静态数组结构,具有固定大小。这个结构的实例永远不会超过 30 个,而且我们仍然只使用了不到 5% 的可用内存,所以即使没有使用所有这些内存,也不会影响我们的程序性能。像这样:
extern struct my arr[30];
然而,这种方法也有一定的困难,例如,有时我需要从列表中删除一个元素,然后它会留下一个空元素,迫使我通过一个索引重写所有其他元素。
有没有什么干净、优雅的方法可以在不使用链表的情况下实现类似于链表的功能?
您可以在静态数组上使用链表。唯一的区别是,您使用的不是指向动态分配的内存块的指针,而是指向数组内元素的指针。如果您愿意,可以只使用数组索引而不是指针。 当然,实现会有一些不同: - 添加或删除元素时不必(取消)分配内存 - 相反,您必须自己管理空闲槽,使用另一种结构,例如队列甚至另一个空元素列表
您可以使用数组索引代替指针。然后,您可以构建链接列表并使用索引值来访问下一个或上一个小部件。
您可以使用另一个 bool
数组来标记使用了哪些条目。
有很多现有的 API 提供这种在编译时为最大数量的实体保留内存的功能,并提供分配和释放它们的函数。有关示例,请参阅 Contiki memb
API and implementation。
有很多方法可以实现这一点,但这里有一个:您可以创建一个 struct widget
数组 + 一个布尔值数组来标记它们是否空闲。然后你可以用找到第一个 struct widget
可用的函数替换你的 malloc。这类似于各种内核中的 slab 分配器。
最好使用静态数组方法,而不是指针,您可以访问需要访问的元素的索引。
虽然您会遇到一些常见问题,例如;
- 数组固定长度(最大限制需自行决定)
- 使用内存的数组不会被用于其他用途。
- 它和指针有同样的问题。
优点是;
- 知道位置后更简单/更快,
- 问题检测会很容易,因为内存分配会很方便。
- 列表中的条目更有可能(在内存中)彼此更接近(与 malloc() 不同,在 malloc() 中,您的条目分散在您分配的所有其他内容中),这可以提高性能(缓存位置)。