3

文字列を事前に初期化された配列 a[] に再割り当てしようとしましたが、取得できるのはエラーだけでした

main()
{
    char a[] = "Sunstroke";
    char *b = "Coldwave";

    a = "Coldwave";
    b = "Sunstroke";
    printf("\n %s %s",a,b);
}

[エラー]: タイプ 'char *' からタイプ 'char[10]' に割り当てるときに互換性のないタイプです..これを検索しましたが、理由が見つかりませんでした..また、次のように再宣言して再割り当てしようとしました

char a[] = "Sunstroke";

しかし、それはうまくいきませんでした...

しかし、ポインタの場合、上記のプログラムのように可能でした..

4

4 に答える 4

6

ここで何が起こっているのかを理解するには、次の 2 つの言語規則が重要です。

  • 配列は代入できません。
  • 配列は、その最初の要素へのポインターに変換できます。

文字列リテラルのようなものを理解することも重要"Sunstroke"です。これは定数文字の静的配列であり、文字列のすべての文字を最後にターミネータで保持するのに十分な大きさです。したがって、この場合、これはconst char[10]配列であり、9 つの文字の後にゼロ値のターミネータが続きます。staticであるため、配列はプログラムの存続期間中、メモリ内のどこかに格納されます

char a[] = "Sunstroke";

これにより、ローカル配列が作成され、文字列リテラルから文字をコピーして初期化されます。

char *b = "Coldwave";

これにより、ポインターが作成され、リテラル自体を指すように初期化されます。これは危険であることに注意してください: リテラルは ですconstが、ポインターはそうではないため、リテラルを変更しようとするコードを記述して、未定義の動作を与えることができます。この変換は推奨されていない (確かに C++ では、C についてはわかりません) ため、コンパイラは警告を表示する必要があります。できる限りすべてのコンパイラ警告を有効にしましたね。

a = "Coldwave";

これは配列の再割り当てを試みますが、配列は割り当て可能でないため失敗します。そうでない理由は特にありません。それがまさに言語が進化した方法です。

b = "Sunstroke";

これにより、ポインターが別のリテラルを指すように再割り当てされます。constそれは問題ありません(上記の欠如を除いて)。

文字列を操作する必要がある場合は、次のようにします。

  • C では、必要に応じて十分な大きさの配列を慎重に作成し、ライブラリ関数<string.h>(または独自の手作りコード) を使用してそれらの配列内の文字を操作する必要があります。
  • C++ では、std::stringクラスを使用してメモリ管理、割り当てなどを処理します。
于 2013-09-11T15:51:41.697 に答える
4

「Coldwave」などのハードコードされた文字列リテラルは、実際にはchar[](char 配列) 型ですが、それらを変更する動作は未定義です ( C99 :6.4.5.6)。ただし、以下bはまだchar*(char ポインター)であることに注意してください。

char *b = "Coldwave";

char[]割り当てられたもの。大丈夫。ただし、これとは異なります。

char a[] = "Coldwave";

これは の初期化ですchar[]。変数は宣言時に一度だけ初期化できます。初期化は、このような割り当てを介して配列または他の複合型 (構造体など) を設定できる唯一の状況です。ただし、これはできません。

char c[] = a;

代入の右側で使用すると、配列変数はそれらが表す配列へのポインターとして機能するため、機能するのはそのためchar *b = aです。

したがって、上記の変数でこれを行うことができない理由は次のとおりです。

a = b;
// or
a = "Sunstroke";

char*それは aを aに割り当てることになるからですchar[]-- ダメです; 逆にしかできません。

于 2013-09-11T15:39:53.873 に答える
3

Cの場合、 c99 ドラフト標準セクションの6.5.16 代入演算子の段落2を見ると、次のように書かれています。

代入演算子は、左オペランドとして変更可能な左辺値を持つものとします。

およびセクション6.3.2.1 左辺値、配列、および関数指定子の段落1は次のように述べています。

[...]変更可能な左辺値は、配列型を持たない左辺値です[...]

したがって、配列は変更可能な左辺値ではないため、それらに割り当てることはできません。初期化セクション6.7.8 初期化パラグラフ14については、次のように述べています。

文字型の配列は、文字列リテラルで初期化できます[...]

C++ ドラフト標準では、関連するセクションは4.2 配列からポインターへの変換の段落1であり、次のように記述されています。

「NT の配列」または「T の境界が不明な配列」型の左辺値または右辺値は、「T へのポインタ」型の prvalue に変換できます。結果は、配列の最初の要素へのポインターです。

prvalueは純粋な右辺値であり、セクション5.17 代入および複合代入演算子の段落1には次のように書かれています。

[...]すべて、左オペランドとして変更可能な左辺値が必要です[...]

于 2013-09-11T15:46:42.110 に答える
1

プログラムを単純化して、次のようにします。

char a[] = "Sunstroke";
char *b = a;

のアドレスが 100 であると仮定すると、aメモリ内では次のようになります (ポインターのサイズとエンディアンなどは異なる場合があります)。

[S] [u] [n] [s] [t] [r] [o] [k] [e] [\0]         ...       [0] [0] [0] [100]
100 101 102 103 104 105 106 107 108 109                           200
 ^                                                                 ^
 |                                                                 |
 a                                                                 b

アレイのライフサイクルaが常に同じ場所である限り、それを変更することはできません。

b一方、 は配列のアドレスを含むポインターであるため、 の値を変更して他の場所を指すことができます。b

于 2013-09-11T15:48:10.247 に答える