【问题标题】:Arduino - what's a correct way to handle an array of structs?Arduino - 处理结构数组的正确方法是什么?
【发布时间】:2018-03-10 00:54:17
【问题描述】:

我在尝试用字符串结构填充数组时遇到问题。可能,我在使用 Arduino 上的指针或内存分配规则时缺少一些基本的东西。

在下面查看我的代码。

要填充的数据结构:

struct SMSData {
   String id;   
   String status;
   String from;
   String date;
   String text;
};

字符串解析例程:

SMSData* readSMS(String reply) {
  debugSerial.println(reply);

  // declare a pointer to result array
  SMSData* smsArray = NULL;

  const String startPattern = F("+CMGL: ");

  int index = -1;

  // calculate result array length
  byte count = 0;
  do {
    index = reply.indexOf(startPattern, (index + 1));

    if(index < 0) {
      break;
    }

    count++;
  } while(true);

  if(count == 0) {
    return NULL;
  }

  debugSerial.println(count);

  // allocate memory to store result array
  smsArray = malloc(count * sizeof(SMSData*));

  if(smsArray == NULL) {
    return NULL;
  }

  // start parsing input String
  index = reply.indexOf(startPattern);

  int fromIndex = 0;
  while(true) {
    debugSerial.println();

    if(index < 0) {
      break;
    }

    // init data for the next element of result array
    SMSData smsData = {"", "", "", "", ""};

    // start filling result array element
    // get id
    fromIndex = index + startPattern.length();
    index = reply.indexOf(F(","), fromIndex);
    smsData.id = reply.substring(fromIndex, index);

    debugSerial.println(smsData.id);

    // get status
    fromIndex = reply.indexOf(F("\""), index) + 1;
    index = reply.indexOf(F("\""), fromIndex);
    smsData.status = reply.substring(fromIndex, index);

    debugSerial.println(smsData.status);

    // get phone
    fromIndex = reply.indexOf(F("\""), index + 1) + 1;
    index = reply.indexOf(F("\""), fromIndex);
    smsData.from = reply.substring(fromIndex, index);    

    debugSerial.println(smsData.from);

    // get date
    fromIndex = reply.indexOf(F("\""), index + 1) + 1;
    index = reply.indexOf(F("\""), fromIndex);
    smsData.date = reply.substring(fromIndex, index);

    debugSerial.println(smsData.date);

    // get text
    fromIndex = index + 1;
    index = reply.indexOf(startPattern, fromIndex);

    if(index < 0) {
      smsData.text = reply.substring(fromIndex);
    } else {
      smsData.text = reply.substring(fromIndex, index);
    }

    smsData.text.trim();
    debugSerial.println(smsData.text);

    // add filled element to result array
    smsArray[count - 1] = smsData;
  }

  return smsArray;
}

输出解析数据:

SMSData* smsArray = readSMS(reply);

int count = sizeof(smsArray);

debugSerial.print(F("SMS count:"));
debugSerial.println(count);

for(int i = 0; i < count; i++) {
  SMSData smsData = smsArray[i];

  debugSerial.print(F("id: "));
  debugSerial.println(smsData.id);
  debugSerial.print(F("status: "));
  debugSerial.println(smsData.status);
  debugSerial.print(F("from: "));
  debugSerial.println(smsData.from);
  debugSerial.print(F("date: "));
  debugSerial.println(smsData.date);
  debugSerial.print(F("text: "));
  debugSerial.println(smsData.text);
}

free(smsArray);

要解析的虚拟字符串:

String reply = "+CMGL: 1,\"REC READ\",\"+123456789012\",,\"2017/09/26,18:31:25+03\"\r\nHi\r\n+CMGL: 2,\"REC READ\",\"+123456789012\",,\"2017/09/26,18:34:25+03\"\r\nHello\r\n";

当我运行草图时,它的输出通常不同,但总是损坏和不完整,例如

+CMGL: 1,"REC READ","+123456789012",,"2017/09/26,18:31:25+03"
Hi
+CMGL: 2,"REC READ","+123456789012",,"2017/09/26,18:34:25+03"
Hello

2

1
REC READ
+12345678905+03 017/09/26,18:31:25+03
Hi

2
REC REA

根据输出,您可以看到,它会注销整个输入字符串,开始解析它,经历第一次循环迭代(混合来自结构字段的字符串),再次开始第二次迭代,用大量混合的字符串填充结构然后中途停止响应。

目前我认为除了内存分配问题之外没有其他原因导致这种行为,但我无法找出我做错了什么。

感谢您的帮助。

【问题讨论】:

  • StringF(...) 的定义是什么?
  • @Yunnosch String 是标准的 Arduino 数据类型(描述 - arduino.cc/en/Reference/StringObject,源 - github.com/arduino/Arduino/blob/master/hardware/arduino/avr/…),F 是用于将字符串值存储在闪存中的宏(请参见相同的头文件进行定义) .
  • reply 是一个字符串变量,在我的帖子中看到它的声明和初始化。
  • 除了查看 smsArray 的大小(它始终是 4,作为指针)之外,您还需要找到条目的数量。指向的分配内存的大小在解析器函数之外是未知的。
  • smsArray = malloc(count * sizeof(SMSData*)); 正在为count 指向SMSData 的指针分配空间,这不是您想要的实际数组。应该是smsArray = malloc(count * sizeof *smsArray);

标签: c++ string struct arduino malloc


【解决方案1】:

首先,你的代码是c++,不是严格意义上的c,没关系,但是标签应该改一下。以下是我在您的代码中发现的一些问题...

    ///// Passing nothing, println requires one or two parameters
    debugSerial.println();

documentation on println

    // init data for the next element of result array
    ////// You are creating a local buffer, it will go out of scope
    ////// when you leave the function.  And you are trying to store
    ////// it in an array that you return from your function.
    ////// And you are changing the data with pass through your loop
    ////// (2 passes).
    SMSData smsData = {"", "", "", "", ""};

    //...
    ////// You should be assigning an allocated struct pointer
    ////// count is not changing, you are only assigning the last
    ////// element of the array.
    smsArray[count - 1] = smsData;


///// This will not work.  smsArray is a pointer and you have lost
///// the information about how much memory was allocated and assigned to
///// the pointer.
SMSData* smsArray = readSMS(reply);
int count = sizeof(smsArray);

我们使用 C 风格的指针作为数组使用的一个技巧是分配一个比我们需要的大一倍的指针块,并确保它们都设置为 NULL(使用 calloc() 而不是 malloc())。然后,我们将在数组中设置每个指针,除了最后一个。最后,我们将遍历数组,直到指针为 NULL,表示数据结束。

您应该在 smsArray 中存储指向已分配数据的指针,并且应该释放(销毁)该数据以及数组。

所以,您的代码可能看起来像...

SMSData** smsArray = NULL;
smsArray = (SMSData **)calloc(count+1, sizeof(SMSData *));
int idx = 0;
//...

    SMSData* smsData = new smsData();
    smsData->id = ""; //etc. for the rest of the fields
    //...

    smsArray[idx++] = smsData;
    //..

返回后……

SMSData ** smsArray = readSMS(reply);
SMSData ** ptr = smsArray;
while (ptr != NULL) {
    //do stuff with ptr->whatever
    destroy(ptr); //destroy since allocated with new
    ptr++;
}
free(smsArray); //free since allocated with malloc

这不是最好的代码(并且可能有错误,我现在无法访问我的编译器)。但它试图坚持你的方法。

【讨论】:

  • 我明白了 - 应该 1) 使用指针数组和 2) 在每个循环中分配一个新结构。谈到Serial.println - 我现在找不到它的源代码,但是......即使没有传递任何参数,它也能工作。
  • println 可能没问题,但我在答案中包含了文档链接,它显示了一个或两个参数。编译可能是用 NULL 终止参数,所以它“恰好”起作用
  • Arduino 文档非常基础,您可能会同意。我刚刚在github.com/arduino/Arduino/blob/master/hardware/arduino/avr/… - size_t println(void) 找到了声明。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-04
  • 1970-01-01
  • 1970-01-01
  • 2015-02-11
  • 2014-09-23
  • 2010-10-08
  • 2013-07-24
相关资源
最近更新 更多