Cでは、次のような定数文字列へのポインターを作成できます
char *s = "abc";
しかし、文字列は基本的に null で終了する文字配列であるため、これが許可されないのはなぜでしょうか?
char *s = {'a', 'b', 'c' , '\0' };
それは基本的に、言語がそれを許可していないためです。
文字列リテラルは type の式char[N+1]
で、N
はリテラルの長さです。これは、静的ストレージ期間を持つ無名配列オブジェクトを参照します。つまり、オブジェクトはプログラムの実行全体にわたって存在します。
(C++ とは異なり、 ではありません const char[N+1]
が、変更しようとすると未定義の動作になることに注意してください。)
また、配列型の式と同様に、ほとんどのコンテキストで配列の最初の要素へのポインターに暗黙的に変換されます。
したがって、この宣言では:
char *s = "abc";
式"abc"
は、初期化に使用される配列の最初の文字へのポインターに暗黙的に変換されますs
。しかし、次のように書く方がはるかに安全です。
const char *s = "abc";
誤って配列を変更しようとしないようにします。
では、なぜこれが機能しないのでしょうか。
char *s = { 'a', 'b', 'c', '\0' };
{ 'a', 'b', 'c', '\0' }
は式ではないからです。これは初期化子であり、配列オブジェクトの初期化に使用できますが、ポインター オブジェクトの初期化には使用できません。代わりに次のように書くと:
char arr[] = { 'a', 'b', 'c', '\0' };
次に、初期化子を使用して配列オブジェクトを初期化します。配列オブジェクトは作成されません。バージョンが機能するchar *s
には、ポインターが指すオブジェクトを作成する必要がありますs
。中括弧で囲まれた初期化子リストは、それを行いません。
C99 では、新しい機能である複合リテラルが追加されました。これは、いくつかの点で文字列リテラルに似ていますが、より一般的です。たとえば、次のようになります。
(char[]){ 'a', 'b', 'c', '\0' }
は型の式です。char[4]
繰り返しますが、配列型の式と同様に、ほとんどのコンテキストで暗黙的にポインターに変換されます。したがって、この:
char *arr = (char[]){ 'a', 'b', 'c', '\0' };
有効でありarr
、複合リテラルによって作成された無名配列オブジェクトの最初の要素を示します。
重要な違いが 1 つあります。その配列オブジェクトの有効期間は必ずしも静的ではありません。複合リテラルが関数の外にある場合、配列オブジェクトはプログラムの実行全体にわたって存在します。それ以外の場合は、自動保存期間を持つローカル オブジェクトのように動作します。
C99 をサポートしていないコンパイラ (*cough*Microsoft*cough*) を使用している場合は、いつでも最初に配列オブジェクトを宣言できます。
const char arr[] = { 'a', 'b', 'c', '\0' };
const char *s = arr;
comp.lang.c FAQのセクション 6 も参照してください。ここでは、配列とポインターについて説明しています (また、配列とポインターに関するいくつかのよくある誤解を解決する優れた仕事をしています)。
これは、ポインターが配列ではないためです。(最初の要素のアドレスを指すポインターに配列を減衰させることはできますが、ポインターは配列にはなりません。)
あなたはこれを行うことができます:
char s[] = {'a', 'b', 'c', '\0'};
// ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
// array valid initializer for char[]
しかし、これではありません:
char* s = {'a', 'b', 'c', '\0'};
// ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
// pointer invalid initializer for char*
これをコーディングできます:
char s[] = { 'a', 'b', 'c', '\0` };
これはと同じです
char s[] = "abc";
ただし、文字列リテラルは実際には読み取り専用です (テキスト セグメントであるため)。したがって、文字列リテラルを として理解する必要がありますconst char s[]
(C 標準を正確に気にしている場合は、正確でなくても)。特に、コーディング"abc"[1] = 'X';
は未定義の動作です(Linux ではおそらくセグメンテーション違反でクラッシュします)。
最近のコンパイラ (Clang/LLVM 3.2 や GCC 4.8 など) では、すべての警告を有効にすると、おそらく警告が表示されます。gcc -Wall
もちろん、ポインターと配列は C では同じではありません (配列はポインターに崩壊する可能性があります) 。この回答が説明しています。