92

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

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

            return("one");

        case 2:

            return("two");

        default:

            return("whatever");
    }
}

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

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

4

9 に答える 9

92

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

ローカル変数には、自動ストレージまたはローカルストレージがあります。作成されたスコープが終了すると自動的に破棄されるため、自動

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

注意の言葉!

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

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 ‘&amp;’ operand
     

文字列リテラルはl値です。つまり、文字列リテラルのアドレスを取得できますが、その内容を変更することはできません。ただし、
他のリテラル(、、、intなど)はr値であり(C標準では、これらの式の値という用語を使用します)、それらのアドレスを取得することはできません。floatchar


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

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

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

于 2012-04-02T02:41:59.123 に答える
78

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

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文字列リテラルです。文字char16_t列リテラルの型は「arrayofn const char16_t」です。ここで、nは以下に定義する文字列のサイズです。静的な保存期間があり、指定された文字で初期化されます。char16_t1つのc-charは、代理ペアの形式で複数の文字を生成する場合があります。

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

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

于 2013-05-09T20:56:15.000 に答える
14

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

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

于 2012-04-02T02:42:27.050 に答える
7

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

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

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

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

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

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

于 2013-05-09T21:22:19.447 に答える
6

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

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

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

于 2012-04-02T02:42:07.673 に答える
3

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

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

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

于 2012-04-02T02:44:25.463 に答える
2

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

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

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

于 2013-05-10T06:02:52.040 に答える
0

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

于 2012-04-02T07:08:05.840 に答える
0

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

于 2012-04-02T02:44:43.340 に答える