Получение искаженного массива символов, возвращаемого функцией

c++

Я возвращаю массив char[300] из функции. Если я инициализирую с ним переменную char*, результат будет искажен, но не в том случае, если я добавлю его к String. Что дает?

  const char* post2 =  uploadHourCsv(timeNow,pulseChangeHour) ;
  String postS2 = "";
  postS2 =  uploadHourCsv(timeNow,pulseChangeHour) ;
  //const char* post2 = { uploadHourCsv(timeNow,pulseChangeHour) };
  if( debug ) {
    Serial.print("received in loop() as : [");
    Serial.println(post2);
    Serial.print("String : [");
    Serial.println(postS2);
  }

...

char postStr[300] = "";
// populate array
if( debug ) {
  Serial.print("postStrCsv generated: ");
  Serial.println(postStr);
}
return postStr;

postStrCsv создано: 00000003;3| 0,00;8|17,55;9|17,55;10|18,12;11|16,92;20|93817;22|93789;101|6;время|1559646000;

получено в цикле() как: [000?⸮@⸮⸮?" ⸮⸮?17,55;9|17,58! @0⸮⸮⸮⸮G% @5~`⸮h⸮?⸮⸮

Строка: [00000003;3| 0,00;8|17,55;9|17,55;10|18,12;11|16,92;20|93817;22|93789;101|6;время|1559646000;

, 👍3

Обсуждение

Что именно возвращает uploadHourCsv? Поскольку post2 объявлен как указатель без указания размера, для этого функция должна вернуть указатель на уже выделенную строку., @chrisl

char postStr[300] с содержимым, отображаемым в кавычках., @tony gil

Как это специфично для Arduino? Несмотря на два отличных (и получивших одобрение) ответа ниже, я говорю, что такие вопросы 1) относятся к https://stackoverflow.com/ и 2) быстрее получат лучшие ответы (не в обиду тем, кто здесь), @Mawg says reinstate Monica

спасибо как вы думаете, что мы должны перенести это на SO? отметьте для внимания модератора или отметьте, чтобы закрыть фразу «принадлежит так», @tony gil


2 ответа


Лучший ответ:

12

Это потому, что вы возвращаете указатель на локальную переменную.

Вы возвращаете только адрес в памяти, где размещен ваш массив символов. Это выделение находится в стеке и существует только на время жизни функции. Но у вас все еще есть адрес, по которому была выделена эта память, и вы используете ее так, как если бы она все еще была выделена.

Когда вы добавляете его к объекту String, в куче выделяется место, и данные копируются в него. Это выделение кучи сохраняется до тех пор, пока объект String не будет уничтожен либо путем выхода за пределы области действия, либо путем его уничтожения вручную.

Это чистая случайность, что данные все еще доступны и не повреждены на момент копирования.

Короче:

  • Вы должны никогда возвращать указатель на локально выделенный (нестатический) массив.

Вещи, которые вы можете сделать правильно:

  • Передайте указатель массива на функцию, которая будет заполнена функцией
  • Возвращает указатель на статически размещенный локальный массив
  • Возвращает объект String (хотя я бы не рекомендовал использовать String ни для чего)

Я предпочитаю первый метод, при котором вы определяете массив во внешней области видимости, а затем передаете его (и, возможно, длину массива) в функцию. Затем функция заполняет переданный массив. Это также имеет то преимущество, что функция может использовать возвращаемое значение для указания статуса или другого количества.

Например:

void myFunc(char *buf, int len) {
    for (int i = 0; i < len - 1; i++) {
        buf[i] = 'A';
    }
    buf[len - 1] = 0;
}

char myBuf[20];
myFunc(myBuf, 20);
Serial.println(myBuf);

--> AAAAAAAAAAAAAAAAAAAA
,

У да человек! ПРОГОЛОСОВАНО и ПРИНЯТО. Повторное использование String: полностью согласен, небезопасное выделение памяти в классе String., @tony gil


1

Следуя бесценному совету Маженко и включив другой аналогичный подход, окончательная рабочая реализация заключалась в том, чтобы рассматривать функцию как метод работы в массиве char[300] как "выходной параметр".

char postStr[300] =  "";
uploadHourCsv(timeNow,pulseChangeHour,postStr) ;
...

void uploadHourCsv(int unixtimeEvent, int pulseChange, char* postStr) {
  // подготавливаем данные и переменные
  strcat(postStr, stationId);
  strcat(postStr,";3|"); // чува гора
  strcat(postStr, dtostrf(rainHour,6,2,charDummy));
  ...
}
,

Если вы сделаете первый strcat вместо этого strcpy, вы можете избежать первой инициализации массива. (И обратите внимание, что то, как вы инициализируете его в вызывающем объекте, заполняет всю длину нулевыми байтами, а не только первый элемент, так что это занимает намного больше времени, чем необходимо, если только он не будет оптимизирован.), @Peter Cordes