web-dev-qa-db-ja.com

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

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

_char *foo( int rc ) 
{
    switch (rc) 
    {
      case 1:           return("one");
      case 2:           return("two");
      default:           return("whatever");
    }
}
_

したがって、C/C++のローカル変数の有効期間は、実際には関数内のみです。 char* foo(int)が終了した後、それが返すポインターはもはや何の意味も持たないということですか?

私はローカル変数の寿命について少し混乱しています。誰も私に良い説明を与えることができますか?

79
user113454

はい、ローカル変数の有効期間はスコープ内です({})作成元。
ローカル変数には、自動ストレージまたはローカルストレージがあります。
Automaticは、作成されたスコープが終了すると自動的に破棄されるためです。

ただし、ここにあるのは文字列リテラルで、読み取り専用メモリで定義された実装に割り当てられています。文字列リテラルはローカル変数とは異なり、プログラムの有効期間中は有効なままです。static duration [参照1] 一生。

注意事項!
ただし、文字列リテラルの内容を変更しようとする試みは、未定義の動作です。ユーザープログラムでは、文字列リテラルの内容を変更できません。
したがって、文字列リテラルを宣言する際には、constを使用することが常に推奨されます。

const char*p = "string"; 

の代わりに、

char*p = "string";    

実際、C++ではなく、constなしで文字列リテラルを宣言することはC++では推奨されていません。ただし、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型を持ち、ワイド文字のシーケンスで初期化されます...

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

81
Alok Save

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

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

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

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

8通常の文字列リテラルとUTF-8文字列リテラルは、ナロー文字列リテラルとも呼ばれます。狭い文字列リテラルの型は「n const charの配列」で、nは以下で定義される文字列のサイズであり、静的ストレージ期間(3.7)を持ちます。

9 u"asdf"など、uで始まる文字列リテラルは、char16_t文字列リテラルです。 char16_t文字列リテラルの型は「array of n const char16_t」です。nは、以下で定義する文字列のサイズです。静的な保存期間があり、指定された文字で初期化されます。単一のc-charは、サロゲートペアの形式で複数のchar16_t文字を生成する場合があります。

10 U"asdf"などのUで始まる文字列リテラルは、char32_t文字列リテラルです。 char32_t文字列リテラルの型は「array of n const char32_t」です。nは、以下で定義する文字列のサイズです。静的な保存期間があり、指定された文字で初期化されます。

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

75
Daniel Fischer

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

また、文字列リテラルは読み取り専用なので、(良いスタイルのために)fooconst char *foo(int)に変更する必要があるかもしれません

14
asaelr

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

  • const char*から文字列リテラルへ。変更できません。呼び出し元によって解放されてはなりません。以下で説明する解放の問題のため、デフォルト値を返す目的にはほとんど役立ちません。実際にどこかに関数ポインタを渡す必要がある場合は、理にかなっているかもしれません。そのため、文字列を返す関数が必要です。

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

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

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

1つの問題は、これらを1つの関数に混ぜると複雑になる場合があることです。呼び出し元は、返されたポインターをどのように処理するか、どのくらい有効であるか、呼び出し元がそれを解放する必要があるかどうかを知る必要があります。したがって、たとえば、呼び出し元がfreeに必要なヒープに割り当てられたバッファへのポインタを返す関数や、呼び出し元がする必要がある文字列リテラルからのデフォルト値へのポインタを返すことはできません- notfree

7
hyde

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

これがCの便利な機能であるということですね。これにより、メッセージが保存されているメモリをプログラマに心配させることなく、関数が事前に構成されたメッセージを返すことができます。

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

6
thb

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

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

何をしているのかが未定義で無効になるのではないかと心配している場合は、コンパイラの警告を表示して、実際に間違っていることを確認してください。

3
AusCBloke

strはダングリングポインターになることはありません。 Because it points to static address文字列リテラルが存在する場所。それがロードされるとき、それは主にreadonlyglobalです。解放または変更しようとしても、segmentation faultメモリ保護のあるプラットフォーム

2
qwr

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

0
Imp

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

ありがとう

ビハリP L V.

0
VIHARRI PLV