用 C 实现一个变长数组

用 C 实现一个变长数组

我想用 C++ 的人都用过 std::vector 。它是一个可变长的数组,在很多时候都比 C 固有的定长数组要灵活。

C 里没有这样的标准化设施,但是写软件的人通常都会实现一个。正所谓,不厌其烦的重造轮子 :D 。这里我们不讨论造轮子的好坏问题,直接讨论下实现这么个东西的一点小技巧吧。总是固执于用谁做的轮子的问题,眼光就太短浅了。

一般的 vector 的实现,需要记录三个数据:数据空间的地址,空间大小,数组里已存在的元素个数。也可能是这三个元素的变体。比如在 msvc 的 stl 实现中,vector 保存的是三个 iterator:数组头指针、最后一个元素的指针、分配出来的全部空间的末尾指针。

msvc 的 vector 的分配策略是这样的。一开始不于分配空间,当第一个元素被 push back 后,空间扩展。通常,扩展的请求至少要满足请求扩展的尺寸,或者是讲已有的空间容量翻倍。比如,当你已经有 16 个单元的数组空间时,如果再申请 3 个,vector 会自己扩展到 32 个单元;若你一次要求从 16 个单元扩展到 40 个单元(超过 16*2),那么就直接扩展到 40 。

记得 stlport 也是类似的策略吧,实现的可能稍有不同。

正是如此,我们在用 vector 的时候要比较小心。如有可能,最好先 reserve 出需要的空间,而不是一个个的 push back 。否则,如果你从 0 个元素,push back 100 次,其空间会重新分配 8 次(1、2、4、8、16、32、64、128)。btw, 如果你经常这样做,最好实现一个优秀的 allocator 。IMHO ,msvc 的 stl 实现并不比 stlport 差,大多数性能差别都是因为 msvc 的 stl 实现未能给出一个通用的,优化过的分配器而已。(实现一个好的内存分配器,对于大的 C++ 软件项目,都值得去做)

今天想说的并不是怎么优化这个策略。是我这几天在写代码时发现,如果你遵照空间加倍这个策略来运作,其实并不需要三个变量来维持 vector 结构,两个就够了。那个表识容量大小的变量是多余的。:D

下面是段示范代码:

struct vector {
    int *data;
    int size;
};

void 
vector_expand(struct vector *v, int s)
{
    int new_size=v->size + s;
    if ((new_size ^ v->size) > v->size) {
       v->data=(int *)realloc(v->data,sizeof(int)*new_size * 2);
    }
}

利用二进制逻辑运算的性质,我们只需要把新的尺寸和老的尺寸异或,再和老的尺寸比较,就可以知道,新的尺寸的二进制最高是 1 的那一位的位置是否大于老的尺寸。然后分配新尺寸的两倍,空间是绰绰有余的。

当然,如果你希望空间分配的恰到好处(2^n -1 ),可以稍微写的繁琐一点,以下代码在 32bit 机器上,可以把一个数字从它的二进制为 1 的最高位到最低位都填成 1 。

unsigned 
foo(unsigned a)
{
    a|=a>>1;
    a|=a>>2;
    a|=a>>4;
    a|=a>>8;
    a|=a>>16;

    return a;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值