Warum kann ich mit PROGMEM keine Zeiger anstelle eines Arrays verwenden?

11

Ich ändere derzeit einige Bibliotheken, um Flash anstelle von RAM für die Speicherung von Zeichenfolgen zu verwenden, damit mir in einem Projekt nicht der SRAM ausgeht.

Einige Zeichenfolgen in der Bibliothek werden folgendermaßen deklariert:

const char *testStringA = "ABC";

Dies unterscheidet sich davon, wie ich dies normalerweise sehe:

const char testStringB[] = "DEF";

Ich denke jedoch, dass diese beiden gleichwertig sind, wenn sie als const deklariert und in der Deklaration initialisiert werden. Beide funktionieren gut im Code.

Ich habe versucht, diese auf Flash zu verschieben:

const prog_char *testStringC PROGMEM = "GHI";

Ich stellte dann fest, dass dies nicht funktionierte. Beim Drucken wurde ein Gobbledegook produziert.

Nach dem üblicheren Muster von:

const prog_char testStringD[] PROGMEM = "JKL";

funktioniert gut.

Ich kann in der Demontage sehen:

00000068 <testStringC>:
  68:   04 01                                               ..

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

Es ist also klar, dass der Zeiger und PROGMEM dazu führen, dass der String / das Array nicht initialisiert wird.

Warum ist das?

Beispielcode:

#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
quelle

Antworten:

6

Nun, diese Frage wurde in einer Antwort auf die Frage C zum Stapelüberlauf mit Bedacht beantwortet : Unterschiede zwischen Zeichenzeiger und Array .

Grundsätzlich, was Sie als PROGMEM deklarieren,

const prog_char testStringD[] PROGMEM = "JKL";

ist sowohl das Array als auch der Speicher, auf den es zeigt, dh die Elemente des Arrays, beide im Gültigkeitsbereich des aktuellen Bereichs. Während mit:

const prog_char* testStringC PROGMEM = "GHI";

Sie deklarieren einen PROGMEM-Zeiger auf eine konstante Zeichenfolge, die möglicherweise an anderer Stelle im Speicher verbleibt, jedoch nicht als PROGMEM-Zeichenfolge deklariert ist.

Ich habe das zwar nicht getestet, aber Sie sollten versuchen zu erklären:

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

um die spitze Zeichenfolge tatsächlich im PROGMEM-Bereich zuzuweisen. Ich denke, es sollte funktionieren, wenn man das Arduino- F()Makro verwendet , das viel Boilerplate-Code hinzufügt, um tatsächlich das gleiche Ergebnis wie die Array-Deklaration zu erzielen.

Wie in den Kommentaren erwähnt, könnte das PSTR()Makro anstelle des F()Makros verwendet werden , wenn es sich nicht in einem globalen Kontext befindet .

Einfacher ist besser: Verwenden Sie die Array-Deklaration, nicht die Zeigerdeklaration!

Bei dieser anderen Antwort ist das __flashQualifikationsmerkmal eine dritte Lösung ;-)

zmo
quelle
Ich stimme voll und ganz zu, dass "einfacher ist besser" - das Array ist viel klarer. Ich interessiere mich einfach immer, wenn etwas nicht sofort offensichtlich ist.
Cybergibbons
F () gibt FlashStringHelper zurück, was im Wesentlichen dasselbe ist, aber die Verwendung von PSTR () funktioniert einwandfrei (solange Sie die Konstanten in eine Funktion bringen).
Cybergibbons
Tatsächlich habe ich zuerst das PSTR()Makro vorgeschlagen, aber F()vor dem Senden geändert , da Ihre Konstanten in Ihrem Q globale Konstanten sind. Daher habe ich es vorgezogen, bei der zu bleiben, die in beiden Kontexten funktionieren sollte.
Zmo
3

Was diese Zeile:

const prog_char *testStringC PROGMEM = "GHI";

Dies bedeutet, Prologcode zu schreiben, um die Zeichen in der Zeichenfolge in den SRAM zu kopieren, und dann den in Flash gespeicherten Zeiger auf diesen SRAM-Speicherort zu initialisieren . Sie müssen den Zeiger auf normalem Wege laden und dann den Zeiger wie gewohnt dereferenzieren.

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

Diese Linie:

const prog_char testStringD[] PROGMEM = "JKL";

Erstellt das Array von Zeichen in Flash, sodass Sie wie erwartet darauf zugreifen können.

Ignacio Vazquez-Abrams
quelle