2

単方向リストのエントリを表す C 構造体を考えてみましょう。任意のデータへのポインタ、そのデータのサイズ、および次のエントリを見つける方法が含まれています

typedef struct{
  unsigned char *data
  unsigned char dataSize
  unsigned char nextEntry
} Entry;

次に、次のエントリのコレクションとそれらが表すデータについて考えます。

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 への変更が 2 回チェックインされることは避けられません。誰かがそれを編集したときと、コードがテストに失敗したときにリンクされたリストのインデックスを修正するために 2 回目。

私の最初の本能は、「いや、それはばかげている」と「なぜこれを試すのですか」です。しかし、私は過去にかなり印象的な C-Macro の魔法を見てきました。また、マクロの魔法は上記よりも維持するのがさらに悪いと思いますが、試してみる価値はあると思いますよね?

明確化- 情報の消費者がこのようになることを期待しているため、MasterList配列(適切なリンクリストではない)に固執しています。実際には、リンクされたリストの一部ではない、固定インデックスにある必要がある他の情報がそこにあります。その上に、このリンクされたリスト データがあります。これがリンクされたリストである理由は、固定インデックスを持つ他の要素が追加されたときに押し出されないようにするためです。たとえば、特定の文字列をインデックス 3 に押し込む必要があることが判明した場合、entryB と entryC は邪魔にならないように押し出される可能性がありますが、リンクされたリストの先頭 (インデックス 0 に固定) から開始することで発見できます。リストを歩いています。

4

4 に答える 4

3

行番号をリセットして使用することにより:

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

gcc -E からの出力:

# 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) }
};

行番号をリセットせずに実行できるはずです。

于 2012-08-01T19:18:23.573 に答える
0

各ノードのデータに最後のエントリのインデックスを推測させるのではなく、実際のリンクされたリストを使用する必要があるようです。

あなたはプリプロセッサのごまかしに心を開いていましたが、コンパイル時に強制されるべきではないデータへの実行時アクセスを許可することで、設計上の問題を引き起こしました。一つには、各ノードは、それが含まれていることさえ知らない配列のグローバルな知識を持っている必要があります.CにはPerl/Python/Rubyはもちろんのこと、C++/Java/C#の魔法があまりないことは知っていますが、これは言語機能の問題ではなく、単なるデータ構造の問題です。

コメントから編集:

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[] ではなく Entry[] が必要になるようですが、これは Entry にディープ コピー機能を追加することで実現できます。(&arr.entries)次に、クライアント コードが Entry の配列を期待するときはいつでも渡すことができます。ルーチンaddEntryはインデックスの問題を管理し、元の問題を解決します。

于 2012-08-01T19:53:19.157 に答える
0

コンパイラまたはプリプロセッサで利用可能なツールでこれを行うには、実際には良い方法がないようです。これを行う最善の方法は、リンクされたリストが既に接続された配列を生成するコード生成ツールを使用することだと思います。

于 2012-08-11T21:27:15.443 に答える
-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 }
};
于 2012-08-01T19:33:17.247 に答える