Чому я не можу використовувати вказівники замість масиву з PROGMEM?


11

Зараз я змінюю деякі бібліотеки, щоб використовувати флеш замість оперативної пам’яті для зберігання рядків, щоб у мене не було проекту SRAM.

Деякі рядки в бібліотеці оголошені таким чином:

const char *testStringA = "ABC";

Це відрізняється від того, як я зазвичай вважаю це зробленим:

const char testStringB[] = "DEF";

Однак я думаю, що ці два рівні, коли вони оголошені const та ініціалізовані в декларації. Обидва добре працюють у коді.

Я намагався перемістити їх у спалах:

const prog_char *testStringC PROGMEM = "GHI";

Потім я виявив, що це не працює. Під час друку він виготовляв гусячий кулик.

Однак, дотримуючись більш звичного шаблону:

const prog_char testStringD[] PROGMEM = "JKL";

працює чудово.

Я можу побачити в розборці:

00000068 <testStringC>:
  68:   04 01                                               ..

0000006a <_ZL11testStringD>:
  6a:   4a 4b 4c 00                                         JKL.

Тож зрозуміло, що вказівник і результати PROGMEM в рядку / масиві не ініціалізуються.

Чому це?

Приклад коду:

#include <avr/pgmspace.h>

const int BUFFER_LEN = 20;

const char *testStringA = "ABC";
const char testStringB[] = "DEF";
const prog_char *testStringC PROGMEM = "GHI";
const prog_char testStringD[] PROGMEM = "JKL";

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    char buffer[BUFFER_LEN];

    Serial.println(testStringA);
    Serial.println(testStringB);

    strncpy_P(buffer, testStringC, BUFFER_LEN);
    Serial.println(buffer);

    strncpy_P(buffer, testStringD, BUFFER_LEN);
    Serial.println(buffer);

    delay(1000);

}

Відповіді:


6

Що ж, на це питання мудро відповіли у відповіді на запитання C Стек переповнення : відмінності між покажчиком та масивом char .

По суті, те, що ти оголошуєш ПРОГМЕМУ,

const prog_char testStringD[] PROGMEM = "JKL";

- це і масив, і пам'ять, на яку він вказує, тобто елементи масиву, що знаходяться в поточному стеку області. Тоді як:

const prog_char* testStringC PROGMEM = "GHI";

ви оголошуєте вказівник PROGMEM на постійний рядок, який може залишатися в іншому місці пам'яті, але не оголошується як рядок PROGMEM.

Хоча я цього і не перевіряв, але слід спробувати заявити:

const prog_char* testStringC PROGMEM = F("GHI");

фактично виділити загострену рядок у просторі ПРОГМЕМ. Я думаю, що це має працювати, використовуючи F()макрос Arduino , який додає багато кодового коду, щоб насправді мати такий же результат, як і декларація масиву.

Як сказано в коментарях, якщо не в глобальному контексті, PSTR()макрос можна використовувати замість F()макросу.

Простіше, краще: використовуйте декларацію масиву, а не вказівну!

Cf що інша відповідь , __flashкласифікатор - це третє рішення ;-)


Я повністю погоджуюся з тим, що "простіше краще" - масив набагато зрозуміліший. Мені завжди цікаво, коли щось не відразу стає очевидним.
Кібергіббони

F () повертає FlashStringHelper, який по суті є тим самим, але використання PSTR () працює чудово (до тих пір, поки ви не будете вносити коефіцієнти у функцію).
Кібергібони

Дійсно, я фактично запропонував спочатку PSTR()макрос, але змінив його до того, F()як надсилати, оскільки ваші конкурси є глобальними у вашому Q, тому я вважав за краще дотримуватися того, який повинен працювати в обох контекстах.
змо

3

Що це за рядок:

const prog_char *testStringC PROGMEM = "GHI";

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

const char *str = pgm_read_word(&testStringC);
Serial.println(str);

Цей рядок:

const prog_char testStringD[] PROGMEM = "JKL";

створює масив символів у спалах, дозволяючи вам отримати доступ до нього, як очікувалося.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.