web-dev-qa-db-ja.com

Cで文字列を初期化する適切な方法

私は人々のコードを次のように見ました:

char *str = NULL;

これも見たことがあります

char *str;

文字列を初期化する適切な方法は何ですか?そして、いつ文字列を初期化する必要があるのですか?

13
foobar01

使用する前に設定する必要があります。これが、定義されていない動作を回避するために従うべきhaveの唯一のルールです。作成時に初期化するか、使用する直前に割り当てるかは関係ありません。

個人的には、変数を自分で未知の値に設定しないようにしたいので、(数行以内に)近接して設定しない限り、通常は最初の変数を実行します。

実際、C99では、ブロックの上部でローカルを宣言する必要がなくなったので、必要になるまで通常は作成を延期します。その時点で、ローカルを初期化することもできます。

変数には、特定の状況下でデフォルト値が与えられることに注意してください(たとえば、変数がファイルレベルで宣言されているなど、関数の外で静的ストレージ期間である場合)。

ローカル変数にはこの保証はありません。したがって、上記の2番目の宣言(char *str;)は関数内にあり、ゴミを含んでいる可能性があり、それを使用しようとすると、前述の恐ろしい未定義の動作が呼び出されます。

C99標準の関連部分6.7.8/10

自動保存期間を持つオブジェクトが明示的に初期化されていない場合、その値は不確定です。静的ストレージ期間を持つオブジェクトが明示的に初期化されていない場合は、次のようになります。

  • ポインタ型の場合は、nullポインタに初期化されます。
  • 算術型の場合、(正または符号なし)ゼロに初期化されます。
  • 集合体の場合、すべてのメンバーはこれらのルールに従って(再帰的に)初期化されます。
  • 共用体の場合、最初の名前付きメンバーはこれらのルールに従って(再帰的に)初期化されます。
16
paxdiablo
文字列を初期化する適切な方法は何ですか?

さて、2番目のスニペットは文字列へのninitializedポインタを定義しているので、最初のものを言います。 :)

一般に、安全にプレイしたい場合は、すべてのポインタをNULLに初期化することをお勧めします。このように、NULLポインターを逆参照するとクラッシュが発生するため、初期化されていないポインターに由来する問題を簡単に見つけることができます(実際には、標準に関する限り、未定義の動作ですが、すべてのマシンでクラッシュだとわかった).

ただし、文字列へのNULLポインターを空の文字列と混同しないでください。文字列へのNULLポインターは、そのポインターが何も指していないことを意味しますが、空の文字列は「実際の」ゼロです-length文字列(つまり、NUL文字のみが含まれます)。

char * str=NULL; /* NULL pointer to string - there's no string, just a pointer */
const char * str2 = ""; /* Pointer to a constant empty string */

char str3[] = "random text to reach 15 characters ;)"; /* String allocated (presumably on the stack) that contains some text */
*str3 = 0; /* str3 is emptied by putting a NUL in first position */
6
Matteo Italia

これは、char ptrだけでなく、c変数に関する一般的な質問です。

宣言時に変数を初期化することをお勧めします。すなわち

char *str = NULL;

良いことです。これにより、未知の値を持つ変数が存在することはありません。たとえば、後でコードで行う場合

if(str != NULL)
 doBar(str);

何が起こるか。 strは不明な(そしてほぼ確実にNULLではない)状態です

静的変数はゼロ/ NULLに初期化されることに注意してください。あなたが地元の人や静物について尋ねている場合、それは質問から明確ではありません

4
pm100

グローバル変数はコンパイラーによってデフォルト値で初期化されますが、ローカル変数は初期化する必要があります。

2
Max
static const char str[] = "str";

または

static char str[] = "str";
1
Teme

Free()はNULL値を渡しても何もしないため、次のようにプログラムを簡略化できます。

char *str = NULL;

if ( somethingorother() )
{
    str = malloc ( 100 );

    if ( NULL == str )
        goto error;
}

...

error:

cleanup();
free ( str );

何らかの理由でsomethingorother()が0を返した場合、strを初期化していないと、ランダムなアドレスをどこかで解放して、失敗の原因となる可能性があります。

Gotoを使用したことをお詫びします。 :)

1
onemasse

単一化されたポインターは未定義と見なされるべきであり、未定義の値を使用してエラーを生成しないようにするには、常に使用する方が良い

char *str = NULL;

また、

char *str;

これは、割り当てを忘れた場合に使用するとほとんどの場合問題が発生する場所への未割り当てのポインタにすぎないため、任意に割り当てる(または別のポインタをコピーする)必要があります。

つまり、以下を選択できます。

  • 宣言後すぐに割り当てることを知っている場合は、次のように設定することを避けることができますNULL(これは一種のルールです)
  • それ以外の場合でも、確認したい場合は、それを実行してください。初期化せずに使用しようとすると、唯一の実際の問題が発生します。
1
Jack

「念のため」の宣言では、すべてのポインタ変数をNULLに初期化しないでください。

初期化されていないポインター変数を使用しようとすると、コンパイラーは警告します。ただし、アドレスで関数に渡す場合は例外です(そして、通常はためにに値を指定します)。 。

ポインタをNULLに初期化することは、それを賢明値に初期化することと同じではなく、NULLに初期化することは、コンパイラがしていない初期化したことを通知する機能を無効にするだけです実用的な値にしてください。

宣言時にNULLへのポインタを初期化しないのは、コンパイラの警告が表示されない場合に警告が表示される場合、またはポインタがNULLであると想定している関数にアドレスで渡す場合のみです。

ポインター変数の宣言と、最初に同じフルスクリーンで最初に値が与えられるポイントの両方が表示されない場合は、関数が大きすぎます。

1

それは、それをどのように使用するかによって完全に異なります。以下では、変数を初期化する方が理にかなっていますnot

int count;
while ((count = function()) > 0)
{
}
1
user207421

適切とは、バグがないことを意味しますか?まあ、それは状況に依存します。しかし、私がお勧めできるいくつかの経験則があります。

まず、Cの文字列は他の言語の文字列とは異なります。

それらは文字のブロックへのポインタです。その終わりは、0バイトまたはNULLターミネーターで終了します。したがって、nullで終了する文字列。

したがって、たとえば、次のようなことを行う場合:

char* str;  
gets(str);

または、何らかの方法でstrと対話する場合、それは重大なバグです。その理由は、今言ったように、Cの文字列は他の言語のような文字列ではないからです。それらは単なるポインタです。 char * strはポインタのサイズであり、常にそうです。

したがって、必要なのは、文字列を保持するためのメモリを割り当てることです。

/* this allocates 100 characters for a string 
   (including the null), remember to free it with free() */
char* str = (char*)malloc(100);
str[0] = 0;

/* so does this, automatically freed when it goes out of scope */
char str[100] = "";

ただし、ポインタが必要な場合もあります。
例えば。

/* This declares the string (not intialized) */
char* str;

/* use the string from earlier and assign the allocated/copied
   buffer to our variable */
str = strdup(other_string);

一般に、それは実際に文字列ポインターの使用方法に依存します。私の推奨は、その関数のスコープでのみ使用する予定で、文字列が比較的小さい場合は、固定サイズの配列形式を使用することです。またはNULLに初期化します。次に、明示的にNULL文字列をテストできます。これは、関数に渡されるときに役立ちます。

文字列の終わりがどこにあるかに関して単にNULLをチェックする関数を使用する場合、配列形式を使用することも問題になる可能性があることに注意してください。例えばstrcpyまたはstrcat関数は、バッファの大きさを気にしません。したがって、BSDのstrlcpyやstrlcatなどの代替手段の使用を検討してください。またはstrcpy_s&strcat_s(windows)。

多くの関数は、適切なアドレスを渡すことも期待しています。繰り返しますが、

char* str = NULL;
strcmp(str, "Hello World");

strcmpはNULLが渡されることを好まないため、大きな時間でクラッシュします。

これをCとしてタグ付けしましたが、C++を使用していてこの質問を読んでいる場合は、可能な場合はstd :: stringの使用に切り替え、必要なAPIとやり取りする必要がある文字列で.c_str()メンバー関数を使用します。標準のnullで終了するc文字列。

0
Matt

最初のスニペットは、初期化を伴う変数定義です。 2番目のスニペットは、初期化なしの変数定義です。

文字列を初期化する適切な方法は、定義時に初期化子を提供することです。それをNULLまたは他の何かに初期化することは、それを何にしたいかに依存します。

また、「文字列」と呼ばれるものにも注意してください。 Cにはそのようなタイプはありません。通常、Cコンテキストの「文字列」は、「[いくつかの]文字の配列」を意味します。上記のスニペットにcharへのポインタがあります。

Argv [1]にユーザー名を必要とし、それを文字列 "name"にコピーするプログラムがあるとします。 name変数を定義するときは、初期化しないままにするか、NULLに初期化する(charへのポインターの場合)か、デフォルト名で初期化できます。

int main(int argc, char **argv) {
    char name_uninit[100];
    char *name_ptr = NULL;
    char name_default[100] = "anonymous";

    if (argc > 1) {
        strcpy(name_uninit, argv[1]); /* beware buffer overflow */
        name_ptr = argv[1];
        strcpy(name_default, argv[1]); /* beware buffer overflow */
    }

    /* ... */

    /* name_uninit may be unusable (and untestable) if there were no command line parameters */
    /* name_ptr may be NULL, but you can test for NULL */
    /* name_default is a definite name */
}
0
pmg