私は最近、Cでプログラミングを始めました。JavaとPythonです。今、私の本では、「Hello World」プログラムを作成するための構文は次のようになっています。
char message[10]
strcpy(message, "Hello, world!")
printf("%s\n", message);
さて、この例ではchar配列を使用していますが、文字列はどうなりましたか?なぜそれらの1つだけを使用できないのですか?たぶんこれを行う別の方法がありますか?
Cにはネイティブの文字列型がありません。慣例により、言語はchar
の配列をヌル文字で終了します。つまり、'\0'
で終了します。言語の標準ライブラリの関数とマクロは、ヌル終了文字配列のサポートを提供します。たとえば、 strlen は、'\0'
文字に遭遇するまでchar
の配列を繰り返し処理します。 strcpy'\0'
が見つかるまでソース文字列からコピーします。
Cでのヌル終了文字列の使用は、Cがアセンブリ言語よりも少しだけ高レベルになることを意図していたという事実を反映しています。 PDP-10およびPDP-11のアセンブリ言語 では、ゼロ終端文字列がその時点ですでに直接サポートされていました。
C文字列のこのプロパティは、深刻なセキュリティ上の欠陥を含む、かなり多くの厄介なバッファオーバーランバグにつながることに注意してください。たとえば、strcpy
へのソース引数として渡された文字列をnullで終了することを忘れた場合、関数は、発生するまでソース文字列の終わりを超えてメモリ内にあるものから連続したバイトをコピーし続けます0
に遭遇すると、メモリ内の宛先文字列の位置に続く貴重な情報を上書きする可能性があります。
コード例では、文字列リテラル「Hello、world!」 char
の14バイト長の配列にコンパイルされます。最初の13バイトには、文字、コンマ、スペース、感嘆符が入り、最後のバイトには、コンパイラによって自動的に追加されるヌル終了文字'\0'
が入ります。配列の最後の要素にアクセスする場合、0
と等しいことがわかります。例えば。:
const char foo[] = "Hello, world!";
assert(foo[12] == '!');
assert(foo[13] == '\0');
ただし、この例では、message
の長さはわずか10バイトです。 strcpy
は、message
のアドレスから始まるメモリに、ヌルターミネータを含む14バイトすべてを書き込みます。最初の10バイトはmessage
のスタックに割り当てられたメモリに書き込まれ、残りの4バイトは単にスタックの最後に書き込まれます。この4つの追加バイトをスタックに書き込むことの結果は、この場合予測するのが困難です(この単純な例では、物を傷つけないかもしれません)が、実際のコードでは、通常、破損したデータまたはメモリアクセス違反エラーにつながります。
string
にC
タイプはありません。文字配列を使用する必要があります。
ちなみに、配列のサイズは、配列全体に1つの追加のゼロ終了文字を追加できるようにする必要があるため、コードは機能しません。
あなたが言及した言語でそれを書き留めるには:
Java:
String str = new String("Hello");
Python:
str = "Hello"
JavaとPythonには「文字列」という概念がありますが、Cには「文字列」という概念がありません。Cには文字配列があります。 「読み取り専用」または操作可能。
C:
char * str = "Hello"; // the string "Hello\0" is pointed to by the character pointer
// str. This "string" can not be modified (read only)
または
char str[] = "Hello"; // the characters: 'H''e''l''l''o''\0' have been copied to the
// array str. You can change them via: str[x] = 't'
文字配列は、末尾に一意のセンチネル文字(通常はNULLターミネータ'\0'
)。上記の場合、センチネル文字が自動的に自動的に追加されることに注意してください。
Cでは、文字列は単にnullバイトで終わる文字の配列です。 char*
は、Cコードを読んでいるときに「string」と発音されることがよくあります。
Cは、ファーストクラスの文字列型をサポートしていません。
C++にはstd :: stringがあります
Cには、Javaのような独自のStringデータ型はありません。
文字配列または文字ポインターを使用してCでStringデータ型を宣言できるのは、たとえば次のとおりです。
char message[10];
or
char *message;
ただし、少なくとも宣言する必要があります。
char message[14];
「Hello、world!」をコピーしますメッセージ変数に。
まず、すべてを行う必要はありません。特に、strcpy
は冗長です-printf
itに文字列をコピーする必要はありません。 message
は、その文字列を適切に定義することができます。
第二に、あなたはその「Hello、World!」のための十分なスペースを許可していません。文字列(message
は少なくとも14文字である必要があり、ヌルターミネータに余分な文字を使用できます)。
理由は、しかし、それは歴史です。アセンブラには、文字列はなく、バイト、単語などのみがあります。Pascalには文字列がありましたが、そのため静的型付けに問題がありました-string[20]
はstring[40]
。初期にもこの問題を回避する言語がありましたが、間接性と動的割り当てのオーバーヘッドが発生し、当時ははるかに効率的な問題でした。
Cは単純にオーバーヘッドを回避し、非常に低いレベルを維持することを選択しました。文字列は文字配列です。配列は、最初の項目を指すポインターと非常に密接に関連しています。配列型がポインター型に「減衰」すると、静的な型からバッファーサイズ情報が失われるため、古いPascal文字列の問題は発生しません。
C++では、std::string
これらの問題の多くを回避するクラス-動的な割り当てのオーバーヘッドがありますが、最近はそれを気にしません。とにかく、std::string
はライブラリクラスです。その下にはCスタイルの文字配列処理があります。