5

次の2つのコードを想定します。

char *c = "hello world";
c[1] = 'y';

上記のものは機能しません。

char c[] = "hello world";
c[1] = 'y';

これはそうです。

最初のものに関しては、文字列「hello world」が読み取り専用メモリセクションに格納されている可能性があるため、変更できないことを理解しています。ただし、2つ目はスタック上に文字配列を作成するため、変更できます。

私の質問はこれです-なぜコンパイラは最初のタイプのエラーを検出しないのですか?なぜそれはC標準の一部ではないのですか?これには特別な理由がありますか?

4

4 に答える 4

5

C文字列リテラルはではないため、Cコンパイラは最初のエラーを検出する必要はありませんconst

C99標準のN1256ドラフトを参照してください:

6.4.5パラグラフ5:

変換フェーズ7では、値ゼロのバイトまたはコードが、1つまたは複数の文字列リテラルから生じる各マルチバイト文字シーケンスに追加されます。次に、マルチバイト文字シーケンスを使用して、シーケンスを含めるのに十分な静的ストレージ期間と長さの配列を初期化します。文字列リテラルの場合、配列要素の型はcharであり、マルチバイト文字シーケンスの個々のバイトで初期化されます。[...]

パラグラフ6:

それらの要素が適切な値を持っている場合、これらの配列が別個であるかどうかは指定されていません。プログラムがそのような配列を変更しようとした場合、動作は未定義です。

(C11はこれを変更しません。)

したがって、文字列リテラル"hello, world"char[13]ではなく )型であり、ほとんどのコンテキストでconst char[13]変換されます。char*

オブジェクトを変更しようとするとconst動作が定義されておらず、変更しようとするほとんどのコードはコンパイラーによって診断される必要があります(たとえば、キャストでそれを回避できます)。文字列リテラルを変更しようとすると、未定義の動作が発生しますが、それが原因でconstはありません(そうではありません)。これは、標準で動作が未定義であると具体的に規定されているためです。

たとえば、このプログラムは厳密に準拠しています。

#include <stdio.h>

void print_string(char *s) {
    printf("%s\n", s);
}

int main(void) {
    print_string("Hello, world");
    return 0;
}

文字列リテラルがの場合、(非)をとる関数constに渡すには診断が必要になります。プログラムは有効ですが、が指す文字列を変更しようとすると、未定義の動作を示します。"Hello, world"constchar*print_string()s

その理由は歴史的です。Pre-ANSI Cにはキーワードがなかったため、aを取り、それが指すものを変更しないことを約束constする関数を定義する方法はありませんでした。char*ANSI C(1989)で文字列リテラルを作成constすると、既存のコードが壊れてしまい、標準の以降のエディションでそのような変更を行う良い機会がありませんでした。

gccは、-Wwrite-strings文字列リテラルをとして処理しますconstが、これに対する診断を発行できないため、gccを不適合コンパイラにします。

const char (*p)[6] = &"hello";

"hello"はタイプchar[6]であるため、&"hello"はタイプchar (*)[6]であり、宣言されたタイプのとは互換性がありませんp。で-Wwrite-strings、はタイプ&"hello"として扱われconst char (*)[6]ます。)おそらくこれが理由であり、。-Wall-Wextra含まれていません-Wwrite-strings

一方、警告をトリガーするコードは、-Wwrite-stringsとにかく修正する必要があります。Cコードを記述して、診断の有無にかかわらずコンパイルできるようにすることは悪い考えではありません-Wwrite-strings

( BjarneStroustrupがC++を設計していたとき、彼は古いCコードとの厳密な互換性についてそれほど心配していなかったため、C ++文字列リテラルであることに注意してください。) const

于 2012-01-24T10:18:54.873 に答える
3

コンパイラは最初の「エラー」を検出できます。

const char*gcc の最近のバージョンでは、-Wwrite-strings を使用すると、 fromから に代入できないというメッセージが表示されchar*ます。この警告は、C++ コードではデフォルトでオンになっています。

それが問題です - 最初の割り当てであり、c[1] = 'y'ビットではありません。もちろん、 を取得してchar*逆参照し、逆参照されたアドレスに代入することは合法です。

からの引用man 1 gcc

When compiling C, give string constants the type "const char[length]" so that
copying the address of one into a non-"const" "char *" pointer will get a warning.
These warnings will help you find at compile time code that can try to write into a
string constant, but only if you have been very careful about using "const" in
declarations and prototypes. Otherwise, it will just be a nuisance. This is why we
did not make -Wall request these warnings.

つまり、基本的に、ほとんどのプログラマーは C の初期の頃に const に正しいコードを記述していなかったため、gcc のデフォルトの動作ではありません。しかし、それはg ++用です。

于 2012-01-24T04:04:26.643 に答える
2

-Wwrite-stringsあなたがやりたいことをしているようです。これがの一部であると誓った可能性があり-Wallます。

% cat chars.c 
#include <stdio.h>

int main()
{
  char *c = "hello world";
  c[1] = 'y';
  return 0;
}
% gcc -Wall -o chars chars.c          
% gcc -Wwrite-strings -o chars chars.c
chars.c: In function ‘main’:
chars.c:5: warning: initialization discards qualifiers from pointer target type

マニュアルページから:

Cをコンパイルするときは、文字列定数に「const char [length]」型を指定して、1つのアドレスを「const」以外の「char*」ポインタにコピーすると警告が表示されるようにします。これらの警告は、コンパイル時に文字列定数への書き込みを試みることができるコードを見つけるのに役立ちますが、宣言とプロトタイプでの「const」の使用に非常に注意している場合に限ります。そうでなければ、それは単に迷惑になります。これが、-Wallがこれらの警告を要求しない理由です。

C ++をコンパイルするときは、文字列リテラルから「char*」への非推奨の変換について警告してください。この警告は、C++プログラムではデフォルトで有効になっています。

「C++でデフォルトで有効になっている」というのは、おそらく私(および他の人)-Wallがそれをカバーしていると思う理由であることに注意してください。また、それがの一部ではない理由についての説明にも注意してください-Wall

標準に関連するものとして、 C99、6.4.5項目6(リンクされたPDFの63ページ)は次のようになっています。

それらの要素が適切な値を持っている場合、これらの配列が別個であるかどうかは特定されていません。プログラムがそのような配列を変更しようとした場合、動作は定義されていません。

于 2012-01-24T04:13:56.250 に答える
-1

char* c = strdup("...");c[1]賢明になります。( C に関する暴言を削除しました) インテリジェントなコンパイラはこれに対して警告することができます/実際に警告しますが、C は伝統的にマシンに近く、(境界/フォーマット/...) チェックやその他の「不必要な」オーバーヘッドはありません。

lintは、そのようなエラーを検出するためのツールです:const char*が に割り当てられたことchar*。また、 a をマークしますchar c = c[30];(型に依存しなくなりましたが、エラーのアドレス指定も行います)。 c を として宣言するとよいでしょうconst char*。C は、寛大な伝統を持ち、多くのプラットフォームで動作する古い言語です。

于 2012-01-24T04:03:48.277 に答える