web-dev-qa-db-ja.com

mallocによる文字列の割り当て

私はCプログラミングの初心者で、今は文字列を勉強しています。私の質問は:(下のコードのように)mallocを使用して文字列を割り当てる場合、NULL文字は文字列の最後に自動的に挿入されますか?ここで別の質問で答えを見つけましたが、NULL文字が自動的に含まれていないようです。しかし、ここで問題が発生します。NULL文字がないと、strlenのような関数が機能しないことがわかっています。このコードでは、NULL文字を使用して機能します。だから\0どこにも書いていない場合でも、文字列の最後に。答えは何ですか?

これがコードです:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char** argv) {
    char *stringa1;
    int n;
    int i;

    printf("How many characters in the string? ");
    scanf("%d", &n);

    stringa1 = (char*) malloc(n*sizeof(char));

    printf("Insert the string: ");
    scanf("%s", stringa1);

    free(stringa1);

    return 0;
}
6
FranzGoogle

malloc()は、ヒープに格納されているメモリブロックへの_void*_ポインタを返します。 malloc()で割り当てると、文字列は初期化されず、占有されるのを待機しているスペースのみが初期化されます。ヌル終了文字を追加するには、これを自分で行うか、scanf()のような関数を使用する必要があります、この文字を追加します。そうは言っても、この_\0_文字用のスペースを事前に割り当てる必要があります。

malloc()呼び出しは、代わりに次のようにする必要があります。

_stringa1 = (char*) malloc((n+1)*sizeof(char)); /*+1 for '\0' character */
_

注:mallocのreturnをキャストする必要はありません。詳細については、 this を参照してください。

もう1つ指摘すべきことは、sizeof(char)は_1_であるため、malloc()呼び出しでこれを乗算する必要はありません。

malloc()NULLを返すかどうかも確認する必要があります。これは次のように行うことができます:

_if (stringa1 == NULL) {
    /* handle exit */
_

また、strlen()はnullで終了する文字列でのみ使用できます。それ以外の場合、これは ndefined behaviour になります。

scanf()が呼び出され、_stringa1_にいくつかの文字が含まれると、その上でstrlen()を呼び出すことができます。

さらに、scanf()の戻り値を確認することもお勧めします。次のように確認できます。

_if (scanf("%d", &n) != 1) {
    /* handle exit */
_

これらの変更を加えたコード:

_#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    char *stringa1 = NULL;
    size_t n, slen;

    printf("How many characters in the string? ");
    if (scanf("%zu", &n) != 1) {
        printf("Invalid input\n");
        exit(EXIT_FAILURE);
    }

    stringa1 = malloc(n+1);
    if (stringa1 == NULL) {
        printf("Cannot allocate %zu bytes for string\n", n+1);
        exit(EXIT_FAILURE);
    }

    printf("Insert the string: ");
    scanf("%s", stringa1);

    slen = strlen(stringa1);
    printf("String: %s Length: %zu\n", stringa1, slen);

    free(stringa1);
    stringa1 = NULL;

    return 0;
}
_
12
RoadRunner

(以下のコードのように)mallocを使用して文字列を割り当てると、文字列の最後にNULL文字が自動的に挿入されますか?

いいえ。malloc()は、初期化されていないメモリのブロックを返します。

NULL文字がない場合、「strlen」などの関数は機能しないことを知っています。このコードでは、これを使用して機能します。したがって、どこにも書いていない場合でも、文字列の最後に「\ 0」があると思います。

scanf()は、_'\0'_形式指定子を使用すると、ヌルバイト(_%s_)を挿入します(scanf()が成功した場合)。

man scanf() から:

s空白以外の文字のシーケンスと一致します。次のポインターは、入力シーケンスとを保持するのに十分な長さの文字配列の最初の要素へのポインターと、自動的に追加される終端のnullバイト( '\ 0')でなければなりません。 入力文字列は、空白またはフィールドの最大幅のいずれか早い方で停止します。

(私の強調)。

ちなみに、scanf()およびmalloc()呼び出しのエラーチェックを行う必要があります。

8
P.P

Cでの「文字列」の定義は一連の文字、null文字で終了です。

文字列にメモリを割り当てるには、文字(strlenなど)をカウントし、この終端のnull文字に1を追加します。

scanfstrcpyなどの関数はnull文字を追加します。 strncpyのような関数は常にそうするわけではありません。

1
Paul Ogilvie

mallocは、初期化されていないメモリエクステントへのポインタを返します。

メモリエクステントをゼロで初期化する場合は、callocの代わりに別の標準関数mallocを使用できます。

通常このような質問を考慮に入れてください

printf("How many characters in the string? ");

終了ゼロはカウントされないことを意味します。したがって、1バイトのメモリを割り当てる必要があります。例えば

stringa1 = ( char* )malloc( ( n + 1 ) *sizeof( char ) );

または

stringa1 = ( char* )calloc( n + 1, sizeof( char ) );

最後のケースでは、メモリエクステントがゼロで初期化されるため、0を返す関数strlenを適用できます。

このscanfの呼び出し

scanf("%s", stringa1);

安全ではありません。代わりにfgetsを使用することをお勧めします。例えば

fgets( stringa1, n + 1, stdin );

この関数は、文字列に改行文字を追加できます。あなたが書くことができる文字列からそれを削除するには

stringa1[strcspn( stringa1, "\n" )] = '\0';
1