159

ゲーム内エンティティの「生の」リストを読み取るプログラムがあり、さまざまなことを処理するために、不確定数のエンティティのインデックス番号 (int) を保持する配列を作成する予定です。このようなインデックスを保持するためにメモリや CPU を使いすぎないようにしたいのですが...

私がこれまでに使用した手っ取り早い解決策は、メイン処理関数 (ローカル フォーカス) で、最大ゲーム エンティティのサイズを含む配列と、リストに追加された数を追跡するための別の整数を宣言することです。すべてのリストが 3000 個以上の配列を保持しているため、これは満足のいくものではありません。これはそれほど多くはありませんが、さまざまな機能に 6 ~ 7 個のリストのソリューションを使用できるため、無駄のように感じます。

これを達成するための C (C++ や C# ではない) 固有のソリューションは見つかりませんでした。ポインターを使用できますが、使用するのが少し怖いです (それが唯一の方法でない限り)。

配列が変更された場合に備えて、配列はローカル関数のスコープを離れません (関数に渡されてから破棄されます)。

ポインターが唯一の解決策である場合、リークを回避するためにそれらを追跡するにはどうすればよいですか?

4

10 に答える 10

278

ポインターは使用できますが、使用するのが少し怖いです。

動的配列が必要な場合、ポインターをエスケープすることはできません。なぜあなたは恐れているのですか?彼らは噛みません(あなたが注意している限り、それはそうです)。Cには組み込みの動的配列はありません。自分で作成するだけです。C ++では、組み込みクラスを使用できますstd::vector。C#やその他のほぼすべての高級言語にも、動的配列を管理する同様のクラスがいくつかあります。

独自の配列を作成する場合は、次のことから始めてください。ほとんどの動的配列の実装は、いくつかの(小さい)デフォルトサイズの配列から始めて、新しい要素を追加するときにスペースが不足するたびに、配列のサイズ。以下の例でわかるように、それはまったく難しいことではありません:(簡潔にするために安全チェックを省略しました)

typedef struct {
  int *array;
  size_t used;
  size_t size;
} Array;

void initArray(Array *a, size_t initialSize) {
  a->array = malloc(initialSize * sizeof(int));
  a->used = 0;
  a->size = initialSize;
}

void insertArray(Array *a, int element) {
  // a->used is the number of used entries, because a->array[a->used++] updates a->used only *after* the array has been accessed.
  // Therefore a->used can go up to a->size 
  if (a->used == a->size) {
    a->size *= 2;
    a->array = realloc(a->array, a->size * sizeof(int));
  }
  a->array[a->used++] = element;
}

void freeArray(Array *a) {
  free(a->array);
  a->array = NULL;
  a->used = a->size = 0;
}

それを使用するのも同じくらい簡単です:

Array a;
int i;

initArray(&a, 5);  // initially 5 elements
for (i = 0; i < 100; i++)
  insertArray(&a, i);  // automatically resizes as necessary
printf("%d\n", a.array[9]);  // print 10th element
printf("%d\n", a.used);  // print number of elements
freeArray(&a);
于 2010-08-21T04:01:22.033 に答える
11

私が考えることができるいくつかのオプションがあります。

  1. リンクされたリスト。リンクされたリストを使用して、動的に成長する配列のようなものを作成できます。array[100]しかし、最初に通り抜ける必要はありません1-99。また、どちらを使用するのもそれほど便利ではないかもしれません。
  2. 大規模な配列。すべてに十分なスペースを持つ配列を作成するだけです
  3. 配列のサイズ変更。サイズがわかったら配列を再作成するか、スペースが不足するたびに新しい配列を作成し、すべてのデータを新しい配列にコピーします。
  4. リンクされたリスト配列の組み合わせ。単純に固定サイズの配列を使用し、スペースがなくなったら、新しい配列を作成してそれにリンクします (配列と構造体の次の配列へのリンクを追跡するのが賢明です)。

あなたの状況でどのオプションが最適かを言うのは難しいです. もちろん、単純に大きな配列を作成するのが最も簡単な解決策の 1 つであり、非常に大きなものでない限り、大きな問題は発生しません。

于 2010-08-21T03:31:44.913 に答える
2

あなたが言っているとき

不確定な数のエンティティのインデックス番号 (int) を保持する配列を作成します

あなたは基本的に「ポインター」を使用していると言っていますが、これはメモリ全体のポインターではなく、配列全体のローカルポインターです。概念的には既に「ポインター」(つまり、配列内の要素を参照する ID 番号) を使用しているため、通常のポインター (つまり、最大の配列内の要素を参照する ID 番号: メモリ全体) を使用しないのはなぜですか? )。

オブジェクトにリソース ID 番号を格納する代わりに、ポインタを格納させることができます。基本的に同じことですが、「配列 + インデックス」を「ポインター」に変換することを避けるため、はるかに効率的です。

ポインターをメモリ全体の配列インデックスと考えれば、ポインターは怖くありません (これが実際のポインターです)。

于 2010-08-21T03:44:24.833 に答える
2

あらゆる種類の無制限のアイテムの配列を作成するには:

typedef struct STRUCT_SS_VECTOR {
    size_t size;
    void** items;
} ss_vector;


ss_vector* ss_init_vector(size_t item_size) {
    ss_vector* vector;
    vector = malloc(sizeof(ss_vector));
    vector->size = 0;
    vector->items = calloc(0, item_size);

    return vector;
}

void ss_vector_append(ss_vector* vec, void* item) {
    vec->size++;
    vec->items = realloc(vec->items, vec->size * sizeof(item));
    vec->items[vec->size - 1] = item;
};

void ss_vector_free(ss_vector* vec) {
    for (int i = 0; i < vec->size; i++)
        free(vec->items[i]);

    free(vec->items);
    free(vec);
}

そしてそれを使用する方法:

// defining some sort of struct, can be anything really
typedef struct APPLE_STRUCT {
    int id;
} apple;

apple* init_apple(int id) {
    apple* a;
    a = malloc(sizeof(apple));
    a-> id = id;
    return a;
};


int main(int argc, char* argv[]) {
    ss_vector* vector = ss_init_vector(sizeof(apple));

    // inserting some items
    for (int i = 0; i < 10; i++)
        ss_vector_append(vector, init_apple(i));


    // dont forget to free it
    ss_vector_free(vector);

    return 0;
}

このベクター/配列はあらゆるタイプのアイテムを保持でき、サイズは完全に動的です。

于 2018-11-17T23:44:12.250 に答える
0

これらの投稿は明らかに順番が間違っています。3回連続で1位です。ごめん。

Lie Ryan のコードを使用しようとして、保存された情報を取得する際に問題が発生しました。ベクトルの要素は連続して保存されません。これは、ビットを「ごまかして」各要素のアドレスへのポインターを保存し (もちろん、動的配列の概念の目的に反します)、それらを調べることでわかります。

少しいじくり回して、以下を介して:

ss_vector* vector; // pull this out to be a global vector

// Then add the following to attempt to recover stored values.

int return_id_value(int i,apple* aa) // given ptr to component,return data item
{   printf("showing apple[%i].id = %i and  other_id=%i\n",i,aa->id,aa->other_id);
    return(aa->id);
}

int Test(void)  // Used to be "main" in the example
{   apple* aa[10]; // stored array element addresses
    vector = ss_init_vector(sizeof(apple));
    // inserting some items
    for (int i = 0; i < 10; i++)
    {   aa[i]=init_apple(i);
        printf("apple id=%i and  other_id=%i\n",aa[i]->id,aa[i]->other_id);
        ss_vector_append(vector, aa[i]);
     }   
 // report the number of components
 printf("nmbr of components in vector = %i\n",(int)vector->size);
 printf(".*.*array access.*.component[5] = %i\n",return_id_value(5,aa[5]));
 printf("components of size %i\n",(int)sizeof(apple));
 printf("\n....pointer initial access...component[0] = %i\n",return_id_value(0,(apple *)&vector[0]));
 //.............etc..., followed by
 for (int i = 0; i < 10; i++)
 {   printf("apple[%i].id = %i at address %i, delta=%i\n",i,    return_id_value(i,aa[i]) ,(int)aa[i],(int)(aa[i]-aa[i+1]));
 }   
// don't forget to free it
ss_vector_free(vector);
return 0;
}

配列の各要素には、アドレスがわかれば問題なくアクセスできるので、「次」の要素を追加して、これを連結リストとして使用してみようと思います。確かに、より良いオプションがあります。お知らせ下さい。

于 2021-01-04T18:29:21.607 に答える
0

これらの投稿は間違った順序である可能性があります! これは、3 つの投稿のシリーズの #2 です。ごめん。

私は、Lie Ryan のコードで「いくつかの自由を取り」、連結リストを実装して、連結リストを介して彼のベクトルの個々の要素にアクセスできるようにしました。これによりアクセスが可能になりますが、個々の要素にアクセスするには、検索のオーバーヘッドが原因で時間がかかります。つまり、適切な要素が見つかるまでリストを下っていきます。これは、0 からメモリ アドレスとペアになっているものまでを含むアドレス ベクトルを維持することで解決できます。これは単純な配列ほど効率的ではありませんが、少なくとも適切な項目を検索するために「リストをたどる」必要はありません。

    // Based on code from https://stackoverflow.com/questions/3536153/c-dynamically-growing-array
typedef struct STRUCT_SS_VECTOR
{   size_t size; // # of vector elements
    void** items; // makes up one vector element's component contents
    int subscript; // this element's subscript nmbr, 0 thru whatever
    struct STRUCT_SS_VECTOR* this_element; // linked list via this ptr
    struct STRUCT_SS_VECTOR* next_element; // and next ptr
} ss_vector;

ss_vector* vector; // ptr to vector of components

ss_vector* ss_init_vector(size_t item_size) // item_size is size of one array member
{   vector= malloc(sizeof(ss_vector)); 
    vector->this_element = vector; 
    vector->size = 0; // initialize count of vector component elements
    vector->items = calloc(1, item_size); // allocate & zero out memory for one linked list element
    vector->subscript=0;
    vector->next_element=NULL;
    //      If there's an array of element addresses/subscripts, install it now.
    return vector->this_element;
}

ss_vector* ss_vector_append(ss_vector* vec_element,                 int i) 
//                                                                          ^--ptr to this element  ^--element nmbr
{   ss_vector* local_vec_element=0;
    // If there is already a next element, recurse to end-of-linked-list
    if(vec_element->next_element!=(size_t)0) 
    {   local_vec_element= ss_vector_append(vec_element->next_element,i); // recurse to end of list
        return local_vec_element;
    }
    // vec_element is NULL, so make a new element and add at end of list
    local_vec_element= calloc(1,sizeof(ss_vector)); // memory for one component
    local_vec_element->this_element=local_vec_element; // save the address
    local_vec_element->next_element=0;
    vec_element->next_element=local_vec_element->this_element;
    local_vec_element->subscript=i; //vec_element->size; 
    local_vec_element->size=i; // increment # of vector components
    //      If there's an array of element addresses/subscripts, update it now.
    return local_vec_element;
}

void ss_vector_free_one_element(int i,gboolean Update_subscripts) 
{   // Walk the entire linked list to the specified element, patch up 
    //      the element ptrs before/next, then free its contents, then free it.
    //      Walk the rest of the list, updating subscripts, if requested.
    //      If there's an array of element addresses/subscripts, shift it along the way.
    ss_vector* vec_element;
    struct STRUCT_SS_VECTOR* this_one;
    struct STRUCT_SS_VECTOR* next_one;
    vec_element=vector;
    while((vec_element->this_element->subscript!=i)&&(vec_element->next_element!=(size_t) 0)) // skip
    {   this_one=vec_element->this_element; // trailing ptr
        next_one=vec_element->next_element; // will become current ptr
        vec_element=next_one;
    } 
    // now at either target element or end-of-list
    if(vec_element->this_element->subscript!=i)
    {   printf("vector element not found\n");return;}
    // free this one
    this_one->next_element=next_one->next_element;// previous element points to element after current one
    printf("freeing element[%i] at %lu",next_one->subscript,(size_t)next_one);
    printf(" between %lu and %lu\n",(size_t)this_one,(size_t)next_one->next_element);
    vec_element=next_one->next_element; 
    free(next_one); // free the current element
    // renumber if requested
    if(Update_subscripts)
    {   i=0;
        vec_element=vector;
        while(vec_element!=(size_t) 0)
        {   vec_element->subscript=i;
            i++;
            vec_element=vec_element->next_element; 
        }
    }
    //      If there's an array of element addresses/subscripts, update it now.
/*  // Check: temporarily show the new list
    vec_element=vector;
    while(vec_element!=(size_t) 0)
    {   printf("   remaining element[%i] at %lu\n",vec_element->subscript,(size_t)vec_element->this_element);
        vec_element=vec_element->next_element;
    } */
    return;
} // void ss_vector_free_one_element()

void ss_vector_insert_one_element(ss_vector* vec_element,int place) 
{   // Walk the entire linked list to specified element "place", patch up 
    //      the element ptrs before/next, then calloc an element and store its contents at "place".
    //      Increment all the following subscripts.
    //      If there's an array of element addresses/subscripts, make a bigger one, 
    //      copy the old one, then shift appropriate members.
    // ***Not yet implemented***
} // void ss_vector_insert_one_element()

void ss_vector_free_all_elements(void) 
{   // Start at "vector".Walk the entire linked list, free each element's contents, 
    //      free that element, then move to the next one.
    //      If there's an array of element addresses/subscripts, free it.
    ss_vector* vec_element;
    struct STRUCT_SS_VECTOR* next_one;
    vec_element=vector;
    while(vec_element->next_element!=(size_t) 0)
    {   next_one=vec_element->next_element;
        // free(vec_element->items) // don't forget to free these
        free(vec_element->this_element);
        vec_element=next_one;
        next_one=vec_element->this_element;
    }
    // get rid of the last one.
    // free(vec_element->items)
    free(vec_element);
    vector=NULL;
    //      If there's an array of element addresses/subscripts, free it now.
printf("\nall vector elements & contents freed\n");
} // void ss_vector_free_all_elements()

// defining some sort of struct, can be anything really
typedef struct APPLE_STRUCT
{   int id; // one of the data in the component
    int other_id; // etc
    struct APPLE_STRUCT* next_element;
} apple; // description of component

apple* init_apple(int id) // make a single component
{   apple* a; // ptr to component
    a = malloc(sizeof(apple)); // memory for one component
    a->id = id; // populate with data
    a->other_id=id+10;
    a->next_element=NULL;
    // don't mess with aa->last_rec here
    return a; // return pointer to component
};

int return_id_value(int i,apple* aa) // given ptr to component, return single data item
{   printf("was inserted as apple[%i].id = %i     ",i,aa->id);
    return(aa->id);
}

ss_vector* return_address_given_subscript(ss_vector* vec_element,int i) 
// always make the first call to this subroutine with global vbl "vector"
{   ss_vector* local_vec_element=0;
    // If there is a next element, recurse toward end-of-linked-list
    if(vec_element->next_element!=(size_t)0)
    {   if((vec_element->this_element->subscript==i))
        {   return vec_element->this_element;}
        local_vec_element= return_address_given_subscript(vec_element->next_element,i); // recurse to end of list
        return local_vec_element;
    }
    else
    {   if((vec_element->this_element->subscript==i)) // last element
        {   return vec_element->this_element;}
        // otherwise, none match
        printf("reached end of list without match\n");
        return (size_t) 0;
    }
} // return_address_given_subscript()

int Test(void)  // was "main" in the original example
{   ss_vector* local_vector;
    local_vector=ss_init_vector(sizeof(apple)); // element "0"
    for (int i = 1; i < 10; i++) // inserting items "1" thru whatever
    {   local_vector=ss_vector_append(vector,i);}   
    // test search function
    printf("\n NEXT, test search for address given subscript\n");
    local_vector=return_address_given_subscript(vector,5);
    printf("finished return_address_given_subscript(5) with vector at %lu\n",(size_t)local_vector);
    local_vector=return_address_given_subscript(vector,0);
    printf("finished return_address_given_subscript(0) with vector at %lu\n",(size_t)local_vector);
    local_vector=return_address_given_subscript(vector,9);
    printf("finished return_address_given_subscript(9) with vector at %lu\n",(size_t)local_vector);
    // test single-element removal
    printf("\nNEXT, test single element removal\n");
    ss_vector_free_one_element(5,FALSE); // without renumbering subscripts
    ss_vector_free_one_element(3,TRUE);// WITH renumbering subscripts
    // ---end of program---
    // don't forget to free everything
    ss_vector_free_all_elements(); 
    return 0;
}
于 2021-01-09T19:04:26.270 に答える