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 ma​​lloc 成为一个指向实例的指针数组,然后 malloc 我的实例数组。在我开始更改一堆代码之前,我想了解一个经验丰富的 c 程序员如何处理这种事情。

4

1 回答 1

2

这可能看起来有点邋遢,但如果你真的想传递那个实例**指针并希望它同时适用于主数据集和拆分,你真的需要为主数据集创建一个指针数组. 这是您可以做到的一种方法...

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 回答