Dynamische geheugentoewijzing gebruiken voor arrays

Hoe moet ik dynamische geheugentoewijzingen voor arrays gebruiken?

Hier is bijvoorbeeld de volgende array waarin ik afzonderlijke woorden uit een .txt-bestand lees en ze woord voor woord in de array opsla:

Code:

char words[1000][15];

Hier definieert 1000 het aantal woorden dat de array kan opslaan en elk woord mag niet meer dan 15 tekens bevatten.

Nu wil ik dat dat programma het geheugen dynamisch toewijst voor het aantal woorden dat het telt. Een .txt-bestand kan bijvoorbeeld woorden groter dan 1000 bevatten. Nu wil ik dat het programma het aantal woorden telt en het geheugen dienovereenkomstig toewijst.

Omdat we geen variabele kunnen gebruiken in plaats van [1000], weet ik niet hoe ik mijn logica moet implementeren. Help me alsjeblieft in dit opzicht.


Antwoord 1, autoriteit 100%

Je gebruikt aanwijzers.

In het bijzonder, je gebruikt een aanwijzer naar een adres, en met behulp van een standaard c-bibliotheekfunctie-aanroep, vraag je het besturingssysteem om de heap uit te breiden zodat je kunt opslaan wat je nodig hebt.

Nu kan het weigeren, wat je moet doen.

De volgende vraag wordt: hoe vraag je om een 2D-array? Welnu, u vraagt om een reeks aanwijzers en breidt vervolgens elke aanwijzer uit.

Beschouw dit als voorbeeld:

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

Hierdoor krijg je een tweedimensionale array, waarbij elk element words[i]een andere grootte kan hebben, te bepalen tijdens runtime, net als het aantal woorden.

Je moet free()al het resulterende geheugen door over de array heen te lopen als je klaar bent:

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

Als je dat niet doet, creëer je een geheugenlek.

U kunt ook callocgebruiken. Het verschil zit ‘m in de aanroepconventie en het effect – callocinitialiseert al het geheugen op 0terwijl mallocdat niet doet.

Als u het formaat tijdens runtime moet wijzigen, gebruikt u realloc.


Belangrijk, kijk uit voor de word_size+1die ik heb gebruikt. Strings in C zijn zero-terminated en dit kost een extra karakter waar je rekening mee moet houden. Om ervoor te zorgen dat ik dit onthoud, stel ik meestal de grootte van de variabele word_sizein op de grootte van het woord (de lengte van de string zoals ik verwacht) en laat ik expliciet de +1 in de malloc voor de nul. Dan weet ik dat de toegewezen buffer een reeks word_sizetekens kan bevatten. Dit niet doen is ook prima – ik doe het gewoon omdat ik graag op een voor de hand liggende manier expliciet rekening houd met de nul.

Er is ook een nadeel van deze aanpak – ik heb dit onlangs uitdrukkelijk gezien als een verzonden bug. Mededeling Ik schreef (word_size+1)*sizeof(type)– Stel je echter voor dat ik had geschreven word_size*sizeof(type)+1. Voor sizeof(type)=1Dit zijn hetzelfde, maar Windows gebruikt wchar_tzeer vaak – en in dit geval reserveer je een byte voor je laatste nul in plaats van twee – en ze zijn onder nul-beëindigde elementen van het type type, geen enkele nul bytes. Dit betekent dat u overschrijdt op lezen en schrijven. & nbsp;

Addendum: Doe het op welke manier je maar wilt, pas op voor die nul-terminators als je de buffer gaat doorgeven aan iets dat erop vertrouwt.


Antwoord 2, Autoriteit 29%

Terwijl NetFingers een antwoord met behulp van een reeks aanwijzingen , u kunt ook een reeks arrays gebruiken zolang de grootte van de innerlijke array een constante uitdrukking is. De code hiervoor is eenvoudiger.

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

Antwoord 3, Autoriteit 4%

Als u van plan bent om te gaan voor C++, is STL erg handig voor iets dynamische toewijzing en is het heel gemakkelijk. Je kunt Std :: Vector ..


Antwoord 4, Autoriteit 4%

Als de 15in uw voorbeeld variabel is, gebruikt u een van de beschikbare antwoorden (van negende neven- of john boker of Muggen).
Als de 1000variabel is, gebruikt u 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;}
}

In mijn bovenstaande code is de constante 2000een vereenvoudiging; je moet nog een variabele capacitytoevoegen om meer dan 2000 woorden te ondersteunen:

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
}

Antwoord 5, autoriteit 4%

Als je in C werkt:

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

Antwoord 6

Hier is wat informatie over het dynamisch toewijzen van 2D-arrays:

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


Antwoord 7

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

Antwoord 8

In moderne C (C99) heb je een extra keuze, arrays met variabele lengte, VLA, zoals:

char myWord[N];

In principe zou je zoiets ook in twee dimensies kunnen doen, maar als je maten te groot worden, riskeer je een stapeloverloop. In jouw geval zou het gemakkelijkst zijn om een aanwijzer naar zo’n array te gebruiken en malloc/ reallocte gebruiken om de grootte ervan te wijzigen:

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

Deze code zou moeten werken (modulo typefouten) als wordleneen constante of een variabele is, zolang je alles binnen één functie houdt. Als je het in een functie wilt plaatsen, moet je je functie zoiets declareren als

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

Dat zijn de lengteparameters moeten eerst komen om bekend te staan voor de verklaring van words.

Other episodes