web-dev-qa-db-fra.com

Utilisation de l'allocation de mémoire dynamique pour les baies

Comment suis-je censé utiliser les allocations de mémoire dynamique pour les tableaux?

Par exemple, voici le tableau suivant dans lequel je lis des mots individuels à partir d'un fichier .txt et les enregistre Word par Word dans le tableau:

Code:

char words[1000][15];

Ici, 1000 définit le nombre de mots que le tableau peut enregistrer et chaque mot peut comprendre au plus 15 caractères.

Maintenant, je veux que ce programme alloue dynamiquement la mémoire pour le nombre de mots qu'il compte. Par exemple, un fichier .txt peut contenir des mots supérieurs à 1000. Maintenant, je veux que le programme compte le nombre de mots et alloue la mémoire en conséquence.

Puisque nous ne pouvons pas utiliser une variable à la place de [1000], je ne sais pas comment implémenter ma logique. Veuillez m'aider à cet égard.

12
Rafay

Vous utilisez des pointeurs.

Plus précisément, vous utilisez un pointeur vers une adresse, et en utilisant un appel de fonction de bibliothèque c standard, vous demandez au système d'exploitation d'étendre le tas pour vous permettre de stocker ce dont vous avez besoin.

Maintenant, il peut refuser, ce que vous devrez gérer.

La question suivante devient - comment demandez-vous un tableau 2D? Eh bien, vous demandez un tableau de pointeurs, puis développez chaque pointeur.

À titre d'exemple, considérez ceci:

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 */
}

Cela vous donne un tableau à deux dimensions, où chaque élément words[i] Peut avoir une taille différente, déterminable au moment de l'exécution, tout comme le nombre de mots.

Vous aurez besoin de free() toute la mémoire résultante en faisant une boucle sur le tableau lorsque vous en aurez terminé:

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

free(words);

Si vous ne le faites pas, vous créerez une fuite de mémoire.

Vous pouvez également utiliser calloc. La différence réside dans la convention d'appel et dans l'effet - calloc initialise toute la mémoire à 0 Alors que malloc ne le fait pas.

Si vous devez redimensionner lors de l'exécution, utilisez realloc.


Aussi, important, faites attention au Word_size + 1 que j'ai utilisé. Les chaînes en C sont terminées par zéro et cela prend un caractère supplémentaire dont vous devez tenir compte. Pour m'assurer de me souvenir de cela, je règle généralement la taille de la variable Word_size Sur quelle que soit la taille du mot (la longueur de la chaîne comme je m'y attendais) et laisse explicitement le +1 dans le malloc pour le zéro. Alors je sais que le tampon alloué peut prendre une chaîne de caractères Word_size. Ne pas faire cela est également très bien - je le fais simplement parce que j'aime expliquer explicitement le zéro de manière évidente.

Il y a aussi un inconvénient à cette approche - J'ai vu explicitement cela comme un bug livré récemment. Remarquez que j'ai écrit (Word_size+1)*sizeof(type) - imaginez cependant que j'avais écrit Word_size*sizeof(type)+1. Pour sizeof(type)=1, c'est la même chose mais Windows utilise wchar_t Très fréquemment - et dans ce cas, vous réserverez un octet pour votre dernier zéro plutôt que deux - et ce sont des éléments terminés par zéro de type type, pas de zéro octet unique. Cela signifie que vous serez dépassé en lecture et en écriture.

Addendum: faites-le comme vous le souhaitez, faites juste attention à ces zéro terminateurs si vous allez passer le tampon à quelque chose qui en dépend.

23
user257111

Alors que Ninefingers a fourni ne réponse utilisant un tableau de pointeurs , vous pouvez également utiliser un tableau de tableaux tant que la taille du tableau interne est une expression constante. Le code pour cela est plus simple.

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

Si la 15 dans votre exemple est variable, utilisez l'une des réponses disponibles (de Ninefingers ou John Boker ou Muggen). Si la 1000 est variable, utilisez 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;}
}

Dans mon code ci-dessus, la constante 2000 est une simplification; vous devez ajouter une autre variable capacity pour prendre en charge plus de 2000 mots:

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

Si vous travaillez en 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

Si vous avez l'intention d'aller pour C++, STL est très utile pour quelque chose d'allocation dynamique et est très facile. Vous pouvez utiliser std :: vector.

1
Mahesh

Dans le C moderne (C99), vous avez un choix supplémentaire, des tableaux de longueur variable, VLA, comme celui-ci:

char myWord[N];

En principe, vous pouvez également faire une telle chose en deux dimensions, mais si vos tailles deviennent trop grandes, vous risquez un débordement de pile. Dans votre cas, la chose la plus simple serait d'utiliser un pointeur vers un tel tableau et d'utiliser malloc/realloc pour les redimensionner:

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);

Ce code devrait fonctionner (fautes de frappe modulo) si wordlen est une constante ou une variable, tant que vous gardez tout dans une fonction. Si vous voulez le placer dans une fonction, vous devez déclarer votre fonction quelque chose comme

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

c'est-à-dire que les paramètres de longueur doivent venir en premier pour être connus pour la déclaration de words.

0
Jens Gustedt

Voici quelques informations sur l'allocation dynamique de tableaux 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