2017-02-05 8 views
5

私はCの初心者です。私はCの配列の概念を理解しようとしています。配列の初期化について混乱があります。文字列リテラルを使って文字の配列を初期化する方が良いですか?

文字列リテラルを使用して文字配列を初期化する方が良い方法はありますか?

char arr[3] = "xyz"; 

または

char arr[] = "xyz"; 

事前に感謝します。

+7

おそらく2番目の方法.1番目の例で導入したバグを防ぐためです。 –

+0

コンパイラにあなたのために仕事をさせてください。第二の方法。 – RoadRunner

+1

gccが引数なしで '' xyzz ''に '' xyz ''に警告を出すのは興味深い/奇妙ですが、どちらも問題があります。あなたの最初の例は、 'char arr [4] =" xyz ";' – asimes

答えて

3

文字列リテラルを使用して文字配列を初期化するときは、文字列リテラルで初期化された文字列の境界をコンパイラが自動的に指定しないでください。

C標準(C11 - 6.7.8:段落14)は言う:文字型の

アレイは、必要に応じて括弧で囲まれ、 リテラルまたはUTF-8文字列リテラル文字列で初期化することができます。 文字列リテラルの連続するバイト(空きがある場合、または配列のサイズが不明な場合は終了するNULL 文字を含む) 配列の要素を初期化します。この例では

char arr[3] = "xyz"; 

arrのサイズは3であるが、文字列リテラルのサイズは4あります。文字列は、配列が保持できる文字数を1文字以上( '\ 0'で終了)に定義します。

char arr[] = "xyz"; 

この例では、配列初期化の文字配列の境界を指定しません。配列の境界が省略されている場合、コンパイラはヌル文字を含む文字列リテラル全体を格納するのに十分なサイズを割り当てます。

+0

この回答は2つのアプローチで何が起こるかをよく説明していますが、さまざまなケースで賛否両論について議論しません。 – chux

2

2番目の文字は、3文字以上の別のstringを初期化する場合は自動的に処理されるため、2番目の文字を使用します。

サンプルコード

int main() 
{ 
    int i; 
    char arr[] = "xyz Hello World"; 
    for(i=0;i<sizeof(arr)-1;i++){ 
     printf("%c",arr[i]); 
    } 
    printf("\n"); 
    return 0; 
} 

あなたは以上の3 char文字列を格納したいときに、それはコンパイル時にwarningが表示されます第一1を使用する場合

警告

warning: initializer-string for array of chars is too long [enabled by default]                      
    char arr[3] = "xyz Hello World"; 

2番目のハットを使用する必要がありますstringを使用してcharacterの配列を初期化する方が良い方法です。

+0

ありがとうございました。 –

+0

@ M.Mこれでヌルターミネータは表示されません。 –

0

も使用することを検討してください

const char* arr = "xyz"; 

それは(それはあなたが誤って、配列を変更しない可能「のconst」キーワードを除く)と同じことだが、データが上にコピーされませんスタックでは、実行可能ファイルのデータセグメントで静的コピーを使用します。特に大きな弦の場合は、これが重要です。

+0

ここで、 'arr'はポインタであり、初期化された配列ではありません。 – chux

+0

ポインタと配列は構文的に同等です。 配列内のデータを変更することに興味がない場合は、スタック上に配列を作成し、その配列にデータをコピーするよりも上の方法が優れています。 –

7

特殊な状況でない限り、配列のサイズを明示的に入力しないことによって常に第2の方法を優先します。こうすることで、あなたの目には思わぬように作成されたバグが回避されます。

これを理解するには、まず文字列が正確に何であるかを理解する必要があります。ヌル文字は'\0'と表示されます。文字列は、ゼロ以上の一連の0ではないcharで終わります.1つのヌル文字で終了します。この最後のビットは非常に重要です。次のコードを見てください:

const char* my_string = "xyz"; 
size_t string_len = strlen(my_string); // string_Len == 3 

ポインタは単なるメモリアドレスです。サイズや長さの情報自体は保持しません。次に、strlen()はどのようにしてmy_stringの長さを測定できますか?これはもちろん、文字列の先頭から終端のヌル文字の直前までの非ヌル文字の量を測定することによって行われます。文字列リテラルで暗黙の終了文字が暗黙のであることに注目したかもしれません。リテラル上記の文字列は次のようになりますメモリ内の配列を作成します。

_______ _______ _______ _______ 
|  |  |  |  | 
| 'x' | 'y' | 'z' | '\0' | 
|_______|_______|_______|_______| 
    ^
    | 
`my_string` is a pointer to this cell 

配列自体は名前になりますが、コンパイラはmy_stringの値として、その最初の要素のアドレスを与えることを管理しています。では、最初の例はどうなりますか?

char my_string[ 3 ] = "abc"; 
標準の定義(null文字カウント、文字列リテラルは、歴史的な理由のために constを宣言されていない注意することが Nは、文字列の長さである char[ N ]、プラス1を入力した文字列リテラルことで

、それはまだ定義されていませんそれらを変更する動作)。したがって、上記式​​は、タイプchar[ 4 ]である。一方、my_string(ポインタではなく配列のBTW)は、タイプがchar[ 3 ]です。つまり、より小さな配列をより大きな配列に設定しています(4 > 3以降)。この正確な状況では、文字列リテラルのヌル文字が配列に収まらない場合には、それを切り捨てなければならないことが標準で規定されています。したがって、my_stringはメモリ内で次のようになります。

_______ _______ _______ 
|  |  |  | 
| 'a' | 'b' | 'c' | 
|_______|_______|_______| 

お待ちください...待ちます。終端ヌル文字はどこですか?明示的に配列のサイズを宣言することで、それを切り捨てました!今、strlen()はどのように文字列の長さを決定するのですか?偶然の問題でヌル文字が見つかるまで、文字列を越えて文字を読み続けるだけです。これは未定義の動作です。一方、これを行うことによって:

const char[] my_string = "abc"; 

これを行う危険はありません。 my_stringのタイプは自動的にconst char[ 4 ]に推論され、ヌル文字は保持されます。

tl; dr終了ヌル文字を忘れないでください!

関連する問題