Cでの文字列リテラルの「ライフタイム」


84

次の関数によって返されるポインタにアクセスできませんか?

char *foo(int rc)
{
    switch (rc)
    {
        case 1:

            return("one");

        case 2:

            return("two");

        default:

            return("whatever");
    }
}

つまり、C / C ++のローカル変数の存続期間は、実際には関数内だけですよね?つまり、char* foo(int)終了した後、それが返すポインタはもはや何も意味しませんよね?

ローカル変数の存続期間について少し混乱しています。良い説明は何ですか?


10
関数にある唯一の「var」はパラメーターint rcです。その存続期間は、return-sのそれぞれで終了します。返すポインタは文字列リテラルです。文字列リテラルには静的な保存期間があります。その有効期間は、少なくともプログラムの有効期間と同じです。
kaz 2012

14
@PedroAlvesどうしてですか?メソッドは抽象化を可能にします。将来、文字列が翻訳リソースから読み取られるが、製品のV1(またはV0.5)に国際化サポートが必要ない場合はどうなりますか?
dlev 2013年

1
@PedroAlves 「あなたのコードは確かに機能します(そしてコンパイルしようとするとそれを見ることができます)」それは続きません。多くの(ほとんど?本質的にすべて?)cコンパイラは不正なコードを消費し、動作しているように見えるコードを出力することがよくあります。ただし、別のコンパイラ(または同じコンパイラの別のバージョン)で試してみると、失敗する可能性があります。
dmckee ---元モデレーターの子猫2013

6
@PedroAlves、単一の定数文字列を返す関数は使用が制限される可能性がありますが、入力またはオブジェクトの状態に応じて、いくつかの定数文字列のいずれかを返す関数はどうでしょうか。簡単な例は、列挙型を文字列表現に変換する関数です。
マークランサム2013年

4
strerror明らかに、あなたはその機能を見たことがありません。
カズ2013年

回答:


86

はい、ローカル変数の有効期間は、それが作成されたスコープ({})内にあります。

ローカル変数には、自動ストレージまたはローカルストレージがあります。自動それらは自動的にそれらが端を作成されている範囲内で、スコープ一度破壊されているため。

ただし、ここにあるのは文字列リテラルであり、実装で定義された読み取り専用メモリに割り当てられます。文字列リテラルはローカル変数とは異なり、プログラムの存続期間を通じて存続します。それらは静的な持続時間 [参照1]の寿命を持っています

注意の言葉!

ただし、文字列リテラルの内容を変更しようとすると、未定義の動作(UB)になることに注意してください。ユーザープログラムは、文字列リテラルの内容を変更することはできません。
したがって、const文字列リテラルを宣言するときは常にwhileを使用することをお勧めします。

const char*p = "string"; 

の代わりに、

char*p = "string";    

実際、C ++では、Cではなく文字列リテラルを宣言せずに文字列リテラルを宣言することは非推奨ですconst。ただし、で文字列リテラルを宣言すると、constコンパイラが文字列リテラルを変更しようとした場合に通常警告が表示されるという利点があります。 2番目のケース。

サンプルプログラム

#include<string.h> 
int main() 
{ 
    char *str1 = "string Literal"; 
    const char *str2 = "string Literal"; 
    char source[]="Sample string"; 
 
    strcpy(str1,source);    // No warning or error just Uundefined Behavior 
    strcpy(str2,source);    // Compiler issues a warning 
 
    return 0; 
} 

出力:

cc1:エラーとして扱われる警告
prog.c:関数 'main'内:
prog.c:9:エラー: 'strcpy'の引数1を渡すと、ポインターターゲットタイプから修飾子が破棄されます

コンパイラが2番目のケースについて警告しますが、最初のケースについては警告しないことに注意してください。


ここで数人のユーザーが尋ねている質問に答えるには:

整数リテラルとの関係は何ですか?

言い換えれば、次のコードは有効ですか?

int *foo()
{
    return &(2);
} 

答えは、このコードは無効ではないということです。これは形式が正しくなく、コンパイラエラーが発生します。

何かのようなもの:

prog.c:3: error: lvalue required as unary ‘&’ operand
     

文字列リテラルはl値です。つまり、文字列リテラルのアドレスを取得できますが、その内容を変更することはできません。
しかしながら、任意の他のリテラル(intfloatchar、など)はR値である(C標準は、用語を使用する式の値これらのために)およびそれらのアドレスは全く取ることができません。


[参照1] C99標準6.4.5 / 5「文字列リテラル-セマンティクス」:

変換フェーズ7では、値がゼロのバイトまたはコードが、1つまたは複数の文字列リテラルから生じる各マルチバイト文字シーケンスに追加されます。次に、マルチバイト文字シーケンスを使用して、シーケンスを含めるのに十分な静的ストレージ期間と長さの配列を初期化します。文字列リテラルの場合、配列要素の型はcharであり、マルチバイト文字シーケンスの個々のバイトで初期化されます。ワイド文字列リテラルの場合、配列要素の型はwchar_tであり、ワイド文字のシーケンスで初期化されます。

それらの要素が適切な値を持っている場合、これらの配列が異なるかどうかは指定されていません。プログラムがそのような配列を変更しようとした場合、動作は未定義です。


ユーザーがこのようなものを返している場合はどうなりますか。char * a =& "abc"; を返す; これは無効ですか?
アシュウィン2012

@Ashwin:文字列リテラルのタイプはchar (*)[4]。です。これは、「abc」のタイプがでchar[4]あり、4文字の配列へのポインタがとして宣言されchar (*)[4]ているためです。したがって、アドレスを取得する必要がある場合は、として実行する必要char (*a)[4] = &"abc";があります。はい、有効です。
Alok Save 2012

@Als「abc」はchar[4]です。(ので'\0'
asaelr 2012

1
おそらく、文字をリテラルにchar const s[] = "text";ないことを警告するのも良い考えsです。したがって、スコープの最後で破棄s れるため、それへの残りのポインターはぶら下がります。
celtschk 2012

1
@celtschk:やりたいのですが、Qは特に文字列リテラルに関するものなので、目前のトピックに固執します。ただし、ここで私の答えに興味がある人は、char a [] =“ string”とcharの違いは何ですか。 * p =“ string”?かなり役立つはずです。
Alok Save 2012

74

有効です。文字列リテラルには静的な保存期間があるため、ポインタがぶら下がっていません。

Cの場合、これはセクション6.4.5のパラグラフ6で義務付けられています。

変換フェーズ7では、値がゼロのバイトまたはコードが、1つまたは複数の文字列リテラルから生じる各マルチバイト文字シーケンスに追加されます。次に、マルチバイト文字シーケンスを使用して、シーケンスを含めるのに十分な静的ストレージ期間と長さの配列を初期化します。

また、セクション2.14.5のパラグラフ8〜11のC ++の場合:

8通常の文字列リテラルおよびUTF-8文字列リテラルは、ナロー文字列リテラルとも呼ばれます。狭い文字列リテラルのタイプは「arrayofn const char」です。ここで、nは以下に定義する文字列のサイズであり、静的ストレージ期間(3.7)があります。

9、などのuで始まる文字列リテラルu"asdf"は、char16_t文字列リテラルです。Achar16_t文字列リテラルは、タイプ「Nのアレイを有するconst char16_t」、ここでnは以下に定義する文字列のサイズです。静的な保存期間があり、指定された文字で初期化されます。単一のc-charはchar16_t、代理ペアの形式で複数の文字を生成する場合があります。

10、などのUで始まる文字列リテラルU"asdf"は、char32_t文字列リテラルです。Achar32_t文字列リテラルは、タイプ「Nのアレイを有するconst char32_t」、ここでnは以下に定義する文字列のサイズです。静的な保存期間があり、指定された文字で初期化されます。

11、などのLで始まる文字列リテラルL"asdf"は、幅の広い文字列リテラルです。ワイド文字列リテラルの型は「arrayofn const wchar_t」です。ここで、nは以下に定義する文字列のサイズです。静的な保存期間があり、指定された文字で初期化されます。


参考

14

文字列リテラルはプログラム全体で有効であるため(スタックではなく割り当てられません)、有効になります。

また、文字列リテラルは読み取り専用なので、(スタイルを良くするために)次のように変更fooする必要があります。const char *foo(int)


ユーザーがこのようなものを返している場合はどうなりますか。char * a =& "abc"; を返す; これは無効ですか?
アシュウィン2012

&"abc"ではありませんchar*。これは配列のアドレスであり、そのタイプはchar(*)[4]です。ただし、return &"abc";char *a="abc";return a;は有効です。
asaelr 2012

@asaelr:実際、それは良いスタイルためだけのものではありません。詳細については私の答えを確認してください。
Alok Save 2012

@Alsええと、彼がプログラム全体を書いた場合、彼は書かずに文字列を変更することを避けることができconst、それは完全に合法ですが、それでも悪いスタイルです。
asaelr 2012

プログラム全体で有効な場合、なぜそれをmallocする必要があるのでしょうか。
トムソーヤー

7

はい、それは有効なコードです。以下のケース1を参照してください。少なくとも次の方法で、関数からC文字列を安全に返すことができます。

  • const char*文字列リテラルに。変更することはできず、発信者が解放してはなりません。以下で説明する解放の問題があるため、デフォルト値を返す目的で役立つことはめったにありません。実際に関数ポインタをどこかに渡す必要がある場合は意味があるかもしれないので、文字列を返す関数が必要です。

  • char*またはconst char*静的charバッファに。発信者が解放してはなりません。これは(constでない場合は呼び出し元によって、またはそれを返す関数によって)変更できますが、これを返す関数は(簡単に)複数のバッファーを持つことができないため、(簡単に)スレッドセーフではなく、呼び出し元は必要になる場合があります関数を再度呼び出す前に戻り値をコピーします。

  • char*で割り当てられたバッファにmalloc。変更できますが、通常は呼び出し元が明示的に解放する必要があり、ヒープ割り当てのオーバーヘッドがあります。strdupこのタイプです。

  • const char*またはchar*、関数への引数として渡されたバッファー(返されたポインターは、引数バッファーの最初の要素を指す必要はありません)。バッファ/メモリ管理の責任は呼び出し元に任されています。多くの標準文字列関数はこのタイプです。

1つの問題は、これらを1つの関数に混在させると複雑になる可能性があることです。呼び出し元は、返されたポインターを処理する方法、有効な期間、および呼び出し元がポインターを解放する必要があるかどうかを知る必要があり、実行時にそれを決定する(良い)方法はありません。したがって、たとえば、呼び出し元が必要とするヒープ割り当てバッファへのポインタを返す関数や、呼び出し元がしてfreeはならない 文字列リテラルからのデフォルト値へのポインタを返す関数を使用することはできませfree


参考

6

良い質問。一般的に、あなたは正しいでしょうが、あなたの例は例外です。コンパイラは、文字列リテラルにグローバルメモリを静的に割り当てます。したがって、関数によって返されるアドレスは有効です。

これはCのかなり便利な機能ですよね。これにより、関数は、メッセージが格納されているメモリについてプログラマーに心配させることなく、合成済みメッセージを返すことができます。

@asaelrの正しい観察reも参照してくださいconst


:ユーザーがこのようなものを返している場合はどうなりますか。char * a =& "abc"; を返す; これは無効ですか?
アシュウィン2012

正しい。実際にはconst char *a = "abc";、を省略して書くことができ&ます。その理由は、二重引用符で囲まれた文字列が最初の文字のアドレスに解決されるためです。
バーツ2012

3

ローカル変数は、宣言されているスコープ内でのみ有効ですが、その関数でローカル変数を宣言することはありません。

文字列リテラルは、staticまたはグローバル変数と同様に、プログラムの実行全体を通じて存在するため、関数から文字列リテラルへのポインタを返すことは完全に有効です。

自分がしていることが未定義で無効である可能性があることを心配している場合は、コンパイラの警告を表示して、実際に何か間違っていることがあるかどうかを確認する必要があります。


ユーザーがこのようなものを返している場合はどうなりますか。char * a =& "abc"; を返す; これは無効ですか?
アシュウィン2012

@Ashwin:&"abc"タイプではないですchar*が、両方、"abc"および&"abc"プログラムの全体の実行を通じて有効です。
AusCBloke 2012

2

str文字列リテラルが存在する静的アドレスを指すため、ダングリングポインタになることはありません。

ロードされるとき、それはほとんど読み取り専用で、プログラムに対してグローバルです。

解放または変更しようとしても、メモリ保護のあるプラットフォームでセグメンテーション違反が発生 します


参考

ぶら下がることがない場合は、mallocする必要がありますか?番号?
トムソーヤー

0

ローカル変数はスタックに割り当てられます。関数が終了すると、変数はスコープ外になり、コードからアクセスできなくなります。ただし、その変数を指すように割り当てたグローバル(または単に-まだスコープ外ではない)ポインターがある場合、その変数があったスタック内の場所を指します。別の関数で使用されている値、または意味のない値である可能性があります。


ユーザーがこのようなものを返している場合はどうなりますか。char * a =& "abc"; を返す; これは無効ですか?
アシュウィン2012

0

あなたが示した上記の例では、実際には、上記を呼び出す関数に割り当てられたポインタを返しています。したがって、ローカルポインタにはなりません。さらに、返される必要のあるポインタについては、メモリがグローバルセグメントに割り当てられます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.