【问题标题】:Element-wise shifting from smaller array to a larger array逐元素从较小的数组转移到较大的数组
【发布时间】:2020-05-18 20:33:39
【问题描述】:

我正在 Arduino 框架中编写 ESP32。对于我的应用程序,我需要创建一个缓冲区来存储当前和上次访问时的信息。这是我正在尝试做的事情。

//first buffer
char buffer1[4];

//second buffer
char buffer2[8];

void setup {
    //setup
}

//buffer1 values will change with each iteration of loop from external inputs
//buffer2 must store most recent values of buffer1 plus values of buffer1 from when loop last ran

for example:

**loop first iteration**
void loop {
    buffer1[0] = {1};
    buffer1[1] = {2};
    buffer1[2] = {3};
    buffer1[3] = {1};

    saveold(); //this is the function I'm trying to implement to save values to buffer2 in an element-wise way
}
//value of buffer2 should now be: buffer2 = {1,2,3,1,0,0,0,0}

**loop second iteration**
void loop {
    buffer1[0] = {2};
    buffer1[1] = {3};
    buffer1[2] = {4};
    buffer1[3] = {2};

    saveold();
}

//value of buffer2 should now be: buffer2 = {2,3,4,2,1,2,3,1}

根据我通过在线搜索了解到的情况,我正在尝试制作的“saveold”功能 应该为这些数组操作实现某种形式的 memmove

我尝试将其拼凑起来,但我总是覆盖 buffer2 的值,而不是以某种方式将新值移入,同时保留旧值

这就是我所拥有的:

void saveold() {
  memmove(&buffer2[0], &buffer1[0], (sizeof(buffer1[0]) * 4));
}

据我了解,这会将 buffer1 从索引位置 0 开始复制到 buffer2,从索引位置 0 开始,共 4 个字节(其中 1 个字符 = 1 个字节)。

计算机科学不是我的背景,所以也许我缺少一些基本的解决方案或策略。任何指针将不胜感激。

【问题讨论】:

  • 你并没有真正移动任何东西。您每次只是从buffer1 复制到buffer2 的同一位置。我个人不会费心移动实际数据,而是保留一个标志,告诉您在每次调用时开始复制到哪个字节。但是,如果您真的必须复制,请执行以下操作:memmove(&buffer2[4], &buffer2[0], (sizeof(buffer2[0]) * 4)); memcpy(&buffer2[0], &buffer1[0], (sizeof(buffer1[0]) * 4));
  • 选择 C ​​或 C++,不要同时选择两者。最佳答案取决于语言。

标签: c++ arduino embedded arduino-c++


【解决方案1】:

您有多种选择来实现saveold()

解决方案 1

void saveold() {
    // "shift" lower half into upper half, saving recent values (actually it's a copy)
    buffer2[4] = buffer2[0];
    buffer2[5] = buffer2[1];
    buffer2[6] = buffer2[2];
    buffer2[7] = buffer2[3];
    // copy current values
    buffer2[0] = buffer[0];
    buffer2[1] = buffer[1];
    buffer2[2] = buffer[2];
    buffer2[3] = buffer[3];
}

解决方案 2

void saveold() {
    // "shift" lower half into upper half, saving recent values (actually it's a copy)
    memcpy(buffer2 + 4, buffer2 + 0, 4 * sizeof buffer2[0]);
    // copy current values
    memcpy(buffer2 + 0, buffer1, 4 * sizeof buffer1[0]);
}

一些注意事项

  • 还有更多方法可以做到这一点。不管怎样,选择你最了解的。
  • 确保buffer2 的大小正好是buffer1 的两倍。
  • 如果源和目标不重叠,memcpy() 可以安全使用。 memmove() 检查重叠并做出相应反应。
  • &buffer1[0]buffer1 + 0 相同。随意使用您更容易理解的表达方式。
  • sizeof 是一个运算符,而不是一个函数。所以sizeof buffer[0] 的大小等于buffer[0]。计算数组维度大小的常用且最被接受的表达式是sizeof buffer1 / sizeof buffer1[0]。如果您评估数据类型的大小,您只需要括号,例如sizeof (int)

解决方案 3

最后一个注释直接导致解决方案1的这种改进:

void saveold() {
    // "shift" lower half into upper half, saving recent values
    size_t size = sizeof buffer2 / sizeof buffer2[0];
    for (int i = 0; i < size / 2; ++i) {
        buffer2[size / 2 + i] = buffer2[i];
    }
    // copy current values
    for (int i = 0; i < size / 2; ++i) {
        buffer2[i] = buffer1[i];
    }
}

将此知识应用于解决方案 2 留给您作为练习。 ;-)

【讨论】:

  • 在微控制器上使用 memcpy 几乎从来都不是正确的解决方案。在这种系统中人们使用 memcpy 的大约 95% 的情况下,他们应该使用指针交换来代替。尽管在这种特定情况下,最好的解决方案似乎是保留单个环形缓冲区 - 假设没有硬件限制,例如强制双缓冲区设计的 DMA,问题中没有任何说明。
  • @Lundin 如果您需要性能并拥有足够的 RAM,那就对了。 memcpy() 的另一种方式是可以的。正如我所写,还有更多的可能性,这一切都取决于要求和环境。我刚刚拿起他所在的OP。
  • OP 的代码原样已经有多个缓冲区,因此只需进行少量更改,正确的解决方案是进行指针交换。
  • 老实说,解决方案 1 的简洁性非常吸引人。它对我的应用程序也很有效,所以我会坚持下去。但是,出于学习目的,我也会尝试实现一个环形缓冲区(现在我知道它叫什么了)。这很有教育意义!
【解决方案2】:

执行此操作的正确方法是使用缓冲区指针,不是通过硬拷贝备份。使用 memcpy 进行硬拷贝在 AVR 等速度较慢的传统微控制器上尤其糟糕。不太清楚这个 ESP32 得到了什么 MCU,似乎是来自 Tensilica 的一些奇怪的 MCU。无论如何,这个答案普遍适用于任何数据超过 CPU 数据字长的处理器。

也许我缺少一些基本的解决方案或策略。

确实 - 听起来您正在寻找的是一个 环形缓冲区。也就是说,一个固定大小的数组,它有一个指向有效数据开头的指针,另一个指向数据末尾的指针。您移动的是指针,而不是数据。与使用 memcpy 制作简单的硬拷贝相比,这在执行速度和 RAM 使用方面都更加高效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-09
    • 1970-01-01
    • 2018-06-26
    • 2020-07-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多