PROGMEMで配列の代わりにポインターを使用できないのはなぜですか?


11

現在、一部のライブラリを変更して、プロジェクトでSRAMが不足しないように、文字列の格納にRAMではなくフラッシュを使用するようにしています。

ライブラリの一部の文字列は、次のように宣言されています。

const char *testStringA = "ABC";

これは、私がこれを通常行う方法とは異なります。

const char testStringB[] = "DEF";

ただし、これら2つは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ポインターと配列の違いに対する回答で賢明に回答されています

基本的に、PROGMEMとして宣言するものは、

const prog_char testStringD[] PROGMEM = "JKL";

配列とそれが指すメモリの両方です。つまり、現在のスコープスタックにある配列の要素です。一方:

const prog_char* testStringC PROGMEM = "GHI";

メモリ内の他の場所にとどまることができるが、PROGMEM文字列として宣言されていない定数文字列へのPROGMEMポインタを宣言します。

私はそれをテストしませんでしたが、あなたは宣言しようとする必要があります:

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

指示された文字列を実際にPROGMEMスペース内に割り当てます。私は推測する、それがアルドゥイーノの使用して、作業をしなければならないF()マクロ実際には配列の宣言と同じ結果を持っている定型的なコードの多くを追加します。

コメントで述べたように、グローバルなコンテキストでない場合、PSTR()マクロの代わりにF()マクロを使用できます。

単純な方が良いです:ポインター宣言ではなく配列宣言を使用してください!

他の答えを参考にする__flash修飾子は3番目のソリューションです;-)


私は「シンプルな方が良い」に完全に同意します-アレイはずっと明確です。何かがすぐにわからないときはいつでも興味があります。
Cyber​​gibbons 2014年

F()は基本的に同じFlashStringHelperを返しますが、PSTR()を使用すると正常に機能します(関数内に定数を含める限り)。
Cyber​​gibbons 2014年

実際、実際には最初にPSTR()マクロを提案F()しましたが、送信前に変更しました。constはQでグローバルなものであるため、両方のコンテキストで機能するものを使用することをお勧めします。
zmo 2014年

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";

フラッシュに文字配列を作成し、期待どおりにアクセスできるようにします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.