web-dev-qa-db-ja.com

動的にサイズ設定された構造体の配列を作成するにはどうすればよいですか?

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

例えば:

_    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を印刷しようとすると、必要なエラーが表示されます。だから、少なくとも私のコンパイラでは、フリー関数が機能していないのですか? DevCを使用していますか?


おかげで、私は今のために理解しています:

FirstNameは、mallocによって割り当てられていないchar配列へのポインタです。ポインタのみが割り当てられており、freeを呼び出した後、メモリを消去せず、ヒープ上で使用可能としてマークします後で書かれた。 – MattSmith

更新

私はモジュール化して構造体の作成を関数に入れようとしていますが、何も機能していないようです。私は非常に単純なことを試みていますが、他に何をすべきかわかりません。以前と同じ行に沿って、データをロードする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;
    }//
_

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

50
D. Rattansingh

これをC++およびCとタグ付けしました。

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

#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;
}
35
Tom

配列を動的に割り当てる場合は、stdlib.hmalloc を使用できます。

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

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

割り当てるメモリのサイズはmallocに渡され、voidvoid*)型のポインターを返します。ほとんどの場合、おそらくあなたが望むポインター型にキャストしたいと思うでしょう。この場合はwords*です。

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

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

free(array);

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

12
coobird

これはアカデミックなエクササイズのように見えますが、残念ながら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に割り当てているだけであり、文字列自体を割り当てる必要があり、最後に文字列を解放する必要があることに注意してください。したがって、このコードはcharに100個のポインターを割り当ててから、76にサイズ変更しますが、文字列自体は割り当てません。

上記と非常によく似た文字列の文字数を実際に割り当てたいが、Wordを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);
5
Ryan

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

3
Neil Williams

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

質問のタイトルにはCと書かれていますが、質問にCとC++のタグを付けました...

2
Jeremy Ruten

ここに私がC++でそれを行う方法があります

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

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

2
Adrian

最後の更新でのコードはコンパイルされるべきではなく、実行されるはずです。 LoadDataに&xを渡します。 &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を追加します。

1
Arkadiy

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

#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;
}
1
zhanwu

配列を動的に拡張する場合は、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);
0
ob1

すべてのコーダーは、初心者でも簡単に理解できるようにコードを単純化する必要があります。

したがって、概念を理解していれば、動的に使用する構造の配列は簡単です。

// Dynamically sized array of structures

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

struct book 
{
    char name[20];
    int p;
};              //Declaring book structure

int main () 
{
    int n, i;      

    struct book *b;     // Initializing pointer to a structure
    scanf ("%d\n", &n);

    b = (struct book *) calloc (n, sizeof (struct book));   //Creating memory for array of structures dynamically

    for (i = 0; i < n; i++)
    {
        scanf ("%s %d\n", (b + i)->name, &(b + i)->p);  //Getting values for array of structures (no error check)
    }          

    for (i = 0; i < n; i++)
    {
        printf ("%s %d\t", (b + i)->name, (b + i)->p);  //Printing values in array of structures
    }

    scanf ("%d\n", &n);     //Get array size to re-allocate    
    b = (struct book *) realloc (b, n * sizeof (struct book));  //change the size of an array using realloc function
    printf ("\n");

    for (i = 0; i < n; i++)
    {
        printf ("%s %d\t", (b + i)->name, (b + i)->p);  //Printing values in array of structures
    }

    return 0;
}   
0
Arul Girish