1

sprintf を使用してポインターを文字列にコピーするときにダンプをスタックするコードがいくつかあります。動物の内容を出力という新しいポインタ配列にコピーしようとしています。ただし、スタック ダンプが表示されます。

出力にあるものは次のとおりです。 new animal rabbit new animal horse new animal donkey

私はこれを正しい方法で行っていますか?

どうもありがとう

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

void p_init(const char **animals, char **output);

int main(int argc, char **argv)
{
    char *animals[] = {"rabbit", "horse", "donkey", '\0'};  
    char **prt_animals = animals;
    char *output[sizeof(*animals)];

        /* print the contents here */
    while(*prt_animals)
    {
        printf("Animal: %s\n", *prt_animals++);
    }

        /* copy and update in the output buffer */
    p_init(*&animals, *&output);

    getchar();

    return 0;


void p_init(const char **animals, char **output)
{
    while(*animals)
    {
        sprintf(*output, "new animal %s", *animals); 
        *output++;
    }
}
4

5 に答える 5

7

配列animalsはポインターの配列です。あるサイズのバッファの配列ではありません。したがって、

sizeof(*animals)

その配列の最初の要素のサイズを取得します。に相当

sizeof(char*)

配列にはポインターが格納されるためです。だから、読む行で

char *output[sizeof(*animals)];

1 つの配列に 4 つまたは 8 つのポインターを割り当てます (プラットフォーム上のポインターの幅によって異なります。通常は 4 または 8 です)。しかし、それはもちろん無意味です!あなたがしたかったのは、 と同じサイズのポインターの配列を作成することですanimals。まず動物配列の合計サイズを取得してから、1 つの要素のサイズで割る必要があります。

char *output[sizeof(animals)/sizeof(*animals)];

今、それがあなたが望むものです。しかし、ポインターにはまだ不定値があります... 次に、を使用して配列を渡します*&animals(他の場合も同じです)。どうして?直接渡れanimalsます。そのアドレスを取得して逆参照することは、最初から何もしないことと同じです。

次に、呼び出す関数で、要素が指す文字列をanimal不確定な宛先にコピーします (配列の要素 (ポインター) にはまだ不確定な値があることを思い出してoutputください。まだ値を割り当てていません!)。最初に適切な量のメモリを割り当て、要素がそれを指すようにする必要があります。

while(*animals) {
        // now after this line, the pointer points to something sensible
        *output = malloc(sizeof("new animal ") + strlen(*animals));
        sprintf(*output, "new animal %s", *animals); 
        output++; // no need to dereference the result
        animals++; // don't forget to increment animals too!
}

その他、上記のサイズについて

確認しなければならない重要なことが 1 つあります。サイズの計算方法です。何をするにしても、常にストリングに十分なスペースがあることを確認してください! AC 文字列は、文字と、文字列の終わりを示す終端のヌル文字で構成されます。したがって、は、 および*outputのためのスペースが含まれるように、少なくとも同じ大きさのバッファーを指す必要が"new animal "あり*animalsます。最初の文字は 11 文字です。2 番目は、実際に何をコピーするかによって異なります。その長さがstrlen返されます。したがって、合計で必要です

12 + strlen(*animals)

終端の null を含むすべての文字のスペース。その番号をコードにハードコードするのは良いスタイルではありません。プレフィックスが変更される可能性があり、数を更新するのを忘れたり、1 つか 2 つの文字について数え間違えたりする可能性があります。sizeofこれが、先頭に追加したい文字列リテラルを提供するを使用する理由です。sizeof式はそのオペランドのサイズに評価されることを思い出してください。main前に配列の合計サイズを取得するために使用します。これを文字列リテラルに使用します。すべての文字列リテラルは文字の配列です。文字列リテラルは、ヌル文字に加えて入力した文字で構成されます。strlenは C 文字列の長さをカウントし、その長さに終端のヌル文字を含まないため、次の条件が成立します。

// "abc" would have the type char[4] (array of 4 characters)
sizeof "..." == strlen("...") + 1

いずれにしても sizeof char は 1 であるため、1 つの要素のサイズで割る必要はありません。違いはありません。なぜsizeofstrlen の代わりに使用するのですか? 終端の null 文字がすでに考慮されており、コンパイル時に評価されるためです。コンパイラは、sizeof 式が返すサイズを文字通り置き換えることができます。

于 2009-03-19T17:15:07.173 に答える
2
char *output[sizeof(*animals)];

へのポインタのサイズ 4 の配列を作成しますchar。ただし、これらのポインタにメモリを割り当てません。これらの配列メンバーにはガベージが含まれています (つまり、所有していないメモリを指しています)。そのメモリに書き込もうとすると、UB が呼び出されます。あなたの場合、UB はスタック ダンプによって現れます。関数のその他の問題をp_init以下に示します。

void p_init(const char **animals, char **output)
{
    /* runs an infinite loop -- since *animals is never incremented */
    /* need to pass size of *animals so that you can terminate your loop */
    while(*animals)
    {
        /* allocate some memory */
        sprintf(*output, "new animal %s", *animals); 
        *output++;
    }
}

修正されたコードは次のようになります。

void p_init(const char ** animals, const size_t nanimals, char **output)
{
    const char **w = animals;
    size_t len = 0;
    while (w < animals + nanimals)
    {
        len = strlen(*w);
        *output = malloc(len + sizeof "new animal " + 1);
        sprintf(*output, "new animal %s", *w); 
        output++;          
        w++;
    }
}

int main(int argc, char **argv)
{
    char *a[] = { "rat", "dog", "lion" };
    char *o[ sizeof a/ sizeof *a ];
    p_init((const char**)a, sizeof a / sizeof *a, o);
    for (size_t i = 0; i < sizeof a / sizeof *a; ++i) printf("%s\n", o[ i ]);
    for (size_t i = 0; i < sizeof a / sizeof *a; ++i) free(o[ i ]);
    return 0;
}

必要なヘッダーを自由に挿入してください。

于 2009-03-19T17:12:40.970 に答える
2

出力配列にコピーを配置するためのスペースを割り当てていません。sprintf を使用してそのバッファーにコピーする前に、malloc を使用してスペースを割り当てる必要があります。

void p_init(const char **animals, char **output)
{
    while(*animals)
    {
        size_t stringSize = 42; /* Use strlen etc to calculate the size you need, and don't for get space for the NULL! */
        *output = (char *)malloc(stringSize);
        sprintf(*output, "new animal %s", *animals); 
        output++;
        animals++;
    }
}

使い終わったら、割り当てられたメモリで free() を呼び出すことを忘れないでください。

于 2009-03-19T17:13:03.230 に答える
1

まず、ポイントは何ですか

p_init(*&animals, *&output);

とは対照的に

p_init(animals, *&output);

?

次に、ここで説明する理由により、char** を const char** に変換することは違法です。

最後に、あなたの主な問題は、テストが

while (*animals)

animal 配列の最後にある空の文字列に達したときに失敗すると予想していたものは間違っています。そのステートメントは、文字列を指すポインターが NULL であるかどうかを実際にチェックしており、文字列が空であるかどうかをチェックしていません。空の文字列 (単一の文字 '\0' を含む文字列) は、null ポインターと同じではありません。

つまり、animals 配列の最後の要素に到達すると、*animals は NULL 以外のポインターとして評価され、たまたま空の文字列を指します。したがって、テストはパスし、ループは永久に続きます (つまり、segfault が発生するように、animals 配列の末尾を十分に超えて実行するまで続きます)。

これを修正するには、animals 配列を作成するときに '\0' を NULL に置き換えるか、while チェックを変更して strlen(*animals) == 0 をチェックするようにします (または、ヌル ポインター。

編集:他の人が私が見逃した同様に深刻な問題を指摘したことに注意してください。

于 2009-03-19T17:12:27.290 に答える
1

sizeof(*animals)非常に大きなバッファを作成していませんsizeof(char*).32ビットシステムでは4バイトです。出力文字列を配置する場所を作成していません。バッファへの書き込みなどの安全なメカニズムを使用していないため、安全snprintfに失敗するのではなくクラッシュします。

ゼロで終わる配列の使用を維持しながら、これらの修正を以下に示します。

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

void p_init ( const char **animals, char **output );

int main(int argc, char **argv) {
    // no reason to use char 0 rather than int 0 to mark end here
    const char* animals[] = {"rabbit", "horse", "donkey", "pig", 0};

    printf("sizeof(*animals) = %zd\n", sizeof(*animals));
    printf("number of elements in animals = %zd\n", sizeof(animals) / sizeof(*animals));

    char *output[sizeof(animals)/sizeof(*animals)];

    // print animals
    for ( const char**p = animals; *p; ++p)
        printf ( "Animal: %s\n", *p );

    // format animals to output
    p_init ( animals, output);

    // print output
    for ( char**p = output; *p; ++p)
        printf ( "Animal: %s\n", *p );

    // free output
    for ( char**p = output; *p; ++p )
        free(*p);

    return 0;
}

void p_init ( const char **animals, char **output ) {
    while ( *animals ) {
        size_t  len = strlen ( *animals );
        char*   buf = malloc ( len + 13 );

        snprintf ( buf, len + 13, "new animal %s", *animals );

        *output = buf;

        ++animals;
        ++output;
    }

    *output = 0;
}
于 2009-03-19T17:14:38.657 に答える