web-dev-qa-db-ja.com

C-strcpy()が必要な理由

次のコードスニペットのように、文字列を文字配列に割り当てるためにstrcpy()が必要な理由を誰かに説明してもらえますか?.

int main(void) {

char s[4];

s = "abc"; //Fails
strcpy(s, "abc"); //Succeeds

return 0;
}

s = "abc"が失敗する理由は何ですか?そして、なぜstrcpy()は、宣言された後に文字列をchar配列に割り当てる唯一の方法なのですか?基本的な割り当てを実行するために関数を使用する必要があるのは、私には奇妙に思えます。

28
C_p678

Cの配列は、割り当て不可およびコピー初期化不可です。これがCでの配列の状態です。歴史的に、(割り当てのRHSでの)値のコンテキストでは、配列はポインターに減衰します。これが、割り当てとコピーの初期化を正式に妨げるものです。これは、char配列だけでなく、すべての配列に適用されます。

C言語は、この配列の動作をその前身であるB言語とBCPL言語から継承します。これらの言語では、配列は物理ポインターで表されていました。 (そして明らかに、ポインターの再割り当ては、ある配列を別の配列に割り当てるときに起こりたいことではありません。)C言語では、配列はポインターではありませんが、減衰することによってBおよびBCPL配列の履歴動作を「シミュレート」します。ほとんどの場合、ポインタへ。この歴史的な遺産が、Cアレイを今日までコピーできないようにしている理由です。

上記の1つの例外は、文字列リテラルによる初期化です。つまりできるよ

char c[] = "abc";

しかし、それだけです。

つまり、配列をコピーする場合は常に、memcpyなどのライブラリレベルのメモリコピー関数を使用する必要があります。 strcpyは、文字列を処理するように特別に調整されたフレーバーです。

29
AnT

それは単にCの配列です。それらに割り当てることはできません。必要に応じて、ポインタを使用できます。

char *p;
p = "abc";

ちなみに、 C FAQ があります。

配列はCでは「二級市民」です。この偏見の1つの結果は、それらに割り当てることができないということです。

13
cnicutar

簡単な答え:歴史的な理由。 Cには文字列型が組み込まれていませんでした。 std :: stringが登場するのは、C++が登場するまでではなく、それでも最初の実装では実現しませんでした。

長い答え:「abc」のタイプはchar[]ではなく、char *です。 strcpyは、ポインターが指すデータ(この場合はABC)をコピーできるメカニズムの1つです。

配列を初期化する方法はstrcpyだけではありませんが、文字列の最後にある終了0を検出して尊重するのに十分賢い方法です。 memcpyを使用して文字列をsにコピーすることもできますが、その場合は、コピーするデータの長さを渡し、sに終了0(NULL)が存在することを確認する必要があります。

1
Jeff Paquette

C言語には、文字列リテラルへのポインタとその長さの指示を取得するための便利な構文がありません。多くのPascal方言を含む一部の言語では、各文字列の前にその長さを報告するバイトが付いています。これは多くの目的でうまく機能しますが、文字列リテラルを255文字に制限します。 Cのアプローチでは、任意の長さの文字列リテラルに対応できますが、長さに関係なく1バイトのオーバーヘッドしか追加されません。

ゼロで終了する文字列は、ほとんどすべての目的で他の形式より劣っていますother文字列リテラルよりも劣っていますが、リテラルは、多くのプログラムが処理しなければならない最も一般的な形式の文字列であり、したがって、ライブラリ関数がそれらを効果的に処理することにはかなりの利点があります。そうすれば、他のタイプのライブラリルーチンの個別のセットを用意するよりも、理想的とは言えない場合にゼロで終了する文字列を使用する方が簡単になります。

0
supercat