Разбор и сравнение строк на Arduino: поведение отличается от gcc?

Я пытаюсь проанализировать простую строку, вывести результат анализа через serial.println и запустить функцию на основе полученной команды. Я написал приведенный ниже код, который прекрасно работает на c (см. приложенный файл c). Однако поведение одного и того же кода в IDE arduino совершенно другое, и я действительно не могу понять, почему.

#include <string.h>

void rcv_msg(char *rcv_msg) {

  char *all_tokens[2]; 
  int i = 0;

  all_tokens[i] = strtok(rcv_msg, "{,}");
  while (all_tokens[i] != NULL) {
    all_tokens[++i] = strtok(NULL, "{,}");
  }

  char *command = all_tokens[0]; 
  char *value = all_tokens[1];


  /*
  //Эти принтлы не работают, они игнорируются, почему?
  Serial.println("all_tokens[0]: ");
  Serial.println(all_tokens[0]);
  Serial.println("all_tokens[1]: ");
  Serial.println(all_tokens[1]);
  */

  if (strcmp(command,"message_1") == 0 && strcmp(value,"1") == 0) {

    Serial.println("message 1: ");
    Serial.println(command);
    Serial.println(value);
    Serial.println(" ");

  }

  if (strcmp(command,"message_2") == 0 && strcmp(value,"0") == 0) {

    Serial.println("message 2: ");
    Serial.println(command);
    Serial.println(value);
    Serial.println(" ");

  }
}



void setup() {

    Serial.begin(115200);
}

void loop() {

    char msg1[] = "{message_1,1}";
    char msg2[] = "{message_2,0}";

    Serial.println("Send msg1: ");
    rcv_msg(msg1); 
    delay(5000);

    Serial.println("Send msg2: ");
    rcv_msg(msg2); 
    delay(5000);    
}

Этот код выводит на последовательный монитор следующий неверный вывод:

Отправить сообщение 2: сообщение 2: сообщение_2 {сообщение_2,0}

Отправить сообщение 1: сообщение 2: сообщение_1 {сообщение_2,0}

Отправить сообщение 2: сообщение 2: сообщение_2 {сообщение_2,0}

Отправить сообщение 1: сообщение 2: сообщение_1 {сообщение_2,0}

Во-первых, я не понимаю, почему в начале сообщение 2 печатается перед сообщением 1, хотя сообщение 1 было отправлено первым. Во-вторых, мне непонятно, что сравнение, выполненное с помощью strcmp, всегда возвращает сообщение 2.

Что еще более важно, почему запуск того же кода в c дает правильный вывод? Интересно, есть ли какая-либо ошибка в моем коде Arduino. Кстати, я тестирую код на Teensy 3.2, но планирую использовать его и на Arduino Uno.

Код c (идеально работающий):

#include <string.h>
#include <stdio.h>


void rcv_msg(char *rcv_msg) {

    char *all_tokens[2]; //ПРИМЕЧАНИЕ: сообщение состоит из двух токенов: команда и значение
    int i = 0;


    all_tokens[i] = strtok(rcv_msg, "{, }");
    while (all_tokens[i] != NULL) {
        all_tokens[++i] = strtok(NULL, "{, }");
    }

    printf("all_tokens[0] : %s \n", all_tokens[0]);
    printf("all_tokens[1] : %s \n", all_tokens[1]);

    char *command = all_tokens[0];
    char *value = all_tokens[1];

    printf("command : %s \n", command);
    printf("value : %s \n", value);



    if (strcmp(command,"message_1") == 0 && strcmp(value,"1") == 0) {
        printf("activating command : %s %s \n\n\n", command, value);
    }

    if (strcmp(command,"message_2") == 0 && strcmp(value,"0") == 0) {
        printf("activating command : %s %s \n\n\n", command, value);
    }
}



int main() {

    char msg1[] = "{message_1, 1}";
    char msg2[] = "{message_2, 0}";

    rcv_msg(msg1);
    rcv_msg(msg2);

}

, 👍1


1 ответ


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

1

Обе версии вашего кода имеют неопределенное поведение. На данном входе ваш код вызовет strtok три раза и сохранит результат в массиве три раза: для первого токена, для второго token и, наконец, для нулевого указателя. Вы сохраняете все три (включая этот нулевой указатель!) в массиве all_tokens, который имеет размер 2.

Вы, очевидно, знаете о потенциальной опасности переполнения массива: вы даже написали этот комментарий в своем коде

char *all_tokens[2]; //ПРИМЕЧАНИЕ: сообщение состоит из двух токенов: команда и значение

и еще в следующем цикле

int i = 0;

all_tokens[i] = strtok(rcv_msg, "{, }");
while (all_tokens[i] != NULL) {
    all_tokens[++i] = strtok(NULL, "{, }");
}

вы по-прежнему настаиваете на хранении более двух значений в массиве all_tokens.


Кроме того, во второй версии вашего кода используются совершенно другие значения ключей сообщений (почему это вдруг "motor1_pattern1" вместо "message_1"?), что делает его непонятно, как вы сравнили функционал второй версии с функционалом первой.

,

большое спасибо. Я добавил while (i < 2 && all_tokens != NULL), что решило проблему. Это лучший способ сделать это? Также я отредактировал код, удалив motor1_pattern с message_1 (извините, я сделал неудачную копию-вставку). Я все еще удивляюсь: Почему серийный монитор по-прежнему отображает сначала второе сообщение, хотя первое сообщение было отправлено. Кажется, первое сообщение не доходит до серийного монитора... что я упускаю? Наконец, вы предлагаете использовать strtok_r вместо strtok?, @L_T

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