59

構造体の配列を作成する方法を知っていますが、サイズは事前定義されています。しかし、配列が大きくなるような構造体の動的配列を作成する方法はありますか?

例えば:

    typedef struct
    {
        char *str;
    } words;

    main()
    {
        words x[100]; // I do not want to use this, I want to dynamic increase the size of the array as data comes in.
    }

これは可能ですか?


私はこれを研究しました:words* array = (words*)malloc(sizeof(words) * 100);

100 を取り除き、入ってくるデータを保存したいです。したがって、76 フィールドのデータが入ってくる場合、100 ではなく 76 を保存したいと思います。どのくらいのデータが入ってくるかわからないと仮定しています。私のプログラムに。上で定義した構造体では、最初の「インデックス」を次のように作成できます。

    words* array = (words*)malloc(sizeof(words));

ただし、後で配列に要素を動的に追加したいと考えています。問題の領域を十分に明確に説明したことを願っています。主な課題は、2 番目のフィールドを動的に追加することです。少なくとも、それが当面の課題です。


しかし、私は少し進歩しました:

    typedef struct {
        char *str;
    } words;

    // Allocate first string.
    words x = (words) malloc(sizeof(words));
    x[0].str = "john";

    // Allocate second string.
    x=(words*) realloc(x, sizeof(words));
    x[1].FirstName = "bob";

    // printf second string.
    printf("%s", x[1].str); --> This is working, it's printing out bob.

    free(x); // Free up memory.

    printf("%s", x[1].str); --> Not working since its still printing out BOB even though I freed up memory. What is wrong?

私はいくつかのエラーチェックを行い、これが私が見つけたものです。x のメモリを解放した後、次を追加します。

    x=NULL;

次に、 x を印刷しようとすると、必要なエラーが発生します。少なくとも私のコンパイラでは、 free 関数が機能していないということですか? 私はDevCを使用していますか??


ありがとう、私は今理解しました:

FirstName は、malloc によって割り当てられていない char の配列へのポインターです。ポインターのみが割り当てられており、free を呼び出した後、メモリは消去されず、ヒープで利用可能であるとマークされます。後で書きます。– マット・スミス

アップデート

モジュール化して、構造体の配列の作成を関数に入れようとしていますが、何も機能していないようです。私は非常に単純なことをしようとしていますが、他に何をすべきかわかりません。これは前と同じ行に沿っており、データをロードしている別の関数 loaddata と、印刷を行う必要があるメソッドの外にあります。どうすればそれを機能させることができますか?私のコードは次のとおりです。

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

    typedef struct
    {
        char *str1;
        char *str2;
    } words;

    void LoadData(words *, int *);

    main()
    {
        words *x;
        int num;

        LoadData(&x, &num);

        printf("%s %s", x[0].str1, x[0].str2);
        printf("%s %s", x[1].str1, x[1].str2);

        getch();
    }//

    void LoadData(words *x, int * num)
    {
        x = (words*) malloc(sizeof(words));

        x[0].str1 = "johnnie\0";
        x[0].str2 = "krapson\0";

        x = (words*) realloc(x, sizeof(words)*2);
        x[1].str1 = "bob\0";
        x[1].str2 = "marley\0";

        *num=*num+1;
    }//

この単純なテスト コードがクラッシュしており、その理由がわかりません。バグはどこですか?

4

10 に答える 10

39

これをCと同様にC++としてタグ付けしました。

C ++を使用している場合は、はるかに簡単です。標準テンプレートライブラリには、オブジェクトのリストを動的に作成できるvectorというテンプレートがあります。

#include <stdio.h>
#include <vector>

typedef std::vector<char*> words;

int main(int argc, char** argv) {

        words myWords;

        myWords.push_back("Hello");
        myWords.push_back("World");

        words::iterator iter;
        for (iter = myWords.begin(); iter != myWords.end(); ++iter) {
                printf("%s ", *iter);
        }

        return 0;
}

Cを使用している場合は、非常に困難です。そうです、malloc、realloc、freeが役立つツールです。代わりに、リンクリストデータ構造の使用を検討することをお勧めします。これらは一般的に成長が容易ですが、ランダムアクセスを容易にすることはできません。

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

typedef struct s_words {
        char* str;
        struct s_words* next;
} words;

words* create_words(char* word) {
        words* newWords = malloc(sizeof(words));
        if (NULL != newWords){
                newWords->str = word;
                newWords->next = NULL;
        }
        return newWords;
}

void delete_words(words* oldWords) {
        if (NULL != oldWords->next) {
                delete_words(oldWords->next);
        }
        free(oldWords);
}

words* add_word(words* wordList, char* word) {
        words* newWords = create_words(word);
        if (NULL != newWords) {
                newWords->next = wordList;
        }
        return newWords;
}

int main(int argc, char** argv) {

        words* myWords = create_words("Hello");
        myWords = add_word(myWords, "World");

        words* iter;
        for (iter = myWords; NULL != iter; iter = iter->next) {
                printf("%s ", iter->str);
        }
        delete_words(myWords);
        return 0;
}

うん、世界最長の答えでごめんなさい。したがって、「リンクリストコメントを使用したくない」へのWRT:

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

typedef struct {
    char** words;
    size_t nWords;
    size_t size;
    size_t block_size;
} word_list;

word_list* create_word_list(size_t block_size) {
    word_list* pWordList = malloc(sizeof(word_list));
    if (NULL != pWordList) {
        pWordList->nWords = 0;
        pWordList->size = block_size;
        pWordList->block_size = block_size;
        pWordList->words = malloc(sizeof(char*)*block_size);
        if (NULL == pWordList->words) {
            free(pWordList);
            return NULL;    
        }
    }
    return pWordList;
}

void delete_word_list(word_list* pWordList) {
    free(pWordList->words);
    free(pWordList);
}

int add_word_to_word_list(word_list* pWordList, char* word) {
    size_t nWords = pWordList->nWords;
    if (nWords >= pWordList->size) {
        size_t newSize = pWordList->size + pWordList->block_size;
        void* newWords = realloc(pWordList->words, sizeof(char*)*newSize); 
        if (NULL == newWords) {
            return 0;
        } else {    
            pWordList->size = newSize;
            pWordList->words = (char**)newWords;
        }

    }

    pWordList->words[nWords] = word;
    ++pWordList->nWords;


    return 1;
}

char** word_list_start(word_list* pWordList) {
        return pWordList->words;
}

char** word_list_end(word_list* pWordList) {
        return &pWordList->words[pWordList->nWords];
}

int main(int argc, char** argv) {

        word_list* myWords = create_word_list(2);
        add_word_to_word_list(myWords, "Hello");
        add_word_to_word_list(myWords, "World");
        add_word_to_word_list(myWords, "Goodbye");

        char** iter;
        for (iter = word_list_start(myWords); iter != word_list_end(myWords); ++iter) {
                printf("%s ", *iter);
        }

        delete_word_list(myWords);

        return 0;
}
于 2008-11-04T05:26:00.580 に答える
13

配列を動的に割り当てたい場合は、mallocfromを使用できますstdlib.h

構造体を使用して 100 個の要素の配列を割り当てたい場合はwords、次のことを試してください。

words* array = (words*)malloc(sizeof(words) * 100);

割り当てたいメモリのサイズが渡され、 ( )malloc型のポインタが返されます。ほとんどの場合、目的のポインター型 (この場合は.voidvoid*words*

ここsizeofでキーワードを使用してwords構造体のサイズを調べ、そのサイズに割り当てたい要素の数を掛けます。

完了したら、メモリ リークfree()を防ぐために、使用したヒープ メモリを解放するために必ず を使用してください。

free(array);

割り当てられた配列のサイズを変更したい場合は、realloc他の人が述べたように使用することができますが、多くreallocの s を実行すると、メモリが断片化する可能性があることに注意してください。reallocプログラムのメモリ フットプリントを低く抑えるために配列のサイズを動的に変更する場合は、あまり多くの sを実行しない方がよい場合があります。

于 2008-11-04T04:55:22.140 に答える
6

これは、C ++を使用できないため、残念ながら困難になる学術的な演習のように見えます。基本的に、割り当てのオーバーヘッドの一部を管理し、後でサイズを変更する必要がある場合は、割り当てられたメモリの量を追跡する必要があります。これは、C++標準ライブラリが優れているところです。

たとえば、次のコードはメモリを割り当て、後でサイズを変更します。

// initial size
int count = 100;
words *testWords = (words*) malloc(count * sizeof(words));
// resize the array
count = 76;
testWords = (words*) realloc(testWords, count* sizeof(words));

あなたの例では、charへのポインタを割り当てているだけであり、文字列自体を割り当てる必要があり、さらに重要なことに、最後に文字列を解放する必要があることに注意してください。したがって、このコードは100個のポインターをcharに割り当ててから、76にサイズ変更しますが、文字列自体は割り当てません。

上記と非常によく似た文字列の文字数を実際に割り当てたいのではないかと疑っていますが、単語をcharに変更してください。

編集:また、コードをどこにでもコピーしないように、一般的なタスクを実行し、一貫性を強制する関数を作成することは非常に理にかなっていることを覚えておいてください。たとえば、a)構造体を割り当て、b)構造体に値を割り当て、c)構造体を解放することができます。だからあなたは持っているかもしれません:

// Allocate a words struct
words* CreateWords(int size);
// Assign a value
void AssignWord(word* dest, char* str);
// Clear a words structs (and possibly internal storage)
void FreeWords(words* w);

編集:構造体のサイズを変更する限り、char配列のサイズを変更するのと同じです。ただし、違いは、構造体配列を大きくする場合は、新しい配列項目をNULLに初期化する必要があることです。同様に、構造体配列を小さくする場合は、アイテムを削除する前にクリーンアップする必要があります。つまり、構造体配列のサイズを変更する前に、割り当てられた(および割り当てられたアイテムのみの)空きアイテムです。これが、これを管理するのに役立つヘルパー関数を作成することを提案した主な理由です。

// Resize words (must know original and new size if shrinking
// if you need to free internal storage first)
void ResizeWords(words* w, size_t oldsize, size_t newsize);
于 2008-11-04T05:53:20.073 に答える
3

別のオプションは、リンクされたリストです。プログラムがデータ構造をどのように使用するかを分析する必要があります。ランダムアクセスが必要ない場合は、再割り当てよりも高速になる可能性があります。

于 2008-11-04T05:08:20.927 に答える
2

C++ では、vectorを使用します。配列に似ていますが、要素を簡単に追加および削除でき、メモリの割り当てと割り当て解除を処理します。

質問のタイトルが C であることは知っていますが、質問に C および C++ のタグを付けました...

于 2008-11-04T05:07:07.560 に答える
1

前回の更新のコードはコンパイルされるべきではなく、ましてや実行されるべきではありません。&x を LoadData に渡しています。&x の型は **words ですが、LoadData は words* を想定しています。もちろん、スタックを指しているポインターで realloc を呼び出すとクラッシュします。

それを修正する方法は、 LoadData を単語を受け入れるように変更することです ** 。このように、実際に main() でポインターを変更できます。たとえば、realloc 呼び出しは次のようになります。

*x = (words*) realloc(*x, sizeof(words)*2);

これは、「num」が int ではなく int* である場合と同じ原則です。

これに加えて、単語の文字列がどのように格納されているかを実際に把握する必要があります。const 文字列を char * に代入すること (str2 = "marley\0" のように) は許可されていますが、C であっても正しい解決策になることはめったにありません。

もう 1 つのポイント: 文字列の末尾に 2 つの 0 が本当に必要でない限り、"marley\0" は必要ありません。コンパイラは、すべての文字列リテラルの末尾に 0 を追加します。

于 2008-11-05T19:18:24.767 に答える
1

テスト コードの場合: 関数内のポインターを変更する場合は、関数に「ポインターへのポインター」を渡す必要があります。修正されたコードは次のとおりです。

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

typedef struct
{
    char *str1;
    char *str2;
} words;

void LoadData(words**, int*);

main()
{
    words **x;
    int num;

    LoadData(x, &num);

    printf("%s %s\n", (*x[0]).str1, (*x[0]).str2);
    printf("%s %s\n", (*x[1]).str1, (*x[1]).str2);
}

void LoadData(words **x, int *num)
{
    *x = (words*) malloc(sizeof(words));

    (*x[0]).str1 = "johnnie\0";
    (*x[0]).str2 = "krapson\0";

    *x = (words*) realloc(*x, sizeof(words) * 2);
    (*x[1]).str1 = "bob\0";
    (*x[1]).str2 = "marley\0";

    *num = *num + 1;
}
于 2011-11-04T14:45:59.600 に答える
0

配列を動的に拡張する場合は、malloc() を使用して一定量のメモリを動的に割り当て、不足するたびに realloc() を使用する必要があります。一般的な手法は、指数関数的成長関数を使用して、小さな固定量を割り当て、割り当てられた量を複製して配列を大きくすることです。

コード例は次のとおりです。

size = 64; i = 0;
x = malloc(sizeof(words)*size); /* enough space for 64 words */
while (read_words()) {
    if (++i > size) {
        size *= 2;
        x = realloc(sizeof(words) * size);
    }
}
/* done with x */
free(x);
于 2008-11-04T05:05:23.587 に答える
0

これがC++で行う方法です

size_t size = 500;
char* dynamicAllocatedString = new char[ size ];

すべての構造体または C++ クラスに同じプリンシパルを使用します。

于 2011-07-13T21:58:19.383 に答える