このエラーはどういう意味ですか?どうにも解決できません。
警告:文字列定数から 'char *'への非推奨の変換[-Wwrite-strings]
このエラーはどういう意味ですか?どうにも解決できません。
警告:文字列定数から 'char *'への非推奨の変換[-Wwrite-strings]
回答:
私の不思議なように、このエラーの理由と理由について、少しの背景技術情報を提供します。
C文字列を初期化する4つの異なる方法を調べ、それらの違いを確認します。これらは問題の4つの方法です。
char *text = "This is some text";
char text[] = "This is some text";
const char *text = "This is some text";
const char text[] = "This is some text";
このために、3番目の文字「i」を「o」に変更して、「Thos is some text」にします。それは、すべての場合(あなたが思うだろうが)、次のようにして達成できます。
text[2] = 'o';
それでは、文字列を宣言する各方法が何をするのか、そしてそのtext[2] = 'o';
ステートメントが物事にどのように影響するのかを見てみましょう。
最初に最もよく見られる方法:char *text = "This is some text";
。これは文字通りどういう意味ですか?さて、Cでは、文字通り「text
読み取り専用(コード)スペースに保持されているこの文字列リテラルへの読み取り/書き込みポインターである変数を作成する」という意味です。オプションを-Wwrite-strings
オンにしている場合は、上記の質問に示されているように警告が表示されます。
基本的には、「警告:書き込みできない領域への読み取り/書き込みポイントである変数を作成しようとしました」という意味です。3番目の文字を「o」に設定してみた場合、実際には読み取り専用の領域に書き込もうとしているので、うまくいきません。Linuxを搭載した従来のPCの場合:
セグメンテーション障害
次に、2つ目:char text[] = "This is some text";
。文字通り、Cでは、「型「char」の配列を作成し、「This is some text \ 0」データで初期化します。配列のサイズは、データを格納するのに十分な大きさです」。そのため、実際にRAMが割り当てられ、実行時に値 "This is some text \ 0"がRAMにコピーされます。警告なし、エラーなし、完全に有効。そして、あなたがデータを編集できるようにしたい場合、それを行う正しい方法。コマンドを実行してみましょうtext[2] = 'o'
:
Thosはテキストです
完璧に機能しました。良い。
次に、3番目の方法:const char *text = "This is some text";
。再び文字通りの意味:「読み取り専用メモリ内のこのデータへの読み取り専用ポインタである「テキスト」と呼ばれる変数を作成します。」ポインターとデータの両方が読み取り専用になっていることに注意してください。エラーも警告もありません。テストコマンドを実行しようとするとどうなりますか?できません。コンパイラはインテリジェントになり、悪いことをしようとしていることがわかります。
エラー:読み取り専用の場所 '*(text + 2u)'の割り当て
コンパイルすらしません。読み取り専用メモリへのポインタの書き込みをコンパイラに伝えているため、読み取り専用メモリへの書き込みは保護されます。もちろん、読み取り専用メモリを指す必要はありませんが、読み取り/書き込みメモリ(RAM)を指す場合、そのメモリはコンパイラによる書き込みから保護されます。
最後の最後のフォーム:const char text[] = "This is some text";
。繰り返しますが、以前と同様に[]
、RAMに配列を割り当て、そこにデータをコピーします。ただし、これは読み取り専用配列です。それへのポインタはとしてタグ付けされているので、あなたはそれに書き込むことができませんconst
。書き込みを試みると、次の結果になります。
エラー:読み取り専用の場所 '*(text + 2u)'の割り当て
だから、私たちがどこにいるかの簡単な要約:
このフォームは完全に無効であり、いかなる場合でも回避する必要があります。それはあらゆる種類の悪い出来事への扉を開きます:
char *text = "This is some text";
データを編集可能にする場合、このフォームは適切なフォームです。
char text[] = "This is some text";
編集されない文字列が必要な場合、このフォームは正しいフォームです。
const char *text = "This is some text";
この形式はRAMを無駄にしているように見えますが、その用途はあります。今のところそれを忘れてください。
const char text[] = "This is some text";
PROGMEM
、PSTR()
やなどのマクロで宣言しない限り、RAMに存在することに注意してくださいF()
。したがって、をconst char text[]
超えるRAMは使用しませんconst char *text
。
(const char *)(...)
キャストとして定義する傾向があります。ボードがそれを必要としない場合、実際の効果はありませんが、コードを必要とするボードに移植すると大幅に節約されます。
Makenkoの優れた答えを詳しく説明するために、コンパイラがこれについて警告する正当な理由があります。テストスケッチを作成しましょう。
char *foo = "This is some text";
char *bar = "This is some text";
void setup ()
{
Serial.begin (115200);
Serial.println ();
foo [2] = 'o'; // change foo only
Serial.println (foo);
Serial.println (bar);
} // end of setup
void loop ()
{
} // end of loop
ここには、fooとbarという2つの変数があります。setup()でそれらの1つを変更しますが、結果は次のとおりです。
Thos is some text
Thos is some text
彼らは両方とも変わった!
実際、警告を見ると、次のことがわかります。
sketch_jul14b.ino:1: warning: deprecated conversion from string constant to ‘char*’
sketch_jul14b.ino:2: warning: deprecated conversion from string constant to ‘char*’
コンパイラはこれが危険であると知っており、それは正しいです!この理由は、コンパイラーが(合理的に)文字列定数が変わらないことを期待しているからです(定数であるため)。したがって"This is some text"
、コード内で文字列定数を複数回参照する場合、それらすべてに同じメモリを割り当てることができます。1つを変更すると、それらすべてを変更することになります。
*foo
と*bar
使用は、これが起こらないようにしますか?また、これは、次のような文字列をまったく配置しないこととどのように異なりますか?char *foo;
new
、strcpy
とdelete
)。
関数がをとる文字列定数を渡そうとするのをやめるか、関数がをとるようchar*
に変更しますconst char*
。
「ランダムな文字列」のような文字列は定数です。
例:
void foo (char * s)
{
Serial.println (s);
}
void setup ()
{
Serial.begin (115200);
Serial.println ();
foo ("bar");
} // end of setup
void loop ()
{
} // end of loop
警告:
sketch_jul14b.ino: In function ‘void setup()’:
sketch_jul14b.ino:10: warning: deprecated conversion from string constant to ‘char*’
この関数foo
はchar *を予期します(したがって変更できます)が、変更しない文字列リテラルを渡します。
コンパイラはこれをしないよう警告しています。非推奨であるため、将来のコンパイラバージョンでは警告からエラーに変わる可能性があります。
解決策:fooがconst charを取るようにする*:
void foo (const char * s)
{
Serial.println (s);
}
わかりません。変更できないという意味ですか?
古いバージョンのC(およびC ++)では、上記の私の例のようなコードを記述できます。あなたは、関数(のように作ることができfoo
、あなたがそれまで渡す何かを印刷し)、その後、文字列リテラル伝承(例えば。foo ("Hi there!");
)
ただし、char *
引数として受け取る関数は、その引数を変更できます(つまりHi there!
、この場合は変更できます)。
たとえば、次のように書いたかもしれません。
void foo (char * s)
{
Serial.println (s);
strcpy (s, "Goodbye");
}
残念ながら、リテラルを渡すことで、そのリテラルを潜在的に変更して、「こんにちは!」今は「さようなら」で、これは良くありません。実際、長い文字列でコピーした場合、他の変数を上書きする可能性があります。または、一部の実装では、「こんにちは」という理由でアクセス違反が発生します。読み取り専用(保護された)RAMに置かれた可能性があります。
そのため、コンパイラライターはこの使用法を徐々に非推奨にしているため、リテラルを渡す関数はその引数をとして宣言する必要がありますconst
。
can not
変更されるということですか?
私はこのコンパイルエラーがあります:
TimeSerial.ino:68:29: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(Serial.find(TIME_HEADER)) {
^
この行を置き換えてください:
#define TIME_HEADER "T" // Header tag for serial time sync message
この行で:
#define TIME_HEADER 'T' // Header tag for serial time sync message
コンパイルはうまくいきます。