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.
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.
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);
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
}
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, ¤tSize, 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;
}
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.
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
.
Voici quelques informations sur l'allocation dynamique de tableaux 2D:
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);