web-dev-qa-db-ja.com

配列の動的メモリ割り当ての使用

配列に動的メモリ割り当てを使用するにはどうすればよいですか?

たとえば、次の配列は、.txtファイルから個々の単語を読み取り、配列にWordごとに保存します。

コード:

char words[1000][15];

ここで、1000は配列が保存できるワード数を定義し、各ワードは15文字以下で構成できます。

ここで、そのプログラムがカウントする単語数に応じてメモリを動的に割り当てる必要があります。たとえば、.txtファイルに1000を超える単語が含まれている場合があります。次に、プログラムで単語数をカウントし、それに応じてメモリを割り当てる必要があります。

[1000]の代わりに変数を使用することはできないので、ロジックの実装方法は完全に空白です。この点で私を助けてください。

12
Rafay

ポインタを使用します。

具体的には、アドレスへのポインタを使用し、標準のCライブラリ関数呼び出しを使用して、オペレーティングシステムにヒープを拡張し、必要なものを格納できるようにします。

今、それはあなたが処理する必要があるでしょう拒否するかもしれません。

次の質問になります-2D配列をどのように求めますか?さて、あなたはポインタの配列を求め、そして各ポインタを展開します。

例として、これを考慮してください:

_int i = 0;
char** words;
words = malloc((num_words)*sizeof(char*));

if ( words == NULL )
{
    /* we have a problem */
    printf("Error: out of memory.\n");
    return;
}

for ( i=0; i<num_words; i++ )
{
    words[i] = malloc((Word_size+1)*sizeof(char));
    if ( words[i] == NULL )
    {
        /* problem */
        break;
    }
}

if ( i != num_words )
{
    /* it didn't allocate */
}
_

これにより、2次元配列が得られます。各要素_words[i]_は、ワード数と同じように、実行時に決定可能な異なるサイズを持つことができます。

配列を使い終わったら、配列をループして、結果として得られるすべてのメモリをfree()する必要があります。

_for ( i = 0; i < num_words; i++ )
{
    free(words[i]);
}

free(words);
_

そうしないと、メモリリークが発生します。

callocを使用することもできます。違いは、呼び出しの規則と効果にあります。callocはすべてのメモリを_0_に初期化しますが、mallocは初期化しません。

実行時にサイズを変更する必要がある場合は、reallocを使用します。


また、重要なのは私が使用したWord_size + 1に注意することです。 Cの文字列は0で終了し、これはあなたが説明する必要がある余分な文字をとります。これを確実に覚えるために、通常、変数_Word_size_のサイズをWordのサイズ(期待どおりの文字列の長さ)に設定し、ゼロのmallocに+1を明示的に残します。次に、割り当てられたバッファが_Word_size_文字の文字列をとることができることを知っています。これを行わないことも問題ありません。私は明白にゼロを明示的に説明するのが好きだからです。

このアプローチにはマイナス面もあります-最近、これが出荷されたバグであることがはっきりとわかりました。 _(Word_size+1)*sizeof(type)_を作成したことに注意してください。ただし、Word_size*sizeof(type)+1を作成したことを想像してください。 sizeof(type)=1の場合、これらは同じものですが、Windowsは_wchar_t_を非常に頻繁に使用します。この場合、最後のゼロ用に2バイトではなく1バイトを予約します。タイプtype、単一のゼロバイトではありません。これは、読み取りと書き込みでオーバーランすることを意味します。

補遺:好きなようにしてください。それらに依存するものにバッファを渡す場合は、これらのゼロターミネータに注意してください。

23
user257111

Ninefingersは ポインタの配列を使用した回答 を提供しましたが、内部配列のサイズが定数式である限り、配列の配列を使用することもできます。このためのコードはより単純です。

char (*words)[15]; // 'words' is pointer to char[15]
words = malloc (num_words * sizeof(char[15]);

// to access character i of Word w
words[w][i];

free(words);
7
Heatsink

15は、例では変数です。利用可能な回答の1つを使用します(Ninefingers、John Boker、Muggenのいずれか)。 1000は変数です。reallocを使用してください:

words = malloc(1000 * sizeof(char*));
// ... read 1000 words
if (++num_words > 1000)
{
    char** more_words = realloc(words, 2000 * sizeof(char*));
    if (more_words) {printf("Too bad");}
    else {words = more_words;}
}

上記の私のコードでは、定数2000は簡略化です。 2000ワード以上をサポートするには、別の変数capacityを追加する必要があります。

if (++num_words > capacity)
{
    // ... realloc
    ++capacity; // will reallocate 1000+ words each time; will be very slow
    // capacity += 1000; // less reallocations, some memory wasted
    // capacity *= 2; // less reallocations but more memory wasted
}
1
anatolyg

Cで作業している場合:

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

#define Word_LEN 15

int resizeArray(char (**wordList)[Word_LEN], size_t *currentSize, size_t extent)
{
  int result = 1;
  char (*tmp)[Word_LEN] = realloc(*wordList, 
                                 (*currentSize + extent) * sizeof **wordList);
  if (tmp)
  {
    *currentSize += extent;
    *wordList = tmp;
  }
  else
    result = 0;

  return result;
}

int main(void)
{
  char *data[] = {"This", "is", "a", "test", 
                  "of", "the", "Emergency", 
                  "Broadcast", "System", NULL};
  size_t i = 0, j;
  char (*words)[Word_LEN] = NULL;
  size_t currentSize = 0;

  for (i = 0; data[i] != NULL; i++)
  {
    if (currentSize <= i)
    {
      if (!resizeArray(&words, &currentSize, 5))
      {
        fprintf(stderr, "Could not resize words\n");
        break;
      }
    }
    strcpy(words[i], data[i]);
  }

  printf("current array size: %lu\n", (unsigned long) currentSize);
  printf("copied %lu words\n", (unsigned long) i);

  for (j = 0; j < i; j++)
  {
    printf("wordlist[%lu] = \"%s\"\n", (unsigned long) j, words[j]);
  }

  free(words);

  return 0;
}
1
John Bode

C++を使用するつもりなら、STLは動的割り当てに非常に役立ち、非常に簡単です。 std :: vectorを使用できます。

1
Mahesh

最近のC(C99)では、次のような追加の選択肢、可変長配列、VLAがあります。

char myWord[N];

原則として、このようなことを2次元で行うこともできますが、サイズが大きくなりすぎると、スタックオーバーフローのリスクがあります。あなたの場合、最も簡単なことは、そのような配列へのポインタを使用し、malloc/reallocを使用してサイズを変更することです。

typedef char Word[wordlen];
size_t m = 100000;

Word* words = malloc(m * sizeof(Word));
/* initialize words[0]... words[m-1] here */
for (size_t i = 0; i < m; ++i) words[i][0] = '\0';

/* array is too small? */
m *= 2;
void *p = realloc(words, m*sizeof(Word));
if (p) words = p;
else {
 /* error handling */
}
.
free(words);

このコードは、wordlenが定数または変数の場合、1つの関数内にすべてを保持している限り、(タイプミスで)機能します。関数に配置したい場合は、関数を次のように宣言する必要があります

void myWordFunc(size_t wordlen, size_t m, char words[m][wordlen]);

つまり、長さパラメータは、wordsの宣言で認識されるために最初に来る必要があります。

0
Jens Gustedt

2D配列を動的に割り当てる方法について、以下に少し説明します。

http://www.eskimo.com/~scs/cclass/int/sx9b.html

0
John Boker
char ** words = malloc( 1000 * sizeof(char *));
int i;
for( i = 0 ; i < 1000 ; i++)
     *(words+i) = malloc(sizeof(char) * 15);

//....
for( i = 0 ; i < 1000 ; i++)
     free(*(words+i));

free(words);
0
user418748