C语言 恢复具有多个索引范围的大型数组的元素



这是一个棘手的问题,我已经思考了很长时间,还没有在任何地方看到一个令人满意的答案。假设我有一个大小为 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 %dn",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文件中读取。我只是在代码中展示了初始化以用于说明目的。

有许多方法可以在初始化时或之后快速将数组中的内存清零。

对于堆栈上的数组,用零初始化它。{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 - %dn", i, foo[i]);
}
}

那将打印...

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

作为旁注,仅使用大型数组的几个元素是浪费内存。相反,请使用哈希表,或者如果您需要排序,请使用某种类型的树。这些可能很难正确实现,但像 GLib 这样的库可以为您提供良好的实现。

简介

我对您的问题做出了一个强有力的假设,那就是稀疏性(数组中的大多数元素将保持零)。 在此假设下,我将数组构建为列表。我包含一个示例代码,它不完整,也不打算 ---你应该做自己的功课:)

核心对象是一个结构,其指针指向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

在线测试

主要示例

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

int callback(size_t index, int value) {
printf("Vector[%lu] = %dn", 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) = %dn", 1000, vector_get(vec, 1000)); // This should print 3
printf("vector_get(vec, %d) = %dn", 1, vector_get(vec, 1)); // this should print 0
printf("size(vec) = %lun", 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) = %lun", 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_eachcallback是您真正应该查看的内容。

实现

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

特定部分是向量中特定元素的搜索。每次你翻阅列表时, 这只有在您具有稀疏性时才方便(索引的大部分将始终返回零)。 在此实现中,如果访问未登记的索引,则结果为 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中,您可以看到列表的工作原理(以及相同的概念) 也用于设置和删除):我们从头开始,并且 我们越过列表,直到到达索引相等的元素 到请求的那个。如果我们找不到它,我们只需返回 0。如果我们需要在值为 找不到,很容易实现"错误回调"。

只要稀疏性仍然存在,在整个数组中搜索索引在内存需求方面是一个很好的折衷方案,效率可能不是问题。

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->nextcurr->next->next.我们必须释放以前的curr->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

最新更新