C-动态数组处理建议

C-动态数组处理建议,c,arrays,realloc,C,Arrays,Realloc,我目前正在学习C语言,作为一个在高级语言方面有丰富经验的人 在考虑了您的一些评论并进行了更多的测试和fideling之后,我提出了一个更好的解决方案(向下滚动) 尽管如此,建议和评论仍然非常受欢迎 也就是说,我在C语言中对动态数组处理有点纠结。我把它归结到一个点,我非常喜欢,简单地用NULL终止每个数组,并在必要时使用realloc 基本上我是在寻求建议:这样做除了明显的缺点(不能使用空值作为数据值)之外,还有什么缺点吗(见下面的示例代码) 更好的版本(更新!) 在阅读了您的答案并亲自调试之后,

我目前正在学习C语言,作为一个在高级语言方面有丰富经验的人

在考虑了您的一些评论并进行了更多的测试和fideling之后,我提出了一个更好的解决方案(向下滚动)

尽管如此,建议和评论仍然非常受欢迎

也就是说,我在C语言中对动态数组处理有点纠结。我把它归结到一个点,我非常喜欢,简单地用
NULL
终止每个数组,并在必要时使用
realloc

基本上我是在寻求建议:这样做除了明显的缺点(不能使用空值作为数据值)之外,还有什么缺点吗(见下面的示例代码)

更好的版本(更新!) 在阅读了您的答案并亲自调试之后,我提出了一个新版本,它主要基于下面提到的
struct
方法

看看,告诉我,我现在做错了什么。:)

#包括“stdlib.h”
#包括“assert.h”
typedef void*任何;
类型定义结构{
任何*项目;
整数计数;
}名单;
List*List_new(){
List*List=malloc(sizeof(List));
列表->项目=NULL;
列表->计数=0;
退货清单;
}
无效列表\添加(列表*列表,任意元素){
整数计数=列表->计数+1;
list->items=realloc(list->items,count*sizeof(element));
列表->项目[count-1]=元素;
列表->计数=计数;
}
无效列表(列表*列表){
如果(列表){
如果(列表->项目)自由(列表->项目);
免费(名单);
}
}
无效列表(列表*列表,无效(*回调)(列表*,任意,int)){
对于(int i=0;icount;i++){
回调(列表,列表->项目[i],i);
}
}
//样本使用
#包括“stdio.h”
无效调试_项(列表*列表,任意项,int索引){
如果(索引>0)printf(“,”);
printf(“\%s\”,项目);
}
无效调试(列表*列表){
printf(“%i:[”,列表->计数);
列出每个项目(列出调试项目);
printf(“]\n”);
}
int main(){
List*cats=List_new();
调试(猫);
添加列表(猫,“巴尔塔扎”);
添加列表(猫,“疤痕”);
添加列表(猫,“加菲猫”);
调试(猫);
免费列表(猫);
}

牛角面包可能就是这个意思:

#define NULL 0 // in some include file.
这是不寻常的,但也是可能的。现在假设0是一个4字节的int,void**数组是一个8字节的指针:

void* array_new(){
    void** array = malloc(sizeof(NULL)); // this works
    array[0] = NULL;                     // this write 8 bytes
    return array;
}

array[0]=NULL
将0解释为8字节指针,并将其写入分配的4字节及以上

您的
数组长度是O(N)。如果在数组中添加大量项,这将导致二次运行时,这可能会非常慢

相反,您可以使用结构来存储数组的大小

struct dyn_array {
    size_t array_size; // how many elements fit in the array?
    size_t nelems;     // how much array space are we using now? 
    int *elems; //or void*, you know the drill...
}

这种方法的好处之一是您不再需要使用空终止符。它还允许您使用int数组(或其他任何数组),而不仅仅是指针数组。

malloc(sizeof(NULL))
如果
sizeof(NULL)
。对于某些类型的T,更安全的习惯用法是
T*p=malloc(sizeof*p)
。无论如何,这种以0结尾的数组非常难看,容易出错,线性时间长度查询可能会成为大型数组的性能问题。实现动态数组时的通常做法是维护一个基指针和一个显式大小。嘿,谢谢你的评论。1.为什么
malloc(sizeof(NULL))
会失败?基本上我只需要在流的末尾存储一个整数0的空间,对吗?不管实际的数据类型是什么。2.我相信性能论证,这是有道理的,谢谢。3.你能详细说明一下错误吗?我能预料到的错误是什么?为什么这么难看?(不是最初的回答者)我认为在大多数vector实现中,容量是2>=大小的幂。您可能希望以类似的方式重新定位。我可以看到容易出错的问题,如果出于某种原因需要存储NULL。在大型项目中,这可能是由于某人的粗心大意造成的。如果性能在应用程序中很重要,那么它也会影响性能。似乎您也可以使用堆栈,并在底部保留null,然后在您希望从底部读取时查找它,如果您不想存储另一个指针/var,也不会经常查找底部。这将非常缓慢。C中的公共向量实现做了两件事:首先,在结构中保留当前分配大小、当前占用大小、指向内存块的指针和其他数据。此外,他们最初分配一些小的空间,比如8个或10个项目,然后在需要时分配更多的空间,而不是每次。一个常见的策略是在需要时增加1.5倍,这样插入上的性能可以很好地扩展。@Hr.Rabe“基本上我只需要在流的末尾存储一个整数0的空间,对吗?不管实际的数据类型。”-不,这是错误的。在C语言中,数组是同质的。如果要0-终止
T
s数组,则需要使用
(T)0来终止它。“你能详细说明一下一位的错误吗?我能预料到的错误是什么?”-首先,你可能忘记了0-终止符。(人们忘记在任何地方都使用NUL终止字符串,即使是专家。)“为什么它这么难看?”-因为它破坏了对称性,并且你失去了部分域…所以你要说的是,
sizeof(NULL)
实际上可以给出与
NULL
定义的含义不同的大小?这真的会发生吗?oonull可能没有任何意义。Stroustrup强烈建议不要使用任何看起来像“NULL”且在包含文件中定义的内容。他说如果你需要一个0,就写0,如果你需要一个0指针,就写0。如果他被迫定义NULL,那就是
define NULL 0
,这对我来说是非常合理的。0始终可以解释为指针,它可能会更改大小。因此,在将NULL赋值给指针类型的变量的实例中,它会改变它的类型和大小。@Hans_Klünder:这并不合适
void* array_new(){
    void** array = malloc(sizeof(NULL)); // this works
    array[0] = NULL;                     // this write 8 bytes
    return array;
}
struct dyn_array {
    size_t array_size; // how many elements fit in the array?
    size_t nelems;     // how much array space are we using now? 
    int *elems; //or void*, you know the drill...
}