2012-04-22 18 views
13

私は、例えば"hello"const char*であることを知っています。だから、私の質問は以下のとおりです。const char *をchar *に代入するのはなぜですか?

  1. はどのように我々はこのような非const char*"hello"のような文字列リテラルを割り当てることができます。

    char* s = "hello"; // "hello" is type of const char* and s is char* 
            // and we know that conversion from const char* to 
            // char* is invalid 
    
  2. は、すべてのメモリがかかります"hello"のような文字列リテラルであり、私のプログラム、または文が終わると破壊される一時変数のようなものですか?

+2

これは、C互換のみに許可されています。一般的にC++では、非推奨と見なされ、良いコンパイラが警告を出します。 – iammilind

答えて

22

実際に"hello"は、タイプchar const[6]です。

しかし、問題の要点は、まだ権利である - なぜC++は、私たちは非const型に読み出し専用のメモリ位置を割り当てることができるのですか?

このための唯一の理由は、後方constを知らなかった古いCコードに互換性です。 C++が厳格であれば、既存のコードが壊れてしまいます。

ほとんどのコンパイラは、このようなコードについてはにと警告するか、デフォルトでそうするように設定することができます。さらに、C++ 11ではこれを完全に禁止していますが、コンパイラはまだそれを強制しないかもしれません。 Standerdeseファンのため



[参考文献1]C++ 03標準:広くないリテラル§4.2/ 2

列(2.13.4)文字列リテラルは "ポインタへのポインタ"型のrvalueに変換できます。ワイド文字列リテラルを "wchar_tへのポインタ"型のrvalueに変換することができます。どちらの場合でも、結果は配列の最初の要素へのポインタになります。この変換は、明示的に適切なポインタのターゲット型がある場合にのみ考慮され、左辺値から右辺値に変換する一般的な必要性がない場合には考慮されません。 [注:この変換は非推奨のです。附属書Dを参照]。過負荷解決(13.3.3.1.1)のランク付けのために、この変換は配列からポインタへの変換とそれに続く修飾変換(4.4)と考えられる。 [例: "abc"は配列からポインタへの変換として "const charへのポインタ"に変換され、次に修飾変換として "charへのポインタ"に変換されます。 ]

C++ 11は、単にC++ 11で不正なコードであることを意味する上記引用を単に削除します。

[参考文献2]C99標準6.4.5/5 "文字列リテラル - セマンティクス":翻訳段階7において

、値ゼロのバイトまたはコードは、各マルチバイト文字に付加され文字列リテラルまたはリテラルから生成されるシーケンス。次に、マルチバイト文字シーケンスを使用して、シーケンスを格納するのに十分なだけの静的記憶期間および長さのアレイを初期化する。文字列リテラルの場合、配列要素はchar型を持ち、マルチバイト文字列の個々のバイトで初期化されます。ワイド文字列リテラルの場合、配列要素にはwchar_t型があり、ワイド文字列で初期化されます。

要素に適切な値が設定されていれば、これらの配列が異なるかどうかは不明です。 プログラムがそのような配列を変更しようとすると、動作は未定義です。

+1

Standardから関連する引用符を追加しました。あなたが気にしないことを願います。 –

+0

デバッグモードのMSVCは、実行時に文字列リテラルに書き込むのをやめ、コンパイル時にGCCを終了します(可能な場合)。しかし、オプションがあります。私はそれが-wwrite-stringsかそれとも互換モードをオンにし、R/Wメモリに文字列リテラルを入れていると信じています。 –

+2

@Alsあなたはシンプルさを取り入れて醜いものにしました。 ;-)しかし、ありがとう。私は実際にそれを追加したいと思っていましたが、私のドラフトC++ 11で関連する箇所を見つけることができません。 2.14.3では型を定義していますが、附属書では変換の無効を指定していますが、これは非難されましたが、C++ 03で有効であることは何も示されていません。 @KonradRudolph; –

1

ちょうど使用string:C++の方法だろう

std::string s("hello"); 

charを実際に使用する必要がある場合は、配列を作成して内容をコピーする必要があります。

2

は、「hello」のようなリテラル文字列は、すべてのプログラムでメモリを占有します。すべての文は、文が終了すると破棄される一時変数と同じです。

これはプログラムデータに保存されているため、プログラマーの存続期間内で有効です。現在のスコープからポインタとこのデータへの参照を返すことができます。

const char*char*にキャストされる唯一の理由は、winapiシステムコールのように、cと似ています。そして、このキャストは、他のconstキャスティングと違って、明示されていません。

+0

const char *は、コンパイラによってchar *に明示的にキャストできる唯一の型です。 – AlexDan

+0

AlexDan、 'const char *'はconst以外の形式にキャストできる唯一の型です(例えば、 'const int *'は 'int *'に明示的にキャストできません)。しかし、コンパイラを調整してウォーリングを生成することができます。 –

+2

また、*キャスト*は不規則な動詞、3形式:*キャストキャストキャスト* –

-3

あなたの例では、割り当てはしていませんが、構築中です。たとえばstd :: stringにはstd::string(const char *)というコンストラクタがあります(実際は複雑ですが、問題はありません)。同様に、char *(型へのポインタではなく型の場合)はconst char *コンストラクタを持つことができ、これはメモリをコピーしています。

コンパイラの実際の動作は実際には分かりませんが、これは先ほど説明したものと似ていると思います。"Hello"のコピーがスタックに作成され、sがこのコピーのアドレスで初期化されます。

+2

これは単に正しくありません。コピーは行われず、ポインタを介して文字列を変更するのはUBです。 –

+0

スタックに「Hello」のコピーはありません!いくつかのグローバル[データセグメント](https://secure.wikimedia.org/wikipedia/en/wiki/Data_segment)で作成される文字列リテラルです(実装固有の詳細です)。 – Praetorian

+0

@KonradRudolph、私はそれが正しいとは決して言わなかった。私は悲しい、それはこれのようになることができる。とにかくリンクに感謝します。 – Steed

1

2番目の質問に対する答えは、変数sがタイプポインターとしてRAMに格納されているということです。グローバルまたは静的な場合は、ヒープ上に割り当てられ、実行中のプログラムの存続期間中はそこに残ります。ローカル変数( "auto")の場合、スタックに割り当てられ、現在の関数が返るまでそこに残ります。いずれの場合も、ポインタを保持するために必要なメモリ量を占有します。

文字列"Hello"は定数であり、他のすべての定数および初期化子と共にプログラム自体の一部として格納されます。アプライアンスで実行するプログラムを作成した場合、その文字列はROMに格納されます。

文字列が定数でsがポインタなので、コピーする必要はありません。ポインタsは、文字列が格納されている場所を指します。

関連する問題