1

この質問は、私が掘り下げたこのポインターの問題を処理するためのベスト プラクティスに関するものです。

csv を読み取る関数で動的に生成される構造体の配列があります。

int init_from_csv(instance **instances,char *path) {
    ... open file, get line count
   *instances = (instance*) malloc( (size_t) sizeof(instance) * line_count );
    ... parse and set values of all instances
    return count_of_valid_instances_read;
}
// in main()
instance *instances;
int ins_len = init_from_csv(&instances, "some/path/file.csv");

ここで、この生データに対して関数を実行し、それを分割し、分割に対して同じ関数を再度実行する必要があります。このデータ セットはかなり大きくなる可能性があるため、インスタンスを複製したくありません。分割されている構造体へのポインターの配列が必要なだけです。

instance **split = (instance**) malloc (sizeof(instance*) * split_len_max);
int split_function(instance *instances, ins_len, instances **split){
  int i, c;
  c = 0;
  for (i = 0; i < ins_len; i++) {
    if (some_criteria_is_true) {
      split[c++] = &instances[i];
  }
  return c;
}

今私の質問は、構造体の配列とポインターの配列の両方で関数を実行するためのベストプラクティスまたは最も読みやすい方法でしょうか? 簡単な例count_data()です。

int count_data (intances **ins, ins_len, float crit) {
  int i,c;
  c = 0;
  for (i = 0; i < ins_len; i++) {
    if ins[i]->data > crit) {
      ++c;
    }
  }
  return c;
}

// code smell-o-vision going off by now
int c1 = count_data (split, ins_len, 0.05);  // works
int c2 = count_data (&instances, ins_len, 0.05); // obviously seg faults

init_from_csv をインスタンスへのポインターの配列に malloc し、次にインスタンスの配列を malloc することができます。たくさんのコードを変更し始める前に、ベテランの C プログラマーがこの種のことをどのように処理するかを学びたいと思います。

4

1 に答える 1

2

これは少し不自然に思えるかもしれませんが、そのインスタンス** ポインターを渡してメイン データ セットと分割の両方で機能させたい場合は、メイン データ セット用のポインターの配列も作成する必要があります。 . これがあなたがそれを行うことができる1つの方法です...

size_t i, mem_reqd;
instance **list_seg, *data_seg;

/* Allocate list and data segments in one large block */
mem_reqd = (sizeof(instance*) + sizeof(instance)) * line_count;
list_seg = (instance**) malloc( mem_reqd );
data_seg = (instance*) &list_seg[line_count];

/* Index into the data segment */
for( i = 0; i < line_count; i++ ) {
    list_seg[i] = &data_seg[i];
}

*instances = list_seg;

instance*これで、メイン リストであろうとスプリットであろうと、いつでもポインターの配列を操作できます。余分なメモリを使用したくないことはわかっていますが、instance構造体がそれほど小さくない場合は、各インスタンスに余分なポインターを割り当てて、混乱を招くコードの重複を防ぐことをお勧めします。

メイン インスタンス リストが完成したら、次の操作を実行できます。

void free_instances( instance** instances )
{
    free( instances );
}

これを構造体として実装したくなるでしょう:

struct instance_list {
    instance ** data;
    size_t length;
    int owner;
};

そうすれば、より良い方法で関数からこれを返すことができます。

instance_list* alloc_list( size_t length, int owner )
{
    size_t i, mem_reqd;
    instance_list *list;
    instance *data_seg;

    /* Allocate list and data segments in one large block */
    mem_reqd = sizeof(instance_list) + sizeof(instance*) * length;
    if( owner ) mem_reqd += sizeof(instance) * length;
    list = (instance_list*) malloc( mem_reqd );
    list->data = (instance**) &list[1];
    list->length = length;
    list->owner = owner;

    /* Index the list */
    if( owner ) {
        data_seg = (instance*) &list->data[line_count];
        for( i = 0; i < line_count; i++ ) {
            list->data[i] = &data_seg[i];
        }
    }

    return list;
}

void free_list( instance_list * list )
{
    free(list);
}

void erase_list( instance_list * list )
{
    if( list->owner ) return;
    memset((void*)list->data, 0, sizeof(instance*) * list->length);
}

これで、CSV からロードする関数は、このモンスターの作成の詳細に集中する必要がないため、本来のタスクを単純に実行できます。データが含まれているか、単に他のリストを指しているかにかかわらず、他の関数からリストを返すことができるようになりました。

instance_list* load_from_csv( char *path )
{
    /* get line count... */
    instance_list *list = alloc_list( line_count, 1 );
    /* parse csv ... */
    return list;
}

など...そうですね。このコードがコンパイルまたは動作するという保証はありませんが、近いはずです。単純な配列よりも少しだけ複雑な配列を使用して何かを行うときはいつでも、それをカプセル化するためのわずかな余分な努力をすることが役立つと思います。これは、分析などで使用する主要なデータ構造であるため、独自のデータ型を持っているという点で、少し高さを与えることは理にかなっています。

わかりません、それはやり過ぎでしたか?=)

于 2012-08-02T04:25:05.740 に答える