Почему я не могу использовать указатели вместо массива с 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);

}
Cybergibbons
источник

Ответы:

6

Что ж, на этот вопрос мудро ответили в ответе на вопрос C переполнения стека : различия между указателем на символ и массивом .

По сути, то, что вы объявляете как PROGMEM,

const prog_char testStringD[] PROGMEM = "JKL";

это и массив, и память, на которую он указывает, то есть элементы массива, как в текущем стеке области видимости. Принимая во внимание, что:

const prog_char* testStringC PROGMEM = "GHI";

Вы объявляете указатель PROGMEM на постоянную строку, которая может остаться в другом месте в памяти, но не объявлена ​​как строка PROGMEM.

Хотя я не проверял это, но вы должны попытаться объявить:

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

фактически выделить указанную строку в пространстве PROGMEM. Я предполагаю, что это должно работать, используя F()макрос Arduino , который добавляет много стандартного кода, чтобы фактически иметь тот же результат, что и объявление массива.

Как сказано в комментариях, если бы не в глобальном контексте, PSTR()макрос мог бы использоваться вместо F()макроса.

Чем проще, тем лучше: используйте декларацию массива, а не указатель!

Если этот другой ответ , __flashклассификатор является третьим решением ;-)

ZMO
источник
Я полностью согласен с тем, что «чем проще, тем лучше» - массив намного понятнее. Я просто всегда интересуюсь, когда что-то не сразу видно.
Cybergibbons
F () возвращает FlashStringHelper, который по сути тот же, но использование PSTR () работает нормально (до тех пор, пока вы вводите константы внутри функции).
Cybergibbons
на самом деле, я фактически предложил сначала PSTR()макрос, но F()перед отправкой изменил его , потому что ваши константы являются глобальными в вашем Q, поэтому я предпочел придерживаться того, который должен работать в обоих контекстах.
Zmo
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";

создает массив символов во флэш-памяти, позволяя вам получить к нему доступ, как и ожидалось.

Игнасио Васкес-Абрамс
источник