2012-08-01 77 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