esp32 аварийно завершает работу при попытке разыменования значения указателя

У меня есть прослушиватель websocket, который дает мне std::строку полезной нагрузки моего сообщения (мне нужны байты), и я написал функцию, которая извлекает каждый из байтов и помещает их в 32-битные целочисленные переменные, добавляет это целое число к массиву 32-битных целых чисел и передает обратно адрес указателя:

uint32_t *get32BitInt(WebsocketsMessage message)
{
  std::string raw = message.rawData();
  uint32_t chunks[message.length() / 4];
  int chunkIndex = 0;
  for (int i = 0; i < message.length(); i += 4)
  {
    uint32_t chunk = 0;
    for (int j = 0; j < 4; j++)
    {
      chunk <<= 8;
      chunk |= raw.at(i + j);
    }
    chunks[chunkIndex] = chunk;
    chunkIndex++;
  }
  return chunks;
}

И я знаю, что синтаксический анализ и построение массива работают, потому что если я добавляю цикл перед выходом из общего вызова функции, то он выводит их просто отлично.

parser builds array correctly

И в функции, которая вызывает парсер, я могу распечатать адрес указателя, который был передан обратно.

Но как только я пытаюсь разыменовать что-либо, мой микроконтроллер выходит из строя:

crash

Я знаю, что я довольно зеленый для указателей, и я, вероятно, что-то неправильно понимаю, но я продолжаю смотреть на документы и примеры передачи массивов в качестве указателей, и это кажется правильным.

Кроме того, стоит отметить, что это не кажется проблемой с фактической печатью номера, потому что если я сохраню первый номер массива в переменную, а затем попытаюсь напечатать строку после назначения, контроллер аварийно завершит работу после назначения, но до следующей последовательной печати:

crash at assignment

, 👍0

Обсуждение

uint32_t chunks[сообщение.length() / 4]; - Я не думаю, что вы можете это сделать. Размеры массивов должны быть константами времени компиляции. Или же вам нужно использовать динамическое распределение памяти. что может быть плохо (хотя на ESP32 не так плохо, как на таких платах, как Uno)., @chrisl

@chrisl, gcc допускает это, @Juraj

Просто предположение: Но разве вы не используете локальный массив? Какой ich создается в стеке (фрейме) функции? А затем вы используете указатель на этот массив, когда функция завершается и стек уменьшается, то есть его стек освобождается?, @Peter Paul Kiefer

Ах извините, я сначала написал комментарий, а потом увидел ответ. ;-). Вечеринка провалилась!, @Peter Paul Kiefer

@PeterPaulKiefer не волнуйтесь, я ценю дополнительный глаз и комментарий :), @Chris Schmitz


1 ответ


1

Ах да, один из моих друзей указал на эту проблему. Это небольшая загвоздка:

chunks выделяется на стеке в get32BitInt, возвращая указатель на chunks, вы указываете на память, которая освобождается. Думайте об этом как о указании на переменную члена класса который уже был уничтожен

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

самый простой способ проверить это-выделить фрагменты вне этой функции и передать их в качестве ссылки

Так что да, я выделял память в пределах области функции, а затем сдувал это содержимое памяти, когда эта область функций заканчивалась.

Я рефакторизовал свой код, чтобы переместить массив за пределы области действия функции синтаксического анализа, и передал указатель в функцию синтаксического анализа для заполнения:

void get32BitInt(WebsocketsMessage message, uint32_t *chunks)
{
  Serial.println("--------------------------------");
  Serial.println("raw parse:");

  std::string raw = message.rawData();

  // uint32_t chunks[message.length() / 4];
  int chunkIndex = 0;

  for (int i = 0; i < message.length(); i += 4)
  {
    uint32_t chunk = 0;
    for (int j = 0; j < 4; j++)
    {
      chunk <<= 8;
      chunk |= raw.at(i + j);
    }
    chunks[chunkIndex] = chunk;
    chunkIndex++;
  }

  Serial.println("parsed chunks:");
  for (int i = 0; i < 11; i++)
  {
    Serial.print(chunks[i], HEX);
    Serial.print(" == ");
    Serial.println(chunks[i], BIN);
  }

  Serial.println("--------------------------------");
}

void addWebsocketListener()
{

  client.onMessage([&](WebsocketsMessage message) {

    uint32_t gameFrame[message.length() / 4]; // *попробуйте переместить gameFrame за пределы области видимости
    get32BitInt(message, gameFrame);

    Serial.print("адрес игрового кадра: ");
    Serial.println((int)&gameFrame);

    Serial.println("сохранение первого 32-битного целого числа в переменной");
    uint32_t firstNumber = *gameFrame;

    Serial.print("первое значение игрового кадра: ");
    Serial.println(firstNumber, HEX);

    Serial.println("printing frame:");
    for (int i = 0; i < 11; i++)
    {
      Serial.println(gameFrame[i], HEX);
    }
  });
}

И теперь мой контроллер не выходит из строя :D

И теперь я немного лучше понимаю область действия, когда имею дело с указателями :)

,

вы также можете просто сделать его статичным или глобальным, чтобы избежать необходимости передавать его по кругу., @dandavis

определенно. В данный момент я немного разбираюсь в процедурах, потому что не знаю на 100%, какова будет окончательная форма всего этого, но мой план состоит в том, чтобы реорганизовать это в класс, как только я это сделаю, а когда я это сделаю, мой план состоит в том, чтобы сделать это частной собственностью. Но да, сейчас я просто перенесу это на глобальный уровень, чтобы упростить код, это хорошая идея. Спасибо за предложение :), @Chris Schmitz