Arduino - 什么是存储String数组的正确方法?

时间:2017-11-21 18:24:18

标签: c++ string arduino

在尝试填充String对象的动态数组时,我得到了程序的不可预知的行为。我试图简化程序,但无法弄清楚原因。

仅供参考:debug()debugLn()将数据打印到SerialdebugMem()打印可用内存金额。

因此,最简单的版本按预期工作(场景 - 分配3个字符串的全局数组,定义3个局部变量并将它们的值赋给数组元素):

String* arr;

void setup() {
  Serial.begin(9600);

  debugMem("1");

  String src = "abcdefghijk";

  arr = malloc(3 * sizeof(String));

  debugMem("2");

  String dest1 = src;
  String dest2 = src;
  arr[0] = src.substring(0);
  arr[1] = dest1.substring(0);
  arr[2] = dest2.substring(0);

  debugMem("3");

  dest1.setCharAt(1, 'z');
  dest2.setCharAt(1, 'x');

  debug(F("src: "));
  debugln(src);
  debug(F("src located at "));
  debugln((long)&src);
  debug(F("dest1: "));
  debugln(dest1);
  debug(F("dest1 located at "));
  debugln((long)&dest1);
  debug(F("dest2: "));
  debugln(dest2);
  debug(F("dest2 located at "));
  debugln((long)&dest2);
  debug(F("arr[0]: "));
  debugln(arr[0]);
  debug(F("arr[0] located at "));
  debugln((long)(arr + 0));
  debug(F("arr[1]: "));
  debugln(arr[1]);
  debug(F("arr[1] located at "));
  debugln((long)(arr + 1));
  debug(F("arr[2]: "));
  debugln(arr[2]);
  debug(F("arr[2] located at "));
  debugln((long)(arr + 2));
  debugMem("4");
}

其输出:

>>>>>>>>>>1787<<<<<<<<<<1
>>>>>>>>>>1753<<<<<<<<<<2
>>>>>>>>>>1683<<<<<<<<<<3
src: abcdefghijk
src located at 2294
dest1: azcdefghijk
dest1 located at 2288
dest2: axcdefghijk
dest2 located at 2282
arr[0]: abcdefghijk
arr[0] located at 493
arr[1]: abcdefghijk
arr[1] located at 499
arr[2]: abcdefghijk
arr[2] located at 505
>>>>>>>>>>1683<<<<<<<<<<4

(尽管我对src,dest1,dest2的地址有点困惑,它们的总量大于2048字节的RAM)。

第二个版本实际上是相同的(将值从局部var推送到全局数组),但它的行为是完全不同的&#34; (方案 - 初始化一个虚拟源字符串,在函数中解析它,重新分配全局数组以存储已解析的字符串,从本地变量为每个数组元素分配一个值):

String* phones;
int phonesCount;

void setup() {
  Serial.begin(9600);

  String reply = "1234567890,2345678901,3456789012";

  fillPhones(reply);
}

void loop() {
  debugln(F("loop"));

  for(int i = 0; i < phonesCount; i++) {
    debug(F("Phone"));
    debugln(i);
    debug(F("Phone located at "));
    debugln((long)(phones + i));
    debug(F("Phone: "));
    debugln(*(phones + i));
  }
}

void fillPhones(String reply) {
  phonesCount = parsePhones(reply, phones);  
}

int parsePhones(String reply, String* &phonesArray) {
  debugMem("1");

  phonesArray = NULL;
  int count = 0;

  int fromIndex = 0;
  int toIndex = 0;
  while(toIndex >= 0) {
    debug(F("Phone index: "));
    debugln(count);

    toIndex = reply.indexOf(F(","), fromIndex);
    debug(F("Substring: "));
    debug(fromIndex);
    debug(F(".."));
    debugln(toIndex);
    debugMem("2");

    String entryPhone = reply.substring(fromIndex, toIndex);
    debugMem("3");
    debug(F("Phone to add: "));
    debugln(entryPhone);
    debug(F("Phone located at "));
    debugln((long)&entryPhone);

    String* tmpArray = realloc(phonesArray, (count + 1) * sizeof(String));
    if(tmpArray == NULL) {
      break;
    }
    debug(F("Array allocated bytes: "));
    debugln((count + 1) * sizeof(String));
    debug(F("Array located at "));
    debugln((long)tmpArray);
    debugMem("4");

    phonesArray = tmpArray;
    debugMem("5");

    phonesArray[count] = entryPhone;
    debug(F("Added phone: "));
    debugln(phonesArray[count]);
    debug(F("Added phone located at "));
    debugln((long)(phonesArray + count));
    debugMem("6");

    count++;
    fromIndex = toIndex + 1;
  }
  debug(F("Total phones count:"));
  debugln(count);

  debugMem("7");

  return count;
}

其输出:

>>>>>>>>>>1647<<<<<<<<<<1
Phone index: 0
Substring: 0..10
>>>>>>>>>>1647<<<<<<<<<<2
>>>>>>>>>>1634<<<<<<<<<<3
Phone to add: 1234567890
Phone located at 2273
Array allocated bytes: 6
Array located at 623
>>>>>>>>>>1626<<<<<<<<<<4
>>>>>>>>>>1626<<<<<<<<<<5
Added phone: 1234567890
Added phone located at 623
>>>>>>>>>>1613<<<<<<<<<<6
Phone index: 1
Substring: 11..21
>>>>>>>>>>1626<<<<<<<<<<2
>>>>>>>>>>1613<<<<<<<<<<3
Phone to add: 2345678901
Phone located at 2273
Array allocated bytes: 12
Array located at 657
>>>>>>>>>>1607<<<<<<<<<<4
>>>>>>>>>>1607<<<<<<<<<<5
Added phone: 2345678901
Added phone located at 663
>>>>>>>>>>1607<<<<<<<<<<6
Phone index: 2
Substring: 22..-1
>>>>>>>>>>1620<<<<<<<<<<2
>>>>>>>>>>1607<<<<<<<<<<3
Phone to add: 3456789012
Phone located at 2273
Array allocated bytes: 18
Array located at 657
>>>>>>>>>>1601<<<<<<<<<<4
>>>>>>>>>>1601<<<<<<<<<<5
Added phone: 3456789012
Added phone located at 669
>>>>>>>>>>1601<<<<<<<<<<6
Total phones count:3
>>>>>>>>>>1614<<<<<<<<<<7
loop
Phone0
Phone located at 657
Phone: 1234567890
Phone1
Phone located at 663
Phone: ‚56789012
Phone2
Phone located at 669
Phone: 3456789012

正如你所看到的,虽然没有可观察到的填充失败,但它会循环输出。

我想知道将字符串添加到(动态)字符串数组的正确方法是什么。为什么第一个版本工作正常?是因为它如此小/简单吗?

PS1:第一个例子的简短版本(没有调试输出):

String* arr;

void setup() {
  Serial.begin(9600);

  String src = "abcdefghijk";

  arr = malloc(3 * sizeof(String));

  String dest1 = src;
  String dest2 = src;
  arr[0] = src.substring(0);
  arr[1] = dest1.substring(0);
  arr[2] = dest2.substring(0);

  dest1.setCharAt(1, 'z');
  dest2.setCharAt(1, 'x');
}

PS2:第二个示例的简短版本(带有min debug输出):

String* phones;
int phonesCount;

void setup() {
  Serial.begin(9600);

  String reply = "1234567890,2345678901,3456789012";

  fillPhones(reply);
}

void loop() {
  debugln(F("loop"));

  for(int i = 0; i < phonesCount; i++) {
    debug(F("Phone"));
    debugln(i);
    debug(F("Phone located at "));
    debugln((long)(phones + i));
    debug(F("Phone: "));
    debugln(*(phones + i));
  }
}

void fillPhones(String reply) {
  phonesCount = parsePhones(reply, phones);  
}

int parsePhones(String reply, String* &phonesArray) {
  phonesArray = NULL;
  int count = 0;

  int fromIndex = 0;
  int toIndex = 0;
  while(toIndex >= 0) {
    toIndex = reply.indexOf(F(","), fromIndex);

    String entryPhone = reply.substring(fromIndex, toIndex);

    String* tmpArray = realloc(phonesArray, (count + 1) * sizeof(String));
    if(tmpArray == NULL) {
      break;
    }
    phonesArray = tmpArray;

    phonesArray[count] = entryPhone;

    count++;
    fromIndex = toIndex + 1;
  }
  return count;
}

2 个答案:

答案 0 :(得分:0)

我相信你可以这样做。

String arr[3];
arr[0] = "one";
arr[1] = "two";
arr[2] = "three";

如果数组需要增加元素数量,请使用std :: vector。

#include <vector>  // include this header
// ...

std::vector<String> arr;  // declare the array
arr.push_back("one");  // add as many elements as you want
arr.push_back("two");
arr.push_back("three");
arr[0];  // you can access the elements just like if it was a regular array

编辑:

由于没有可用的载体。 我认为最好的选择是为String分配足够大的指针数组。然后保留索引到下一个要分配的String。

const unsigned MAX_PHONES = 1024;
String* arr = new String*[MAX_PHONES];
unsinged arrSize = 0;

// ...

// add new string
if(arrSize < MAX_PHONES)
    arr[arrSize++] = new String;
else
    assert(false && "MAX_PHONES exceeded");


// releasing memory
for(int i=0; i<arrSize; i++) delete arr[i];
delete[] arr;

答案 1 :(得分:0)

总结自己调查的结果和其他的评论 - 在Arduino程序中至少应该避免使用malloc() / realloc()(至少 - 在Arduino IDE中构建的程序),总是使用new运算符来分配动态数组(尽管您需要为realloc()函数定义一个自定义替代)。