Возврат конкатенации символов

Моя цель — создать функцию, которая объединяет 2 символа в 1 и возвращает его как подписку MQTT. Я получаю разницу между результатами внутри и снаружи функции (я новичок в Arduino), и я не могу понять, почему:

Код:

// Обновите переменную ниже:
const char *direction = "up";
const char* clientID = "Sonoff";

//Константы
const char *client_temp = "HomePi/Dvir/Windows/";
const char* outTopic = "HomePi/Dvir/Messages";
//const char* outTopic2 = "HomePi/Dvir/Windows/ESP32";
const char* inTopic2 = "HomePi/Dvir/Windows/All";
char *inTopic;

void setup() {
  Serial.begin(9600);
// len(create_topic(client_temp, clientID));
  inTopic = create_topic(client_temp, clientID);
  Serial.println(inTopic);
}

char *create_topic(char *chr1, char *chr2){ .   // добавлено *
  char topic[strlen(chr1) +strlen(chr2)+1];
  sprintf(topic,"%s%s",chr1,chr2);
  Serial.println(topic);
  return topic;
}

void loop() {
  // поместите сюда свой основной код для многократного выполнения:

}

Результат (обе строки должны были быть одинаковыми):

HomePi/Dvir/Windows/Sonoff
⸮⸮,茗⸮;⸮_⸮%⸮l?⸮az⸮z⸮%⸮⸮⸮\;⸮m⸮

, 👍1

Обсуждение

В языке C нельзя вернуть локальную строку (например, тему). Используйте либо глобальную переменную, либо параметр со ссылкой на локальную строку в контексте вызывающего объекта, либо используйте malloc()/strdup()., @Mikael Patel


1 ответ


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

4

В обоих случаях конкатенация работает нормально.

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

Следует помнить, что при работе с массивами вы никогда не передаете сам массив в функцию или из нее. Все, что вы передаете, — это указатель на адрес памяти, где находится массив. Если вы выделяете массив в функции, эта память выделяется в стеке. Когда вы возвращаете массив из функции, вы возвращаете адрес, который (в данный момент) находится в стеке. Когда вы покидаете функцию, вся память, выделенная в стеке этой функцией, удаляется, включая ваш массив.

Есть четыре способа решения проблемы — все они имеют свои подводные камни.

  1. Используйте глобальный массив "scratch pad", который достаточно большой для любой конкатенации, которую вы можете захотеть сделать. Сделайте вашу конкатенацию там.
  2. Объявите свой массив статическим, что означает, что он выделяется только один раз и может быть возвращен из функции, но, как и в случае с глобальным массивом, у него должен быть фиксированный размер.
  3. Передайте указатель на массив подходящего размера в вашу функцию, чтобы использовать этот массив для конкатенации, и
  4. Выделите память в куче с помощью malloc().

Мой обычный метод — номер 3. Он самый безопасный и гибкий:

const char *a = "part a";
const char *b = " part b";

char all[14];
doConcat(a, b, all);

void doConcat(const char *a, const char *b, char *out) {
    strcpy(out, a);
    strcat(out, b);
}
,

Можете ли вы более конкретно описать решение 3?, @Guy . D

И он использует char в качестве возвращаемого типа вместо char *., @KIIV

Хуже того, OP пытается вернуть char из char*. Ваши предложения по решению точны, хотя malloc следует избегать на крошечных Arduino., @mystery

@Guy.D , создайте массив подходящего размера перед вызовом функции и просто передайте указатель на него. Также посмотрите strcat http://www.cplusplus.com/reference/cstring/strcat/, @mystery

Я также попробовал с char * - исправляя свой код, @Guy . D

Даже возвращая char * с вашим текущим кодом, результаты все еще там только случайно и только временные. Вы обращаетесь к памяти, которая вам не принадлежит, и она обречена быть перезаписанной в любой момент, что испортит ваши результаты., @Majenko

Проведите эксперимент. Запустите create_topic дважды, а затем попробуйте распечатать оба результата. Посмотрите на искажение., @Majenko