2012-08-01 62 views
2

考虑一个表示单向链表中的条目的C结构。它包含一个指向一些任意的数据,该数据的大小和方式找到下一个入口在编译时挂钩链表

其次,考虑条目下面收集和数据,他们表示:

unsigned char dataA[3]; 
unsigned char dataB[16]; 
unsigned char dataC[17]; 

Entry entryA = {dataA, sizeof(dataA), 3}; //It's important for "3" to match up with the index of entryB once it's put into MasterList below. 
Entry entryB = {dataB, sizeof(dataB), 4}; //Likewise 
Entry entryC = {dataC, sizeof(dataC), 0}; //0 terminates the linked list 
Entry emptyEntry = {(void*)0, 0, 0}; 

Entry MasterList[8] = { 
entryA,  //Index 0 - Contains dataA and points to Index 3 as the next Entry in a linked list 
emptyEntry, //Index 1 - Unused (or used for something else) 
emptyEntry, //Index 2 - Unused 
entryB,  //Index 3 - Contains dataB and points to Index 5 as the next Entry in a linked list 
entryC,  //Index 4 - Contains dataC and terminates the linked list 
emptyEntry, //Index 5 - Unused 
emptyEntry, //Index 6 - Unused 
emptyEntry};//Index 7 - Unused 

我的问题: 你能想出一种方法在编译时自动计算出“nextEntry”的值吗?现在,如果有人在MasterList中刷新条目的顺序或添加一些其他数据并抵消一些条目,则存在巨大的错误潜力。我们通过单元测试或集成测试来捕获所有的错误,但无法避免MasterList的任何更改最终都会被重复检入两次。一旦有人编辑它,并且第二次在代码测试失败时修补链接列表指示。

我最初的本能是“不,这是愚蠢的”,“你为什么要尝试这个?”但过去我已经看到了一些非常令人印象深刻的C-Macro魔术。我也相信任何宏观魔法都会比上面更糟,但我认为这值得一试,对吧?

澄清 - 我坚持与MasterList数组(而不是一个适当的链表),因为信息的消费者期待它是这样的。实际上,那里还有其他信息不是链接列表的一部分,需要位于固定索引处。然后,最重要的是,有这个链表的数据。这是一个链接列表的原因是使它有点免疫,因为添加了具有固定索引的其他元素。例如,如果事实证明我们需要在索引3处插入特定的字符串,则entryB和entryC可能会被推开,但仍可通过从链表头开始(固定在索引0处)和走在名单上。

回答

0

看起来好像没有一个好的方法可以使用编译器或预处理器可用的工具。我认为最好的方法是使用代码生成工具生成链接列表已经连接的数组。

2

通过重置并使用行号:从GCC -E

#line 0 
#define NUM (__LINE__ -2) 
Entry MasterList[8] = 
{ {dataA, sizeof(dataA), NUM } 
, {(void*)0, 0, NUM } 
, {(void*)0, 0, NUM } 
, {dataB, sizeof(dataB), NUM } 
, {dataC, sizeof(dataC), NUM } 
, {(void*)0, 0, NUM } 
, {(void*)0, 0, NUM } 
, {(void*)0, 0, NUM } 
}; 

输出:

# 28 "index.c" 
#pragma #line 0 __FILE__ 
# 1 "index.c" 

Entry MasterList[8] = 
{ {dataA, sizeof(dataA), (2 -2) } 
, {(void*)0, 0, (3 -2) } 
, {(void*)0, 0, (4 -2) } 
, {dataB, sizeof(dataB), (5 -2) } 
, {dataC, sizeof(dataC), (6 -2) } 
, {(void*)0, 0, (7 -2) } 
, {(void*)0, 0, (8 -2) } 
, {(void*)0, 0, (9 -2) } 
}; 

它应该能够做到这一点,无需重新设置行号。

+0

哇。这真的很糟糕。令人印象深刻,我认为我可以使用它,但是我只是想一点就呕吐在嘴里。 – 2015-07-12 17:34:22

-1

不要用索引来做,它是不值得的小增益。并且不要使用单独的对象,而是要初始化。这里是另一个玩具的例子,甚至有next指针const合格的,所以没有人可以乱角落找寻它:

#include <stddef.h> 

typedef struct data data; 

struct data { 
    char const* D; 
    size_t len; 
    data*const next; 
}; 

#define STR_INITIALIZER(STR) .D = STR, .len = (sizeof(STR) - 1) 
#define DATA_INITIALIZER(NAME, STR, HERE) [HERE] = { STR_INITIALIZER(STR), &(NAME)[HERE+1], } 

data table[] = { 
    DATA_INITIALIZER(table, "something", 0), 
    DATA_INITIALIZER(table, "something else", 1), 
    { 0 } 
}; 

这将使用C99“指定初始化”功能,但你可能会拿出一个版本,可以做没有。

如果你真的想全部由宏做了你然后可以使用P99用于展开,类似下面应该"p99_for.h"(未经测试)工作

#define TABLE_ENTRIES(NAME, ...) P99_FOR(NAME, P99_NARG(__VA_ARGS__), P00_SEQ, DATA_INITIALIZER, __VA_ARGS__) 

data table[] = { 
    TABLE_ENTRIES(table, "something", "something else"), 
    { 0 } 
}; 
0

听起来像是你应该只使用一个实际的链接列表,而不是使每个节点的数据在最后一个条目的索引处被猜测。

对于预处理器的缺陷,你已经开放了,但是你已经创建了一个设计问题,通过给运行时访问应该在编译时执行的数据,在那里不应该有一个。首先,每个节点必须具有全局的数组知识,甚至不知道它在哪里。我知道C没有很多C++/Java/C#的魔力,更不用说Perl/Python/Ruby,但这是只是一个数据结构问题而不是语言特征问题。从评论

编辑:

struct SpecialArray { 
    Entry* entries[10000]; //you'll have to write your own real memory management 
    int lastIdx; 
}; 

void addEntry(struct SpecialArray* arr, Entry* entry) { 
    arr->entries[arr->lastIdx] = entry; 
    entry->nextEntry = arr->lastIdx+1; 
    lastIdx++; 
} 

听起来像是你将真正需要一个入口[]不*作品[]从您的评论,但是这可以通过添加进境深层复制功能来实现。然后,只要您的客户端代码需要一个Entry数组,就可以通过(&arr.entries)。例程addEntry将为您管理索引问题并解决您的原始问题。

+0

要说明,MasterList必须是一个数组,因为MasterList中包含的信息的更高级别的用户期望能够通过Index访问信息。其中包含的信息不属于链接列表的一部分,然后在其中嵌入此链接列表。不过,我认为这是一团糟。 – 2012-08-01 19:58:52

+0

为什么需要字段“nextEntry”呢?由于数组的所有者知道索引,所以数组的所有者可以计算出该索引,而索引不是索引。 – djechlin 2012-08-01 20:00:52

+0

复杂度级别的下一个解决方案是编写自己的SpecialArray ADT,该ADT具有push_back功能,该功能将自动将正确的索引插入到具有“索引”字段的对象中。 – djechlin 2012-08-01 20:02:21