Некоторые переменные не сохраняют свои значения при выходе из цикла while?

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

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

На SD-карте есть книги. Я сохраняю название каждой книги в массиве из цикла while. При выходе из цикла while массив по-прежнему остается пустым.

Как мне решить эту проблему? Спасибо

int amtBooks = 5; //это устанавливается ранее, но я не буду показывать весь код

// создаем массив размером с количество книг
  char* book[amtBooks] = { NULL };

// присваиваем имя каждой книги массиву
  int bookNum = 0;
  while (true) {

    File entry =  root.openNextFile();
      if (! entry) {
        break; // больше нет файлов
      }

    book[bookNum] = entry.name(); 

    Serial.println(book[bookNum]); // <--------ЗДЕСЬ ПЕЧАТАЕТСЯ ПРАВИЛЬНО

    bookNum ++;

  }

  root.close(); // закрыть поток


Serial.println(book[0]); <---ONCE OUT OF THE WHILE LOOP NOTHING DISPLAYS?
Serial.println(book[1]);

, 👍1

Обсуждение

примечание: в вашем коде непоследовательные отступы... первая строка и последние две строки должны иметь отступ в 2 пробела... они находятся на том же уровне, что и строка while (true) {... три строки блока if должны иметь отступ на 2 пробела меньше......... неправильный отступ вносит путаницу в отладку, @jsotola

это не про Arduino. это про C, @Juraj


3 ответа


-1

Полезно знать, где находятся переменные.

Локальные переменные располагаются в стеке. Они создаются при входе в функцию и исчезают при выходе из функции. Они могут даже находиться локально внутри if, for, while или другого оператора, если они объявлены внутри этого оператора.

Глобальные переменные имеют фиксированное расположение в памяти. Их можно использовать из любой функции.

Когда указатель создан и он никуда не указывает, вы не можете записывать данные туда, куда указывает этот указатель.
Вы создаете массив указателей, но вам нужна память, которую вы можете использовать для хранения данных. Например: char booknames[5][20]. Массив текста часто копируется с помощью strcpy().

,

Весь этот фрагмент кода находится внутри основного цикла do. Происходит что-то странное, потому что даже если я объявляю массив глобально в самом верху, установка значений массива из цикла while не сохраняется. Если я уберу цикл while, я смогу установить значения массива. Внутри цикла while значения массива присутствуют, но теряются после выхода из цикла while., @Kara

Вот что я пытался объяснить. Помогает, если вы знаете, где находятся переменные. Фактический текст в вашем коде нигде не хранится, так как вы используете указатели. Вам нужно создать ячейку памяти для хранения этих текстов. Не указатель на что-то или ничто, а вам нужна ячейка памяти для символов текста., @Jot


0

Вы сохраняете указатель на массив символов name в переменной entry File. entry выходит из области видимости (удаляется из стека), и ваш сохраненный указатель никуда не указывает.

Вам понадобится char book[amtBooks][13] = {{0}}; и strcpy(book[bookNum], entry.name());

,

1

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

Это правильно.

в данном случае этого, похоже, не происходит.

Это происходит. Причина, по которой кажется, что этого не происходит, заключается в том, что book[bookNum] — это указатель: адрес некоторых данных в памяти. Вы можно проверить, что указатель сохраняет свое значение, выведя его на печать:

Serial.println((uintptr_t) book[bookNum]);

Однако, утверждение, которое вы использовали:

Serial.println(book[bookNum]);

имеет другое значение. Он не выводит значение переменной (т.е. указатель): он печатает символы, которые хранятся в указанный адрес, вплоть до первого NUL и исключая его.

Ваша проблема возникает из-за того, что когда вы заявляете

File entry =  root.openNextFile();

У вас есть переменная, которая является локальной для цикла. Затем, при вызове метод

entry.name()

Вы получаете указатель на массив символов, принадлежащий записи объект. Этот указатель действителен только до тех пор, пока объект находится в области видимости. Как как только он выходит из области видимости (в конце каждой итерации цикла), Объект entry уничтожается, и вся принадлежащая ему память освобождается. На этом этапе сохраненный вами указатель является «висячим» указателем: он указывает в область памяти, которая уже была освобождена.

Правильным решением будет выделить себе память, необходимую для хранения массивы символов, например:

for (int bookNum = 0; bookNum < amtBooks; bookNum++) {
    File entry = root.openNextFile();
    if (!entry) break;
    const char *name = entry.name();
    book[bookNum] = (char *) malloc(strlen(name) + 1);
    strcpy(book[bookNum], name);
    Serial.println(book[bookNum]);
}

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

,