arraylist实现在追加32754个元素后停止工作



我的arraylist实现在追加32754个元素后停止工作。我觉得很奇怪的是,这个问题只发生在添加了这么多元素之后,而32000仍然不太高。我知道我没有检查NULL指针,我的测试程序是一个无限循环。我正在使用旧版本来降低代码的复杂性
输出:

32752
32753
32754
zsh: segmentation fault  ./acl

array.c:

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
union arraylist_meta {
double dummy_double;
long double dummy_long_double;
long long dummy_long_long;
void *dummy_ptr;
void (*dummy_func_ptr)(void);
struct {
size_t len;
size_t cap;
size_t sizeof_one_element;
};
};
void* acl_arraylist_create(size_t array_size, size_t sizeof_one_element) {
union arraylist_meta *arraylist_new = malloc(array_size * sizeof_one_element + sizeof*arraylist_new);
arraylist_new->len = array_size;
arraylist_new->cap = array_size;
arraylist_new->sizeof_one_element = sizeof_one_element;
return arraylist_new+1;
}
void* acl_arraylist_append(void *arraylist_void, void *element) {
union arraylist_meta *arraylist = arraylist_void;
--arraylist;
if(arraylist->len == arraylist->cap) {
arraylist->cap = arraylist->len + 10;
arraylist = realloc(arraylist, arraylist->cap * arraylist->sizeof_one_element + sizeof *arraylist);
}
memcpy((char*)(arraylist + 1) + arraylist->sizeof_one_element * arraylist->len, element, arraylist->sizeof_one_element);
++arraylist->len;
return arraylist+1;
}

array.h:

#ifndef array_h
#define array_h
#include <stddef.h>
void* acl_arraylist_create(size_t array_size, size_t sizeof_one_element);
void* acl_arraylist_append(void *arraylist_void, void *element_void);
#endif

一个简单的测试程序:

#include <acl/array.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
int *num = acl_arraylist_create(0, sizeof *num);
for(int i = 0;;++i) {
num = acl_arraylist_append(num, &i);
printf("%dn", i);
}
}

编辑:
我不久前更改了可执行文件的。通过恢复几次提交,我的构建脚本再次使用了旧名称,但执行了名为的可执行文件。这意味着我上面描述的问题与上面的代码无关。它只在使用以下代码时发生:
array.c:

#include <stddef.h> 
#include <stdlib.h> 
#include <string.h> 
#include <acl/array.h> 
size_t acl_arraylist_len(void *arraylist); 
void acl_arraylist_free(void *arraylist); 
static inline void* acl_arraylist_resize(union acl_arraylist_meta *arraylist, int64_t relativLen) { 
size_t cap = arraylist->cap + relativLen; 
arraylist = realloc(arraylist, cap * arraylist->sizeof_one_element + sizeof *arraylist); 
if(arraylist != NULL) { 
arraylist->cap = cap; 
} 
return arraylist; 
} 
void* acl_arraylist_create(size_t array_size, size_t sizeof_one_element) { 
union acl_arraylist_meta *arraylist_new = malloc(array_size * sizeof_one_element + sizeof*arraylist_new); 
if(arraylist_new == NULL) return NULL; 
arraylist_new->len = array_size; 
arraylist_new->cap = array_size; 
arraylist_new->sizeof_one_element = sizeof_one_element; 
return arraylist_new+1; 
} 
void* acl_arraylist_append(void *arraylist_void, void *element) { 
void *element_append; 
union acl_arraylist_meta *arraylist = acl_arraylist_append_ptr(arraylist_void, &element_append); 
if(arraylist == NULL) return NULL; 
--arraylist; 
memcpy(element_append, element, arraylist->sizeof_one_element); 
return arraylist + 1; 
} 
void* acl_arraylist_append_ptr(void *arraylist_void, void **append_element) { 
union acl_arraylist_meta *arraylist = arraylist_void; 
--arraylist; 
if(arraylist->len == arraylist->cap) { 
acl_arraylist_resize(arraylist, 10); 
if(arraylist == NULL) return NULL; 
} 
*append_element = (char*)(arraylist + 1) + arraylist->sizeof_one_element * arraylist->len; 
++arraylist->len; 
return arraylist + 1; 
} 
void* acl_arraylist_remove(void *arraylist_void, size_t index) { 
union acl_arraylist_meta *arraylist = (union acl_arraylist_meta*)arraylist_void - 1; 
char *arraylist_char = arraylist_void; 
if(index != arraylist->len - 1) { 
memcpy(arraylist_char + arraylist->sizeof_one_element * index, arraylist_char + arraylist->sizeof_one_element * (arraylist->len - 1), arraylist->sizeof_one_element); 
} 
--arraylist->len; 
if(arraylist->len < arraylist->cap - 20) { 
void* arraylistTmp = acl_arraylist_resize(arraylist, -10); 
if(arraylistTmp != NULL) arraylist = arraylistTmp; 
} 
return arraylist + 1; 
} 

array.h:

#ifndef _acl_array_h
#define _acl_array_h
#include <stddef.h>
#include <stdlib.h>
union acl_arraylist_meta {
double dummy_double;
long double dummy_long_double;
long long dummy_long_long;
void *dummy_ptr;
void (*dummy_func_ptr)(void);
struct {
size_t len;
size_t cap;
size_t sizeof_one_element;
};
};
inline size_t acl_arraylist_len(void *arraylist) {
return ((union acl_arraylist_meta*)arraylist - 1)->len;
}
inline void acl_arraylist_free(void *arraylist) {
free((union acl_arraylist_meta*)arraylist-1);
}
void* acl_arraylist_remove(void *arraylist_void, size_t index);
void* acl_arraylist_create(size_t array_size, size_t sizeof_one_element);
void* acl_arraylist_append(void *arraylist_void, void *element);
void* acl_arraylist_append_ptr(void *arraylist_void, void **append_element);
#endif

一个简单的测试程序:

#include <acl/array.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void *num = acl_arraylist_create(100, sizeof(int));
for (int i = 0; i < 65536; ++i)
{
num = acl_arraylist_append(num, &i);
printf("%dn", i);
}
}

令人担忧的是,array.c源文件不包括声明源文件定义的服务的头(acl/array.h(。这意味着没有交叉检查。头提供了将C程序连接在一起的粘合剂,提供了交叉检查,以确保使用所提供服务的代码与提供这些服务的代码一致。

另外:您的示例程序不会创建列表——您的代码不应该编译,因为没有定义num

当重新排序一位时,代码确实可以干净地编译。当我添加一个呼叫时:

void *num = acl_arraylist_create(100, sizeof(int));

main()中循环并运行代码(源代码acl23.c,程序acl23(之前,我在Mac OS库说:之前进行了150次迭代

acl23(54767,0x10d41b5c0) malloc: *** error for object 0x7f8c40c02bb0: pointer being realloc'd was not allocated
acl23(54767,0x10d41b5c0) malloc: *** set a breakpoint in malloc_error_break to debug.

如果你有Valgrind,就用它。

我认为您的代码是在玩火(而且您正在被烧着(,因为您正试图将union arraylist_meta结构和数组数据结合起来。

然而,主要问题是,当重新分配内存时,您没有使用acl_arraylist_append()返回的新值。将循环中的行更改为:

new = acl_arraylist_append(num, &i);

对我来说,代码最高可达65535。我将循环设置为停止,而不是不加限制。

for (int i = 0; i < 65536; ++i).

目前还不清楚数组列表的用户将如何访问数组的元素。据推测,您计划让它们将void *(本例中为num(转换为适当的类型指针(int *array = num;(,然后它们就可以索引到数组中。还不清楚它们是如何确定数组的大小的——最大索引是多少。您还没有提供释放数组的函数。用户无法做到这一点——他们拥有的指针不是分配函数之一(malloc()realloc()等(返回的指针。这些都不是立即关键的;我们可以放心地假设它们被从MCVE(最小、完整、可验证的示例——或者MRE,或者SO现在使用的任何名称(。

这是我的工作代码从您的代码派生而来——全部在一个文件中。这些变化其实很小。

/*array.h:*/
#ifndef array_h
#define array_h
#include <stddef.h>
void *acl_arraylist_create(size_t array_size, size_t sizeof_one_element);
void *acl_arraylist_append(void *arraylist_void, void *element_void);
#endif
/*array.c:*/
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
/*#include <acl/array.h>*/
union arraylist_meta
{
double dummy_double;
long double dummy_long_double;
long long dummy_long_long;
void *dummy_ptr;
void (*dummy_func_ptr)(void);
struct
{
size_t len;
size_t cap;
size_t sizeof_one_element;
};
};
void *acl_arraylist_create(size_t array_size, size_t sizeof_one_element)
{
union arraylist_meta *arraylist_new = malloc(array_size * sizeof_one_element + sizeof *arraylist_new);
arraylist_new->len = array_size;
arraylist_new->cap = array_size;
arraylist_new->sizeof_one_element = sizeof_one_element;
return arraylist_new + 1;
}
void *acl_arraylist_append(void *arraylist_void, void *element)
{
union arraylist_meta *arraylist = arraylist_void;
--arraylist;
if (arraylist->len == arraylist->cap)
{
arraylist->cap = arraylist->len + 10;
arraylist = realloc(arraylist, arraylist->cap * arraylist->sizeof_one_element + sizeof *arraylist);
}
memcpy((char *)(arraylist + 1) + arraylist->sizeof_one_element * arraylist->len, element, arraylist->sizeof_one_element);
++arraylist->len;
return arraylist + 1;
}
/*a simple test programm:*/
/*#include <acl/array.h>*/
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void *num = acl_arraylist_create(100, sizeof(int));
for (int i = 0; i < 65536; ++i)
{
num = acl_arraylist_append(num, &i);
printf("%dn", i);
}
}

我不打算展示输出;从1到65535的数字并不令人兴奋。

我不相信void *是您数组的句柄类型。用户可以提供他们选择的任何指针作为句柄,并且无法知道这是错误类型的指针。请提供不透明类型;在标题中,定义:

typedef struct acl_arraylist acl_arraylist;

然后让函数获取并返回一个acl_arraylist *。客户端代码不需要知道里面有什么。array.c中的代码可能会将union arraylist_meta值包装成一个结构:

struct acl_arraylist
{
union arraylist_meta array;
};

然后你就可以像以前一样打球了。但用户必须努力将任意指针传递给函数——足够努力,他们不会出错。

acl_arraylist_resize返回的新指针在acl_arraylist_append_ptr中被忽略。修改后的代码:

void* acl_arraylist_append_ptr(void *arraylist_void, void **append_element) {
union acl_arraylist_meta *arraylist = arraylist_void;
--arraylist;
if(arraylist->len == arraylist->cap) {
arraylist = acl_arraylist_resize(arraylist, 10);// this line was modified
if(arraylist == NULL) return NULL;
}
*append_element = (char*)(arraylist + 1) + arraylist->sizeof_one_element * arraylist->len;
++arraylist->len;
return arraylist + 1;
}

最新更新