7

私はCおよびC++プログラムで作業しています。以前は、make-strings-writableオプションなしでコンパイルしていました。しかし、それはたくさんの警告を受けていたので、私はそれをオフにしました。

次に、「関数fooの引数3でconstchar*をchar*に変換できません」という形式のエラーが大量に発生しました。それで、私はそれらを修正するために多くの変更を行いました。

しかし、今日、リテラル ""がchar*を期待する関数に渡され、0番目の文字が0に設定されていたため、プログラムがクラッシュしました。定数を編集しようとしただけで、何も悪いことはありませんでした。クラッシュします。

私の質問は、なぜそれがコンパイラエラーではなかったのかということです。

重要な場合、これはgcc-4.0でコンパイルされたMac上にありました。

編集:追加されたコード:

char * host = FindArgDefault("EMailLinkHost", "");
stripCRLF(linkHost, '\n');

どこ:

char *FindArgDefault(char *argName, char *defVal) 
{// simplified
    char * val = defVal;
    return(val);
}

void stripCRLF(char *str, char delim)
{
    char *p, *q;

    for (p = q = str; *p; ++p) {
        if (*p == 0xd || *p == 0xa) {
            if (p[1] == (*p ^ 7)) ++p;
            if (delim == -1) *p = delim;
            }
        *q++ = *p;
        }
    *q = 0;  // DIES HERE
}

これはコンパイルされ、*qを0に設定しようとするまで実行されました...

編集2:

ほとんどの人は私の質問の要点を見逃しているようです。char foo []="bar"が機能する理由を知っています。なぜchar*foo = "bar"; 動作しません。

私の質問は主にパラメータの受け渡しに関するものです。私に起こることの1つは、「これがCとC ++の問題である可能性はありますか?」です。いくつかの.cファイルといくつかの.cppファイルがあり、Cで許可されている可能性は十分にありますが、C++では許可されていません...またはその逆です...

4

6 に答える 6

8

この規格は、リテラルから変換への変換を許可する特別なルールを指定しており、これにより、資格char*が静かに失われます。const(4.2 / 2):

ワイド文字列リテラルではない文字列リテラル(2.13.4)は、「pointertochar」タイプの右辺値に変換できます。ワイド文字列リテラルは、「wchar_tへのポインタ」タイプの右辺値に変換できます。いずれの場合も、結果は配列の最初の要素へのポインターになります。この変換は、明示的に適切なポインタターゲットタイプがある場合にのみ考慮され、左辺値から右辺値に変換する一般的な必要がある場合には考慮されません。[注:この変換は非推奨です。付録Dを参照してください。]

C ++ 0x標準は、その非推奨をさらに進めます…このナンセンスなルールは、今後の標準から完全に削除されます。

const char*toエラーは、リテラルを最初char*のリテラルに変換した結果である必要があります。const char*

于 2010-05-03T19:12:22.467 に答える
6

文字列リテラルを使用しchar *てC++でポインタを初期化することは非推奨の機能ですが、それでも問題はありません。エラーではありません。そのようなポインタを介して変更が試みられないようにするのはあなたの責任です。

つまり、以前に取得したコンパイルエラーについて何かを誤解している必要があります。このような初期化/割り当てでエラーが発生したことはないと思います。質問で言及した「constchar*をchar*に変換できません」というエラーは、他の何かによって生成されたものである必要があります。

文字列リテラルでポインタを初期化できるという事実は、ポインタを初期化するためにchar *任意const char *の値を使用できることを意味しないことに注意してくださいchar *。このコード

const char *pc = "A";
char *p = pc;

エラーが発生しますが、

char *p = "A";

しない。const char *前述の非推奨の機能は、すべてのポインタではなく、文字列リテラルにのみ適用されます。

于 2010-05-03T19:07:01.070 に答える
2

私は他の回答に完全に同意します。g++(少なくともバージョン4.4)が実際にこれらの非推奨の変換を任意の警告レベルで警告としてキャッチすることを追加したいと思います(以前のバージョンがデフォルトでこれを行わない場合は、おそらく警告を上げる必要がありますレベル):

#include <iostream>

using namespace std;

void WithConst(const char * Str)
{
    cout<<Str<<endl;
}

void WithoutConst_NoEdit(char * Str)
{
    cout<<Str<<endl;
}

void WithoutConst_Edit(char * Str)
{
    *Str='a';
    cout<<Str<<endl;
}

int main()
{
    WithConst("Test");
    WithoutConst_NoEdit("Test");
    WithoutConst_Edit("Test");
    return 0;
}

 

matteo@teoubuntu:~/cpp/test$ g++ --version
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
matteo@teoubuntu:~/cpp/test$ g++ -O3 lit_const_corr.cpp -o lit_const_corr.x
lit_const_corr.cpp: In function ‘int main()’:
lit_const_corr.cpp:24: warning: deprecated conversion from string constant to ‘char*’
lit_const_corr.cpp:25: warning: deprecated conversion from string constant to ‘char*’
matteo@teoubuntu:~/cpp/test$ g++ -O3 -Wall lit_const_corr.cpp -o lit_const_corr.x
lit_const_corr.cpp: In function ‘int main()’:
lit_const_corr.cpp:24: warning: deprecated conversion from string constant to ‘char*’
lit_const_corr.cpp:25: warning: deprecated conversion from string constant to ‘char*’
matteo@teoubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x
lit_const_corr.cpp: In function ‘int main()’:
lit_const_corr.cpp:24: warning: deprecated conversion from string constant to ‘char*’
lit_const_corr.cpp:25: warning: deprecated conversion from string constant to ‘char*’

さらに、内部で興味深いことが起こっています。最適化せずにコンパイルすると、「コードの指示どおりに実行される」ため、読み取り専用メモリの場所に書き込もうとするため、クラッシュします。

matteo@teoubuntu:~/cpp/test$ g++ -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x
lit_const_corr.cpp: In function ‘int main()’:
lit_const_corr.cpp:24: warning: deprecated conversion from string constant to ‘char*’
lit_const_corr.cpp:25: warning: deprecated conversion from string constant to ‘char*’
matteo@teoubuntu:~/cpp/test$ ./lit_const_corr.x 
Test
Test
Segmentation fault

ただし、オプティマイザをオンにすると、クラッシュは発生しません。

matteo@teoubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x
lit_const_corr.cpp: In function ‘int main()’:
lit_const_corr.cpp:24: warning: deprecated conversion from string constant to ‘char*’
lit_const_corr.cpp:25: warning: deprecated conversion from string constant to ‘char*’
matteo@teoubuntu:~/cpp/test$ ./lit_const_corr.x 
Test
Test
Test

これは魔法の最適化のトリックによるものだと思いますが、なぜそれが適用されるのかわかりません。何か案が?


補遺

char * foo = "bar"を宣言すると、実際には文句を言います。しかし、char foo [] = "bar"と宣言すると、宣言されません。

ねえ、2つのことを混同しないように注意してください:と

char * foo = "bar";

charへのポインタを宣言し、それにリテラル「bar」のアドレスを割り当てます。これは、実際には読み取り専用のメモリ位置に格納されています(通常、メモリにマップされる実行可能ファイルの一部です)。代わりに、

char foo[]="bar";

「bar」値で初期化されるcharの配列に対して、RWメモリ(スタック上またはコンテキストに応じて他の場所)を宣言して割り当てていますが、文字列テーブルとはまったく関係ありません。その文字列を変更することは完全に合法です。

于 2010-05-03T20:35:12.163 に答える
1

To answer the question why this conversion is legal (although deprecated). Well there was a time, when there was no const keyword in the C language and people managed to produce a bit of code during that time. The designers of C++ must have figured out, that it wasn't a good idea to upset so many people, by breaking their code.

于 2010-05-03T20:48:00.177 に答える
1

それは本当にあなたが「それらを修正するためにどのように経験し、たくさんの変更を加えたか」に依存します。

文字列リテラルをにダウンキャストした場合は、そのエラーをキャッチしないchar*ようにコンパイラに指示しています。変更する場合は、コピーを作成する必要があります。それ以外の場合は、コンパイラがこれらをチェックできるように、関数インターフェイスを宣言してください。const

于 2010-05-03T19:09:02.057 に答える
1

このstripCRLF関数は文字列をその場で変更しますが、それに対して何も実行せず、値を返すこともないため、文字列リテラルを関数に渡すことは基本的にノーオペレーションであり、バグと見なす必要があります。これを解決するには、関数に変更を加えて文字列のコピーを返すか、より厳密な警告フラグを設定して、これがいつ発生したかを検出できるようにします。

このようなことを gcc に警告させたい場合は、-Wwrite-stringsコンパイラ オプションをオンにします。これにより、文字列定数が非定数に変換された場合にコンパイラに警告が表示されchar*ます。また、おそらく便利な-Wcast-qualオプションです。これは、型修飾子を削除する方法でポインターがキャストされるたびに警告を発するはずです(あなたの場合、const削除されました)。これらのメッセージをより強く表示したい場合は、 を使用-Werrorしてすべての警告をエラーにします。

もう1つの争点はFindArgDefault機能です。提供されているように、関数シグネチャは、戻り値とパラメーターの型のconst char*代わりに、より正確に使用する必要があります。char*これにより、戻り値が a に割り当てられたときにコンパイラが文句を言うようになりますchar*(-Wcast-qualオプションが使用されている場合)。完全な関数を投稿していないため、これは有効な変更ではない可能性があります。いずれかの文字列が関数内で変更された場合、対応するパラメーターは のままでなければなりませんがchar*、その場合、引数として文字列リテラルを渡すと、コンパイラの警告が生成されます (使用-Wwrite-strings)。

ところで、あなたのstripCRLF関数は NULL ポインターが渡されたときに問題が発生しやすくif (delim == -1)なり!=ます。

編集: OPが受け取ったエラーメッセージに関する詳細情報を確認した後、元の投稿のトピックから外れた部分を削除し、いくつかのコメントを追加しました.

Edit2: 私はあなたのプログラムの次の簡略化されたバージョンをテストしました:

char *FindArgDefault(char *argName, char *defVal) {
    char * val = defVal;
    return(val);
}

int main (void) {
    char * host = FindArgDefault("EMailLinkHost", "");
    return (int)(host);
}

でコンパイルするとgcc -Wall test.c -o test.o、コンパイラの警告やエラーはゼロになりました。

でコンパイルするとgcc -Wwrite-strings -Wall test.c -o test.o

test.c: 関数 'main' 内:

test.c:10: 警告: 'FindArgDefault' の引数 1 を渡すと、ポインター ターゲット型から修飾子が破棄されます

test.c:10: 警告: 'FindArgDefault' の引数 2 を渡すと、ポインター ターゲット型から修飾子が破棄されます

-Wwrite-stringsこの種の問題について警告するために有効にしたいのはコンパイラオプションだと思います。

于 2010-05-03T21:42:06.807 に答える