将元素添加到固定长度数组并删除第一个元素的干净方法?

时间:2019-02-13 22:01:22

标签: c

我需要一种干净的方法将元素添加到数组中,如果数组“溢出”,则必须将0处的元素替换为1处的元素,依此类推,直到为新元素释放空间为止。

这是一个示例(伪代码):

array = [0, 0, 0, 0, 0]
// elements get added
array = [1, 0, 0, 0, 0]
...
array = [1, 2, 3, 4, 5]
// Array is full!
// Another elements gets added
array = [2, 3, 4, 5, 6]
// And so on..

我尝试在在线编译器上执行此操作,但失败很惨,这是代码:

int main()
{
    int arr[10] = { 555, 555, 555, 555, 555, 555, 555, 555, 555, 555 };

    for (int i = 0; i < 100; i++)
    {
        int arr_idx = i % 10;

        if (arr[10] != 555)
        {
            if (arr_idx < 9)
            {
                arr[arr_idx] = arr[arr_idx + 1];
            }
            else
            {
                arr[arr_idx] = 0;
            }
        }
        else
        {
            arr[arr_idx] = i;
            printf("arr: %d", arr[arr_idx]);
        }

        printf("---\n");
        for (int j = 0; j < 10; j++)
        {
            printf("[%d]Arr = %d\n", j, arr[j]);
        }
    }
    return 0;
}

1 个答案:

答案 0 :(得分:0)

环形缓冲区解决方案和“随机缓冲区”解决方案都不是特别复杂。这是每个。请注意,环形缓冲区解决方案将16个数组中的15个值存储在一个数组中。随机缓冲解决方案使用大小为15的数组。如果将诸如( 1: 30)(99: 30)之类的条目进行映射,以解决数据存储方式的差异,则解决方案将提供相同的输出顺序

这两种解决方案都假定您了解结构(以及结构的指针)。

随机播放缓冲区

这与问题中的代码最接近。

#include <stdbool.h>
#include <stdio.h>
#include <string.h>

enum { SB_SIZE = 15 };

typedef int Data;
#define DATA_PRI_FMT "d"

struct ShuffleBuffer
{
   size_t sb_last;
   Data   sb_data[SB_SIZE];
};
typedef struct ShuffleBuffer ShuffleBuffer;

static inline void sb_shuffle(ShuffleBuffer *sbp)
{
    if (sbp->sb_last > 0)
    {
        memmove(sbp->sb_data, sbp->sb_data + 1, (SB_SIZE - 1) * sizeof(sbp->sb_data[0]));
        sbp->sb_last--;
    }
}

static void sb_insert(ShuffleBuffer *sbp, Data value)
{
    if (sbp->sb_last == SB_SIZE)
        sb_shuffle(sbp);
    sbp->sb_data[sbp->sb_last++] = value;
}

static bool sb_remove(ShuffleBuffer *sbp, Data *valuep)
{
    if (sbp->sb_last == 0)
        return false;
    *valuep = sbp->sb_data[0];
    sb_shuffle(sbp);
    return true;
}

static void sb_print(const char *tag, const ShuffleBuffer *sbp)
{
    printf("%s: (last = %zu)\n", tag, sbp->sb_last);
    int nbytes = 0;
    const char *pad = "";
    for (size_t i = 0; i < sbp->sb_last; i++)
    {
        nbytes += printf("%s(%2zu: %3" DATA_PRI_FMT ")", pad, i, sbp->sb_data[i]);
        if (nbytes > 40)
        {
            putchar('\n');
            nbytes = 0;
            pad = "";
        }
        else
            pad = " ";
    }
    if (nbytes != 0)
        putchar('\n');
}

int main(void)
{
    ShuffleBuffer rb = { 0, { 0 } };

    for (Data i = 0; i < 100; i++)
    {
        sb_insert(&rb, i * 7 + 23);
        sb_print("Post insert", &rb);
        if ((i & 1) == 1)
        {
            Data value;
            if (sb_remove(&rb, &value))
                printf("Value %" DATA_PRI_FMT " removed\n", value);
            else
                sb_print("Ring Buffer Empty", &rb);
        sb_print("Post remove", &rb);
        }
    }

    printf("Insert/remove loop over\n");

    Data value;
    while (sb_remove(&rb, &value))
        printf("Value %" DATA_PRI_FMT " removed\n", value);

    return 0;
}

环形缓冲区

#include <stdbool.h>
#include <stdio.h>

enum { RB_SIZE = 16 };

typedef int Data;
#define DATA_PRI_FMT "d"

struct RingBuffer
{
   size_t rb_head;
   size_t rb_tail;
   Data   rb_data[RB_SIZE];
};
typedef struct RingBuffer RingBuffer;

static inline size_t rb_nextpos(size_t pos)
{
    return (pos + 1) % RB_SIZE;
}

static void rb_insert(RingBuffer *rbp, Data value)
{
    rbp->rb_data[rbp->rb_head] = value;
    rbp->rb_head = rb_nextpos(rbp->rb_head);
    if (rbp->rb_tail == rbp->rb_head)
        rbp->rb_tail = rb_nextpos(rbp->rb_tail);
}

static bool rb_remove(RingBuffer *rbp, Data *valuep)
{
    if (rbp->rb_head == rbp->rb_tail)
        return false;
    *valuep = rbp->rb_data[rbp->rb_tail];
    rbp->rb_tail = rb_nextpos(rbp->rb_tail);
    return true;
}

static void rb_print(const char *tag, const RingBuffer *rbp)
{
    printf("%s: (head = %zu, tail = %zu)\n", tag, rbp->rb_head, rbp->rb_tail);
    int nbytes = 0;
    const char *pad = "";
    for (size_t i = rbp->rb_tail; i != rbp->rb_head; i = rb_nextpos(i))
    {
        nbytes += printf("%s(%2zu: %3" DATA_PRI_FMT ")", pad, i, rbp->rb_data[i]);
        if (nbytes > 40)
        {
            putchar('\n');
            nbytes = 0;
            pad = "";
        }
        else
            pad = " ";
    }
    if (nbytes != 0)
        putchar('\n');
}

int main(void)
{
    RingBuffer rb = { 0, 0, { 0 } };

    for (Data i = 0; i < 100; i++)
    {
        rb_insert(&rb, i * 7 + 23);
        rb_print("Post insert", &rb);
        if ((i & 1) == 1)
        {
            Data value;
            if (rb_remove(&rb, &value))
                printf("Value %" DATA_PRI_FMT " removed\n", value);
            else
                rb_print("Ring Buffer Empty", &rb);
        rb_print("Post remove", &rb);
        }
    }

    printf("Insert/remove loop over\n");

    Data value;
    while (rb_remove(&rb, &value))
        printf("Value %" DATA_PRI_FMT " removed\n", value);

    return 0;
}

如果您的编译器过时,以至于无法将inline识别为关键字,只需在文件顶部附近添加#define inline /* C99 not available */(或者,最好让编译器能够识别几乎20岁的标准)。

环形缓冲区的采样输出

Post insert: (head = 1, tail = 0)
( 0:  23)
Post insert: (head = 2, tail = 0)
( 0:  23) ( 1:  30)
Value 23 removed
Post remove: (head = 2, tail = 1)
( 1:  30)
Post insert: (head = 3, tail = 1)
( 1:  30) ( 2:  37)
Post insert: (head = 4, tail = 1)
( 1:  30) ( 2:  37) ( 3:  44)
Value 30 removed
Post remove: (head = 4, tail = 2)
( 2:  37) ( 3:  44)
Post insert: (head = 5, tail = 2)
( 2:  37) ( 3:  44) ( 4:  51)
Post insert: (head = 6, tail = 2)
( 2:  37) ( 3:  44) ( 4:  51) ( 5:  58)
Value 37 removed
Post remove: (head = 6, tail = 3)
( 3:  44) ( 4:  51) ( 5:  58)
Post insert: (head = 7, tail = 3)
( 3:  44) ( 4:  51) ( 5:  58) ( 6:  65)
Post insert: (head = 8, tail = 3)
( 3:  44) ( 4:  51) ( 5:  58) ( 6:  65) ( 7:  72)
Value 44 removed
Post remove: (head = 8, tail = 4)
( 4:  51) ( 5:  58) ( 6:  65) ( 7:  72)
Post insert: (head = 9, tail = 4)
( 4:  51) ( 5:  58) ( 6:  65) ( 7:  72) ( 8:  79)
Post insert: (head = 10, tail = 4)
( 4:  51) ( 5:  58) ( 6:  65) ( 7:  72) ( 8:  79)
( 9:  86)
Value 51 removed
Post remove: (head = 10, tail = 5)
( 5:  58) ( 6:  65) ( 7:  72) ( 8:  79) ( 9:  86)
Post insert: (head = 11, tail = 5)
( 5:  58) ( 6:  65) ( 7:  72) ( 8:  79) ( 9:  86)
(10:  93)
Post insert: (head = 12, tail = 5)
( 5:  58) ( 6:  65) ( 7:  72) ( 8:  79) ( 9:  86)
(10:  93) (11: 100)
Value 58 removed
Post remove: (head = 12, tail = 6)
( 6:  65) ( 7:  72) ( 8:  79) ( 9:  86) (10:  93)
(11: 100)
Post insert: (head = 13, tail = 6)
( 6:  65) ( 7:  72) ( 8:  79) ( 9:  86) (10:  93)
(11: 100) (12: 107)
Post insert: (head = 14, tail = 6)
( 6:  65) ( 7:  72) ( 8:  79) ( 9:  86) (10:  93)
(11: 100) (12: 107) (13: 114)
Value 65 removed
Post remove: (head = 14, tail = 7)
( 7:  72) ( 8:  79) ( 9:  86) (10:  93) (11: 100)
(12: 107) (13: 114)
Post insert: (head = 15, tail = 7)
( 7:  72) ( 8:  79) ( 9:  86) (10:  93) (11: 100)
(12: 107) (13: 114) (14: 121)
Post insert: (head = 0, tail = 7)
( 7:  72) ( 8:  79) ( 9:  86) (10:  93) (11: 100)
(12: 107) (13: 114) (14: 121) (15: 128)
Value 72 removed
Post remove: (head = 0, tail = 8)
( 8:  79) ( 9:  86) (10:  93) (11: 100) (12: 107)
(13: 114) (14: 121) (15: 128)
Post insert: (head = 1, tail = 8)
( 8:  79) ( 9:  86) (10:  93) (11: 100) (12: 107)
(13: 114) (14: 121) (15: 128) ( 0: 135)
Post insert: (head = 2, tail = 8)
( 8:  79) ( 9:  86) (10:  93) (11: 100) (12: 107)
(13: 114) (14: 121) (15: 128) ( 0: 135) ( 1: 142)
Value 79 removed
Post remove: (head = 2, tail = 9)
( 9:  86) (10:  93) (11: 100) (12: 107) (13: 114)
(14: 121) (15: 128) ( 0: 135) ( 1: 142)
Post insert: (head = 3, tail = 9)
( 9:  86) (10:  93) (11: 100) (12: 107) (13: 114)
(14: 121) (15: 128) ( 0: 135) ( 1: 142) ( 2: 149)
Post insert: (head = 4, tail = 9)
( 9:  86) (10:  93) (11: 100) (12: 107) (13: 114)
(14: 121) (15: 128) ( 0: 135) ( 1: 142) ( 2: 149)
( 3: 156)
Value 86 removed
Post remove: (head = 4, tail = 10)
(10:  93) (11: 100) (12: 107) (13: 114) (14: 121)
(15: 128) ( 0: 135) ( 1: 142) ( 2: 149) ( 3: 156)
Post insert: (head = 5, tail = 10)
(10:  93) (11: 100) (12: 107) (13: 114) (14: 121)
(15: 128) ( 0: 135) ( 1: 142) ( 2: 149) ( 3: 156)
( 4: 163)
Post insert: (head = 6, tail = 10)
(10:  93) (11: 100) (12: 107) (13: 114) (14: 121)
(15: 128) ( 0: 135) ( 1: 142) ( 2: 149) ( 3: 156)
( 4: 163) ( 5: 170)
Value 93 removed
Post remove: (head = 6, tail = 11)
(11: 100) (12: 107) (13: 114) (14: 121) (15: 128)
( 0: 135) ( 1: 142) ( 2: 149) ( 3: 156) ( 4: 163)
( 5: 170)
Post insert: (head = 7, tail = 11)
(11: 100) (12: 107) (13: 114) (14: 121) (15: 128)
( 0: 135) ( 1: 142) ( 2: 149) ( 3: 156) ( 4: 163)
( 5: 170) ( 6: 177)
Post insert: (head = 8, tail = 11)
(11: 100) (12: 107) (13: 114) (14: 121) (15: 128)
( 0: 135) ( 1: 142) ( 2: 149) ( 3: 156) ( 4: 163)
( 5: 170) ( 6: 177) ( 7: 184)
Value 100 removed
Post remove: (head = 8, tail = 12)
(12: 107) (13: 114) (14: 121) (15: 128) ( 0: 135)
( 1: 142) ( 2: 149) ( 3: 156) ( 4: 163) ( 5: 170)
( 6: 177) ( 7: 184)
Post insert: (head = 9, tail = 12)
(12: 107) (13: 114) (14: 121) (15: 128) ( 0: 135)
( 1: 142) ( 2: 149) ( 3: 156) ( 4: 163) ( 5: 170)
( 6: 177) ( 7: 184) ( 8: 191)
Post insert: (head = 10, tail = 12)
(12: 107) (13: 114) (14: 121) (15: 128) ( 0: 135)
( 1: 142) ( 2: 149) ( 3: 156) ( 4: 163) ( 5: 170)
( 6: 177) ( 7: 184) ( 8: 191) ( 9: 198)
Value 107 removed
Post remove: (head = 10, tail = 13)
(13: 114) (14: 121) (15: 128) ( 0: 135) ( 1: 142)
( 2: 149) ( 3: 156) ( 4: 163) ( 5: 170) ( 6: 177)
( 7: 184) ( 8: 191) ( 9: 198)
Post insert: (head = 11, tail = 13)
(13: 114) (14: 121) (15: 128) ( 0: 135) ( 1: 142)
( 2: 149) ( 3: 156) ( 4: 163) ( 5: 170) ( 6: 177)
( 7: 184) ( 8: 191) ( 9: 198) (10: 205)
Post insert: (head = 12, tail = 13)
(13: 114) (14: 121) (15: 128) ( 0: 135) ( 1: 142)
( 2: 149) ( 3: 156) ( 4: 163) ( 5: 170) ( 6: 177)
( 7: 184) ( 8: 191) ( 9: 198) (10: 205) (11: 212)
Value 114 removed
Post remove: (head = 12, tail = 14)
(14: 121) (15: 128) ( 0: 135) ( 1: 142) ( 2: 149)
( 3: 156) ( 4: 163) ( 5: 170) ( 6: 177) ( 7: 184)
( 8: 191) ( 9: 198) (10: 205) (11: 212)
Post insert: (head = 13, tail = 14)
(14: 121) (15: 128) ( 0: 135) ( 1: 142) ( 2: 149)
( 3: 156) ( 4: 163) ( 5: 170) ( 6: 177) ( 7: 184)
( 8: 191) ( 9: 198) (10: 205) (11: 212) (12: 219)
Post insert: (head = 14, tail = 15)
(15: 128) ( 0: 135) ( 1: 142) ( 2: 149) ( 3: 156)
( 4: 163) ( 5: 170) ( 6: 177) ( 7: 184) ( 8: 191)
( 9: 198) (10: 205) (11: 212) (12: 219) (13: 226)
Value 128 removed
Post remove: (head = 14, tail = 0)
( 0: 135) ( 1: 142) ( 2: 149) ( 3: 156) ( 4: 163)
( 5: 170) ( 6: 177) ( 7: 184) ( 8: 191) ( 9: 198)
(10: 205) (11: 212) (12: 219) (13: 226)
Post insert: (head = 15, tail = 0)
( 0: 135) ( 1: 142) ( 2: 149) ( 3: 156) ( 4: 163)
( 5: 170) ( 6: 177) ( 7: 184) ( 8: 191) ( 9: 198)
(10: 205) (11: 212) (12: 219) (13: 226) (14: 233)
Post insert: (head = 0, tail = 1)
( 1: 142) ( 2: 149) ( 3: 156) ( 4: 163) ( 5: 170)
( 6: 177) ( 7: 184) ( 8: 191) ( 9: 198) (10: 205)
(11: 212) (12: 219) (13: 226) (14: 233) (15: 240)
Value 142 removed
Post remove: (head = 0, tail = 2)
( 2: 149) ( 3: 156) ( 4: 163) ( 5: 170) ( 6: 177)
( 7: 184) ( 8: 191) ( 9: 198) (10: 205) (11: 212)
(12: 219) (13: 226) (14: 233) (15: 240)
Post insert: (head = 1, tail = 2)
( 2: 149) ( 3: 156) ( 4: 163) ( 5: 170) ( 6: 177)
( 7: 184) ( 8: 191) ( 9: 198) (10: 205) (11: 212)
(12: 219) (13: 226) (14: 233) (15: 240) ( 0: 247)
…
Post insert: (head = 3, tail = 4)
( 4: 611) ( 5: 618) ( 6: 625) ( 7: 632) ( 8: 639)
( 9: 646) (10: 653) (11: 660) (12: 667) (13: 674)
(14: 681) (15: 688) ( 0: 695) ( 1: 702) ( 2: 709)
Post insert: (head = 4, tail = 5)
( 5: 618) ( 6: 625) ( 7: 632) ( 8: 639) ( 9: 646)
(10: 653) (11: 660) (12: 667) (13: 674) (14: 681)
(15: 688) ( 0: 695) ( 1: 702) ( 2: 709) ( 3: 716)
Value 618 removed
Post remove: (head = 4, tail = 6)
( 6: 625) ( 7: 632) ( 8: 639) ( 9: 646) (10: 653)
(11: 660) (12: 667) (13: 674) (14: 681) (15: 688)
( 0: 695) ( 1: 702) ( 2: 709) ( 3: 716)
Insert/remove loop over
Value 625 removed
Value 632 removed
Value 639 removed
Value 646 removed
Value 653 removed
Value 660 removed
Value 667 removed
Value 674 removed
Value 681 removed
Value 688 removed
Value 695 removed
Value 702 removed
Value 709 removed
Value 716 removed

计时

主循环重复100次,如果禁用打印,这两个程序之间实际上并没有可测量的差异,而如果启用打印,则这几乎无法测量,也不是完全可靠的。

在禁用打印的情况下,缓冲区大小为15或16,在主程序中循环执行一百万次,环形缓冲区花费4.5毫秒,而随机播放缓冲区花费9.0毫秒。将缓冲区大小更改为2047或2048,环形缓冲区的时间为3.7 ms,随机播放缓冲区的时间为87.3 ms。启用打印后,环形缓冲区在打印中所做的少量额外工作会淹没不改组的性能;这两个程序基本上在同一时间运行:4,546.5 ms vs 4,477.2 ms(因此,在启用打印后,混洗缓冲区略微加快了速度-很大程度上是因为它仅产生353 MiB的数据,而环形缓冲区只有368 MiB的数据-并在计时运行的同时将数据写入/dev/null

测量的时间是可执行文件的运行时间,而不是CPU时间本身。测试是在配备2.9 GHz Intel Core i7、16 GiB,2133 MHz LPDDR3 RAM,运行macOS 10.14.3 Mojave并使用自制GCC 8.2.0的15“ MacBook Pro(2017)上进行的。测试很困难-我认为没有打印的结果是有意义的,但是YMMV。如果您需要演示“打印速度慢”,那很可能是一个很好的例子。