Arduino中的向量

60

我正在Arduino上创建一个“路点”向量。每个路点都是一个对象。显然,Arduino需要存储多个路点以进行路径导航。但是,用户需要能够添加,删除路点并移动它们,而不是将这些路点存储在标准预编程数组中。不幸的是,Arduino没有内置向量类型作为库。

我目前正在考虑两种选择:

  1. Container for objects like C++ 'vector'?中,有人发布了一个通用库。它不包含任何索引删除或移动操作,但它包含一些内存管理策略。

  2. 我过去使用过malloc,dealloc和calloc。但是,我不喜欢这个选项,特别是对于类。但这是我场景下更好的选择吗?

哪个方案更好?


4
你可能想在这里查看:http://andybrown.me.uk/ws/2011/01/15/the-standard-template-library-stl-for-avr-with-c-streams/ - paulsm4
在任何安全关键设备上,我都不会在Arduino上使用std::vector<>或任何其他在运行时背后进行动态内存分配的类型。这会为Arduino打开潜在的严重、不安全的崩溃风险,因为堆栈溢出。相反,您需要的是一个固定大小的内存池,它是静态分配的,或者在程序初始化时动态分配一次,但在运行时永远不会增加。您的向量应该是一个自定义向量类或库,在运行时使用这个固定大小的内存池。 - Gabriel Staples
我将我的评论变成了一个新的回答。 - Gabriel Staples
7个回答

67

哇,那个库集合非常令人印象深刻。感谢你找到它! - jakebird451
8
顺便提一下,说出你使用的Arduino类型非常有帮助。适用于Mega 2560的解决方案不太可能适用于较小且较旧的Arduino。这只是一个建议,如果不需要可以忽略 :) - Julie in Austin
2
这个库非常棒。 - Z2VvZ3Vp
我在使用上述库集时遇到了使用fstream库的问题。#include<fstream>会给我以下错误提示:致命错误:unistd.h:没有那个文件或目录。 - S. Salman
4
还有一个替代品,更近期的项目ArduinoSTL,同样适用于Arduino Nano。 - Suuuehgi
截至2021年3月,另一个ArduinoSTL的分支似乎更加更新。 - John Cummings

7
您可以编写这个LinkedList模板类,并在需要的地方直接调用它:
#ifndef LinkedList_hpp
#define LinkedList_hpp


template <class T>
class ListNode {
  public:
    T element;
    ListNode* next;
    ListNode* prev;

    ListNode(T element, ListNode* prev, ListNode* next) : element(element)
    {
      this->next = next;
      this->prev = prev;
    };
};

template <class T>
class LinkedList  {
  private:
    int length;
    ListNode<T>* head;
    ListNode<T>* tail;
    ListNode<T>* curr;
  public:
    LinkedList();
    LinkedList(const LinkedList<T>&);
    ~LinkedList();
    T& getCurrent();
    T& First() const;
    T& Last() const;
    int getLength();
    void Append(T);
    void DeleteLast();
    void DeleteFirst();
    void DeleteCurrent();
    bool next();
    bool moveToStart();
    bool prev();
    void Delete(T&);
    bool Search(T);
    void Clear();
    void PutFirstToLast();
    void Update(T elem);
    LinkedList& operator = (const LinkedList<T>&);
};

template <class T>
LinkedList<T>::LinkedList() {
    length = 0;
    head = nullptr;
    tail = nullptr;
    curr = nullptr;
}

template <class T>
LinkedList<T>::LinkedList(const LinkedList<T> & list) {
    length = 0;
    head = nullptr;
    tail = nullptr;
    curr = nullptr;

    ListNode<T> * temp = list.head;

    while(temp != nullptr)
    {
        Append(temp->element);
        temp = temp->next;
    }
}

template <class T>
LinkedList<T> & LinkedList<T>::operator=(const LinkedList<T> & list)
{
    Clear();

    ListNode<T> * temp = list.head;

    while(temp != nullptr)
    {
        Append(temp->element);
        temp = temp->next;
    }

    return *this;
}

template <class T>
LinkedList<T>::~LinkedList() {
    Clear();
}

template<class T>
T& LinkedList<T>::getCurrent()
{
  return curr->element;
}

template<class T>
T& LinkedList<T>::First() const
{
  return head->element;
}

template<class T>
T& LinkedList<T>::Last() const
{
  return tail->element;
}

template<class T>
int LinkedList<T>::getLength()
{
  return length;
}

template <class T>
void LinkedList<T>::Append(T element)
{
    ListNode<T> * node = new ListNode<T>(element, tail, nullptr);

    if(length == 0)
        curr = tail = head = node;
    else {
        tail->next = node;
        tail = node;
    }

    length++;

}

template <class T>
void LinkedList<T>::DeleteLast()
{
    if(length == 0)
      return;
    curr = tail;
    DeleteCurrent();
}

template <class T>
void LinkedList<T>::DeleteFirst()
{
    if(length == 0)
      return;
    curr = head;
    DeleteCurrent();
}

template <class T>
bool LinkedList<T>::next()
{
    if(length == 0)
        return false;

    if(curr->next == nullptr)
        return false;

    curr = curr->next;
    return true;
}

template <class T>
bool LinkedList<T>::moveToStart()
{
    curr = head;
    return length != 0;
}

template<class T>
bool LinkedList<T>::prev()
{
    if(length == 0)
        return false;

    if(curr->prev != nullptr)
        return false;

    curr = curr->prev;
    return true;
}

template <class T>
void LinkedList<T>::Delete(T & elem)
{
    if(Search(elem))
        DeleteCurrent();
}

template <class T>
void LinkedList<T>::DeleteCurrent()
{
    if(length == 0)
        return;
    length--;
    ListNode<T> * temp = curr;

    if(temp->prev != nullptr)
        temp->prev->next = temp->next;
    if(temp->next != nullptr)
        temp->next->prev = temp->prev;

    if(length == 0)
        head = curr = tail = nullptr;
    else if(curr == head)
        curr = head = head->next;
    else if(curr == tail)
        curr = tail = tail->prev;
    else
        curr = curr->prev;

     delete temp;
}

template <class T>
bool LinkedList<T>::Search(T elem)
{
    if(length == 0)
        return false;
    if(moveToStart())
        do {
            if(curr->element == elem)
                return true;
        } while (next());
    return false;
}

template <class T>
void LinkedList<T>::PutFirstToLast()
{
  if(length < 2)
    return;
  ListNode<T>* temp = head->next;
  head->next->prev = nullptr;
  head->next = nullptr;
  head->prev = tail;
  tail->next = head;
  tail = head;
  head = temp;
}

template <class T>
void LinkedList<T>::Update(T elem)
{
    if(Search(elem))
        curr->element = elem;
}

template <class T>
void LinkedList<T>::Clear()
{
    if(length == 0)
        return;
    ListNode<T> * temp = head;

    while(temp != nullptr)
    {
        head = head->next;
        delete temp;
        temp = head;
    }

    head = curr = tail = nullptr;

    length = 0;

}


#endif

请按照以下方式使用这个类:
LinkedList<int> list;

list.Append(1);
list.Append(2);
list.Append(3);
list.Append(4);

int my_integer;

if(list.moveToStart())
    do{
        my_integer = list.getCurrent();
    }while(list.next());

1
你的类很棒!谢谢。Clear函数中有一个错误,它没有将长度设置为0。 - lgm42

4
听起来你需要实现一个简单的链表。链表可以让你移动对象(在你的情况下是航点),而不会像C++向量一样承担额外的开销。
这里有一个实现在GitHub上

我没有完全理解你在Github顶部的示例。有可能您能展示一些示例代码和期望结果,类似于php.net风格吗? - Krista K
1
虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅链接的答案可能会失效。- 来自审查 - kritzikratzi
8
@kritzikratzi已经出现了404错误。 - Steven Jeffries

3

由于Arduino内存有限,因此您需要知道允许的航点数量。在这种情况下,一个简单的数组来保存已分配航点的内存指针(地址)将提供所需的顺序/顺序。保持一个数组插槽作为工作区将允许航点被移动(重新排序)。


2
我不会在Arduino上使用std::vector<>或任何其他在运行时背后执行动态内存分配的类型,无论在任何安全关键设备上都是如此。这会使Arduino面临潜在的严重、不安全的崩溃风险,因为它会导致堆栈溢出。
相反,你需要一个静态分配的固定大小内存池,或者在程序初始化时动态分配一次,但在运行时永远不增加。你的"vector"应该是一个自定义向量类、数组、链表或库,它在运行时使用这个固定大小的内存池。
引用:
用户将需要能够添加、删除航点并移动它们。
在我看来,最简单的方法是使用静态分配的结构体数组作为内存池。静态数组中的每个结构体都是一个链表节点。
你有一个固定的最大节点数,以防止堆栈溢出。现在可以添加,“删除”和重新排列路径点的顺序。添加和“删除”只需从链接列表中删除节点,重新排列则通过更改所指向的节点及其顺序来完成。
这现在可以在对于Arduino这样的安全关键、内存受限的微控制器上完全安全地执行。同样,结构体的静态数组是你的“内存池”,并且你将使用手动实现的节点链表,所有这些节点都位于该静态结构数组中。

1
你还可以拥有一个固定的航路点结构数组,并在结构中包含一个变量,以确定航路点是否正在使用。添加航路点时,只需循环遍历数组,直到找到未被使用的结构即可。

1
如果您想创建一个int向量,Arduino具有以下内存规格。在Arduino Uno(和其他基于ATmega的板子)中,int存储16位(2字节)值。这保证了从-32.768到32.767的范围(最小值为-2 ^ 15,最大值为(2 ^ 15)-1)。在Arduino Due和其他基于SAMD计算机的板子上(例如MKR1000和Zero),int存储32位值(4字节)。这保证了从-2,147,483,648到2,147,483,647的范围(最小值为-2 ^ 31,最大值为(2 ^ 31)-1)。在这里查看更多信息

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接