【问题标题】:Recovering elements of large array with multiple index ranges恢复具有多个索引范围的大型数组的元素
【发布时间】:2018-05-27 17:00:28
【问题描述】:

这是一个棘手的问题,我一直在思考很长时间,但尚未在任何地方看到令人满意的答案。假设我有一个大小为 10000 的大型 int 数组。我可以通过以下方式简单地声明它:

 int main()
{

    int foo[10000];

    int i;
    int n;
    n = sizeof(foo) / sizeof(int);

    for (i = 0; i < n; i++)
    {
        printf("Index %d is %d\n",i,foo[i] );
    }

    return 0;
}

很明显,在我正式初始化它们之前,数组中的每个索引都会包含随机的数字组合:

Index 0 is 0
Index 1 is 0
Index 2 is 0
Index 3 is 0
   .
   .
   .
Index 6087 is 0
Index 6088 is 1377050464
Index 6089 is 32767
Index 6090 is 1680893034
   .
   .
   .
Index 9996 is 0
Index 9997 is 0
Index 9998 is 0
Index 9999 is 0

然后假设我用值初始化数组的选择索引范围对某个函数的操作:

//Call this block 1
foo[0] = 0;
foo[1] = 7;
foo[2] = 99;
foo[3] = 0;

//Call this block 2
foo[9996] = 0;
foo[9997] = 444;
foo[9998] = 2;
foo[9999] = 0;

for (i = 0; i < (What goes here?); i++)
{
    //I must pass in only those values initialized to select indices of foo[] (Blocks 1 and 2 uncorrupted)

    //How to recover those values to pass into foo_func()?

    foo_func(foo[]);
}

在我自己正式初始化数组之前,我初始化 foo[] 的一些值与数组中预先存在的值重叠。考虑到有多个索引范围,如何仅传递我初始化的数组元素的索引?我就是想不通。感谢您的所有帮助!

编辑:

我还应该提到,数组本身将从 .txt 文件中读取。我只是为了说明的目的在代码中显示了初始化。

【问题讨论】:

  • 根本不传递它们 - 使用全局 const 数组?
  • 首先将数组初始化为零 int foo[10000]={0};然后做你想做的(我不明白你到底想做什么)
  • @MartinJames 我没有提到这一点,但全局常量数组不起作用,因为数组本身将从 .txt 文件中读取,因此必须在代码中动态处理。
  • 关于编辑:请发布实际代码而不是“说明性代码”。

标签: c arrays indexing


【解决方案1】:

您可以通过多种方式快速将数组中的内存清零,无论是在初始化时还是在初始化之后。

对于堆栈上的数组,用零初始化它。 {0} 是它的简写。

int foo[10000] = {0};

对于堆上的数组,使用calloc分配内存并用0初始化。

int *foo = calloc(10000, sizeof(int));

如果数组已经存在,使用memset 快速用零覆盖数组的所有内存。

memset(foo, 0, sizeof(int) * 10000);

现在所有元素都为零。您可以将各个元素一一设置为您喜欢的任何内容。比如……

int main() {
    int foo[10] = {0};

    foo[1] = 7;
    foo[2] = 99;
    foo[7] = 444;
    foo[8] = 2;

    for( int i = 0; i < 10; i++ ) {
        printf("%d - %d\n", i, foo[i]);
    }
}

这将打印...

0 - 0
1 - 7
2 - 99
3 - 0
4 - 0
5 - 0
6 - 0
7 - 444
8 - 2
9 - 0

附带说明,仅使用大型数组中的几个元素会浪费内存。相反,请使用hash table,或者如果您需要订购,请使用某种类型的tree。这些可能很难正确实现,但是a library such as GLib can provide you with good implementations

【讨论】:

  • 这很有帮助,但是假设除了您初始化的值之外,您还初始化了 foo[9]=0。我如何随后将初始化值传入 foo_func() (索引 1、2、7、8、9);而忽略其他人?这可能吗?
  • @J_code 因为数组只是一块内存,所以您无法区分设置为 0 的数字和初始化为 0 的数字。您可以 使用声明某个值作为哨兵,例如,如果您的数字永远不会是负数,请使用 -1。或者如果数组是float,则使用NaN。但是如果不慢慢遍历整个数组,就无法找到“设置”值。最好使用更合适的数据结构,如哈希表或树。
【解决方案2】:

简介

我对你的问题做了一个强有力的假设,它是 sparsness(你的数组中的大部分元素将保持为零)。 在这种假设下,我会将数组构建为 list。我包含了一个示例代码,它不完整,也不打算 是---你应该做你自己的功课:)

核心对象是一个结构体,其指针指向begin 元素和大小:

typedef struct vector {
  size_t size;
  vector_element_t * begin;
} vector_t;

向量的每个元素都有自己的索引和值以及指向列表中下一个元素的指针:

typedef struct vector_element vector_element_t;
struct vector_element {
  int value;
  size_t index;
  vector_element_t *next;
};

在此基础上,我们可以将动态向量构建为列表,通过删除排序约束(不需要,您可以修改此代码 保持顺序),使用一些简单的自定义方法:

vector_t * vector_init(); // Initialize an empty array
void vector_destroy(vector_t* v); // Destroy the content and the array itself
int vector_get(vector_t *v, size_t index); // Get an element from the array, by searching the index
size_t vector_set(vector_t *v, size_t index, int value); // Set an element at the index
void vector_delete(vector_t *v, size_t index); // Delete an element from the vector
void vector_each(vector_t *v, int(*f)(size_t index, int value)); // Executes a callback for each element of the list
                                                                 // This last function may be the response to your question

Test it online

主要例子

这是一个使用所有这些方法并在控制台中打印的 main:

int callback(size_t index, int value) {
  printf("Vector[%lu] = %d\n", index, value);
  return value;
}

int main() {
  vector_t * vec = vector_init();

  vector_set(vec, 10, 5);
  vector_set(vec, 23, 9);
  vector_set(vec, 1000, 3);

  printf("vector_get(vec, %d) = %d\n", 1000, vector_get(vec, 1000)); // This should print 3
  printf("vector_get(vec, %d) = %d\n", 1, vector_get(vec, 1)); // this should print 0
  printf("size(vec) = %lu\n", vec->size); // this should print 3 (the size of initialized elements)

  vector_each(vec, callback); // Calling the callback on each element of the
                              // array that is initialized, as you asked.

  vector_delete(vec, 23);
  printf("size(vec) = %lu\n", vec->size);
  vector_each(vec, callback); // Calling the callback on each element of the array

  vector_destroy(vec);
  return 0;
}

还有输出:

vector_get(vec, 1000) = 3
vector_get(vec, 1) = 0
size(vec) = 3
Vector[10] = 5
Vector[23] = 9
Vector[1000] = 3
size(vec) = 3
Vector[10] = 5
Vector[1000] = 3

带有vector_each 函数的callback 是你真正应该看看的东西。

实现

我将在介绍中为您提供一些简单的功能实现。它们并不完整, 并且应该引入对指针的一些检查。我把它留给你。事实上,此代码不适用于生产,在某些情况下也可能溢出

特定部分是向量中特定元素的搜索。每次遍历列表时, 这只有在你有稀疏性的情况下才方便(你的大部分索引总是返回零)。 在此实现中,如果您访问未登记的索引,则结果为 0。如果您不想要这个 你应该定义一个错误回调。

初始化和销毁​​

初始化时,我们为向量分配内存,但里面没有元素,因此begin 指向NULL。当我们销毁向量时,我们不仅要释放向量,还要释放所包含的每个元素。

vector_t * vector_init() {
  vector_t * v = (vector_t*)malloc(sizeof(vector_t));
  if (v) {
    v->begin = NULL;
    v->size = 0;
    return v;
  }
  return NULL;
}

void vector_destroy(vector_t *v) {
  if (v) {
    vector_element_t * curr = v->begin;
    if (curr) {
      vector_element_t * next = curr->next;
      while (next) {
        curr = curr->next;
        next = next->next;
        if (curr)
          free(curr);
      }
      if (next)
        free(next);
    }
    free(v);
  }
}

get 和 set 方法

在 get 你可以看到列表是如何工作的(和相同的概念 也用于设置和删除):我们从头开始,并且 我们遍历列表,直到我们到达一个索引等于的元素 给请求的人。如果我们找不到它,我们只需返回 0。 如果我们需要在值为 没找到,很容易实现“错误回调”。

只要 sparsness 成立,在整个数组中搜索索引就内存需求而言是一个很好的折衷方案,效率可能不是问题。

int vector_get(vector_t *v, size_t index) {
  vector_element_t * el = v->begin;
  while (el != NULL) {
    if (el->index == index)
      return el->value;
    el = el->next;
  } 
  return 0;
}

// Gosh, this set function is really a mess... I hope you can understand it...
// -.-'
size_t vector_set(vector_t *v, size_t index, int value) {
  vector_element_t * el = v->begin;
  // Case 1: Initialize the first element of the array
  if (el == NULL) {
    el = (vector_element_t *)malloc(sizeof(vector_element_t));
    if (el != NULL) {
      v->begin = el;
      v->size += 1;
      el->index = index;
      el->value = value;
      el->next = NULL;
      return v->size;
    } else {
      return 0;
    }
  }
  // Case 2: Search for the element in the array
  while (el != NULL) {
    if (el->index == index) {
      el->value = value;
      return v->size;
    }
    // Case 3: if there is no element with that index creates a new element
    if (el->next == NULL) {
      el->next = (vector_element_t *)malloc(sizeof(vector_element_t));
      if (el->next != NULL) {
        v->size += 1;
        el->next->index = index;
        el->next->value = value;
        el->next->next = NULL;
        return v->size;
      }
      return 0;
    }
    el = el->next;
  }
}

删除元素

使用这种方法可以很容易地删除一个元素,连接 curr-&gt;nextcurr-&gt;next-&gt;next。我们必须释放之前的curr-&gt;next...

void vector_delete(vector_t * v, size_t index) {
  vector_element_t *curr = v->begin;
  vector_element_t *next = curr->next;

  while (next != NULL) {
    if (next->index == index) {
      curr->next = next->next;
      free(next);
      return;
    } else {
      curr = next;
      next = next->next;
    }
  }
}

一个迭代函数

我认为这是您问题最后一部分的答案, 而是传递一系列索引,而是将回调传递给向量。 回调获取并设置特定索引中的值。如果你想 仅对某些特定索引进行操作,您可以在 回调本身。如果您需要将更多数据传递给回调,请检查 最后一节。

void vector_each(vector_t * v, int (*f)(size_t index, int value)) {
  vector_element_t *el = v->begin;
  while (el) {
    el->value = f(el->index, el->value);
    el = el->next;
  }
}

错误回调

您可能想要提出一些越界错误或其他错误。一种解决方案是使用函数指针丰富您的列表,该函数指针表示当您的用户 sk 获取未定义元素时应调用的回调:

typedef struct vector {
   size_t size;
   vector_element_t *begin;
   void (*error_undefined)(vector *v, size_t index);
} vector_t

也许在你的 vector_get 函数结束时,你可能想做一些类似的事情:

int vector_get(vector_t *v, size_t index) {
  // [ . . .] 
  // you know at index the element is undefined:
  if (v->error_undefined)
    v->error_undefined(v, index);
  else {
    // Do something to clean up the user mess... or simply
    return 0;
  }
}

通常最好添加一个辅助函数来设置回调...

将用户数据传递给“每个”回调

如果您想向用户回调传递更多数据,您可以添加void* 作为最后一个参数:

void vector_each(vector_t * v, void * user_data, int (*f)(size_t index, int value, void * user_data));

void vector_each(vector_t * v, void * user_data, int (*f)(size_t index, int value, void * user_data)) {
   [...]
   el->value = f(el->index, el->value, user_data);
   [...]
}

如果用户不需要,可以传一个精彩的NULL

【讨论】:

    猜你喜欢
    • 2021-08-29
    • 1970-01-01
    • 2020-06-30
    • 1970-01-01
    • 1970-01-01
    • 2016-06-29
    • 2012-12-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多