webentwicklung-frage-antwort-db.com.de

Wie erstelle ich ein dynamisches Array in C?

Ich bin an PHP gewöhnt, aber ich fange an zu lernen. Ich versuche, ein Programm zu erstellen, das eine Datei Zeile für Zeile liest und jede Zeile in einem Array speichert.

Bisher habe ich ein Programm, das die Datei Zeile für Zeile liest und sogar jede Zeile druckt, aber jetzt muss ich nur noch eine Zeile zu einem Array hinzufügen.

Mein Kumpel gestern Abend hat mir etwas darüber erzählt. Er sagte, ich müsste ein multidimensionales Array in C verwenden, also im Grunde array[x][y]. Der [y]-Teil selbst ist einfach, da ich weiß, wie viele Bytes maximal jede Zeile enthalten wird. Ich weiß jedoch nicht, wie viele Zeilen die Datei sein wird.

Ich denke, ich kann es durch die Datei ziehen lassen und jedes Mal eine ganze Zahl inkrementieren und diese verwenden, aber ich glaube, dass es einen einfacheren Weg gibt, dies zu tun.

Irgendwelche Ideen oder sogar einen Hinweis in die richtige Richtung? Ich freue mich über jede Hilfe.

11
Rob

So weisen Sie ein 2D-Array dynamisch zu:

char **p;
int i, dim1, dim2;


/* Allocate the first dimension, which is actually a pointer to pointer to char   */
p = malloc (sizeof (char *) * dim1);

/* Then allocate each of the pointers allocated in previous step arrays of pointer to chars
 * within each of these arrays are chars
 */
for (i = 0; i < dim1; i++)
  {
    *(p + i) = malloc (sizeof (char) * dim2);
   /* or p[i] =  malloc (sizeof (char) * dim2); */
  }

 /* Do work */

/* Deallocate the allocated array. Start deallocation from the lowest level.
 * that is in the reverse order of which we did the allocation
 */
for (i = 0; i < dim1; i++)
{
  free (p[i]);
}
free (p);

Ändern Sie die obige Methode. Wenn Sie eine weitere Zeile hinzufügen möchten, müssen Sie *(p + i) = malloc (sizeof (char) * dim2); und i aktualisieren. In diesem Fall müssen Sie die maximale Anzahl von Zeilen in der Datei vorhersagen, die durch die Variable dim1 angegeben wird, für die das Array p zum ersten Mal zugewiesen wird. Dadurch werden nur die (sizeof (int *) * dim1)-Bytes zugewiesen. Dies ist eine viel bessere Option als char p[dim1][dim2] (in c99).

Es gibt einen anderen Weg, denke ich. Ordnen Sie Arrays in Blöcken zu und verketten Sie sie bei einem Überlauf.

struct _lines {
   char **line;
   int n;
   struct _lines *next;
} *file;

file = malloc (sizeof (struct _lines));
file->line = malloc (sizeof (char *) * LINE_MAX);
file->n = 0;
head = file;

Danach ist der erste Block einsatzbereit. Wenn Sie eine Zeile einfügen müssen, tun Sie einfach:

/* get line into buffer */
file.line[n] = malloc (sizeof (char) * (strlen (buffer) + 1));
n++;

Wenn nLINE_MAX ist, ordnen Sie einen anderen Block zu und verknüpfen Sie ihn mit diesem.

struct _lines *temp;

temp = malloc (sizeof (struct _lines));
temp->line = malloc (sizeof (char *) * LINE_MAX);
temp->n = 0;
file->next = temp;
file = file->next;

Etwas wie das.

Wenn die Variable n eines Blocks zu 0 wird, wird sie freigegeben und der aktuelle Blockzeiger file auf den vorherigen aktualisiert. Sie können entweder von Beginn einer einzelnen verknüpften Liste und von Anfang an navigieren oder Doppelverknüpfungen verwenden.

10
phoxis

Es gibt keinen Standard-Array-Typ mit variabler Größe in C. Sie müssen ihn selbst implementieren oder eine Bibliothek eines Drittanbieters verwenden. Hier ist ein einfaches Beispiel:

typedef struct int_array
{
    int *array;
    size_t length;
    size_t capacity;
} int_array;

void int_array_init(int_array *array)
{
    array->array = NULL;
    array->length = 0;
    array->capacity = 0;
}

void int_array_free(int_array *array)
{
    free(array->array);
    array->array = NULL;
    array->length = 0;
    array->capacity = 0;
}

void int_array_Push_back(int_array *array, int value)
{
    if(array->length == array->capacity)
    {
        // Not enough space, reallocate.  Also, watch out for overflow.
        int new_capacity = array->capacity * 2;
        if(new_capacity > array->capacity && new_capacity < SIZE_T_MAX / sizeof(int))
        {
            int *new_array = realloc(array->array, new_capacity * sizeof(int));
            if(new_array != NULL)
            {
               array->array = new_array;
               array->capacity = new_capacity;
            }
            else
                ; // Handle out-of-memory
        }
        else
            ; // Handle overflow error
    }

    // Now that we have space, add the value to the array
    array->array[array->length] = value;
    array->length++;
}

Verwenden Sie es so:

int_array a;
int_array_init(&a);

int i;
for(i = 0; i < 10; i++)
    int_array_Push_back(&a, i);
for(i = 0; i < a.length; i++)
    printf("a[%d] = %d\n", i, a.array[i]);

int_array_free(&a);

Dies gilt natürlich nur für ein Array von ints. Da C über keine Vorlagen verfügt, müssen Sie den gesamten Code entweder für jeden Array-Typ in ein Makro einfügen (oder einen anderen Präprozessor verwenden, z. B. GNU m4 ). Oder Sie könnten einen generischen Array-Container verwenden, der entweder void*-Zeiger (was alle Array-Elemente als malloc'ed ausgeben muss) oder undurchsichtige Speicher-Blobs verwendet, was bei jedem Elementzugriff eine Umwandlung und bei jedem Element-Get/Set eine memcpy erfordert.

In jedem Fall ist es nicht hübsch. Zweidimensionale Arrays sind noch hässlicher.

7
Adam Rosenfield

Sie können die Funktionen malloc und realloc verwenden, um ein Array von Zeigern dynamisch zu char zuzuordnen und deren Größe zu ändern. Jedes Element des Arrays zeigt auf einen String, der aus der Datei gelesen wird (wobei der Speicher dieses Strings ebenfalls dynamisch zugewiesen wird). Der Einfachheit halber gehen wir davon aus, dass die maximale Länge jeder Zeile weniger als M Zeichen beträgt (Zählen der Zeilenumbrüche), so dass wir keine dynamische Größenänderung einzelner Zeilen vornehmen müssen.

Sie müssen die Array-Größe bei jeder Erweiterung manuell nachverfolgen. Eine übliche Technik besteht darin, die Array-Größe bei jeder Erweiterung zu verdoppeln, anstatt sie um eine feste Größe zu erweitern. Dadurch wird die Anzahl der Aufrufe von realloc minimiert, was potenziell teuer ist. Das bedeutet natürlich, dass Sie zwei Mengen im Auge behalten müssen. die Gesamtgröße des Arrays und die Anzahl der aktuell gelesenen Elemente.

Beispiel:

#define INITIAL_SIZE ... // some size large enough to cover most cases

char **loadFile(FILE *stream, size_t *linesRead)
{
  size_t arraySize = 0;   
  char **lines = NULL;
  char *nextLine = NULL;

  *linesRead = 0;

  lines = malloc(INITIAL_SIZE * sizeof *lines);
  if (!lines)
  {
    fprintf(stderr, "Could not allocate array\n");
    return NULL;
  }

  arraySize = INITIAL_SIZE;

  /**
   * Read the next input line from the stream.  We're abstracting this
   * out to keep the code simple.
   */
  while ((nextLine = getNextLine(stream)))  
  {
    if (arraySize <= *linesRead)
    {
      char **tmp = realloc(lines, arraysSize * 2 * sizeof *tmp);
      if (tmp)
      {
        lines = tmp;
        arraySize *= 2;
      }
    }
    lines[(*linesRead)++] = nextLine;
  )

  return lines;
}
1
John Bode

Während ein mehrdimensionales Array dieses Problem lösen kann, wäre ein rechteckiges 2D-Array nicht wirklich die natürliche C-Lösung.

Hier ist ein Programm, das die Datei zunächst in eine verknüpfte Liste einliest und dann einen Vektor von Zeigern der richtigen Größe zuordnet. Jedes einzelne Zeichen erscheint dann als array[line][col], aber tatsächlich ist jede Zeile nur so lang, wie es sein muss. Es ist C99 außer <err.h>.

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

typedef struct strnode {
  char *s;
  struct strnode *next;
} strnode;

strnode *list_head;
strnode *list_last;

strnode *read1line(void) {
  char space[1024];
  if(fgets(space, sizeof space, stdin) == NULL)
    return NULL;
  strnode *node = malloc(sizeof(strnode));
  if(node && (node->s = malloc(strlen(space) + 1))) {
    strcpy(node->s, space);
    node->next = NULL;
    if (list_head == NULL)
      list_head = node;
    else
      list_last->next = node;
    list_last = node;
    return node;
  }
  err(1, NULL);
}

int main(int ac, char **av) {
  int n;
  strnode *s;

  for(n = 0; (s = read1line()) != NULL; ++n)
    continue;
  if(n > 0) {
    int i;
    strnode *b;
    char **a = malloc(n * sizeof(char *));
    printf("There were %d lines\n", n);
    for(b = list_head, i = 0; b; b = b->next, ++i)
      a[i] = b->s;
    printf("Near the middle is: %s", a[n / 2]);
  }
  return 0;
}
1
DigitalRoss

Anstelle eines Arrays können Sie hier auch eine verkettete Liste verwenden. Der Code ist einfacher, aber die Zuordnung ist häufiger und kann unter Umständen fragmentiert werden.

Solange Sie nicht vorhaben, einen großen Direktzugriff durchzuführen (dies ist hier O(n)), ist die Iteration so einfach wie ein reguläres Array.

typedef struct Line Line;
struct Line{
    char text[LINE_MAX];
    Line *next;
};

Line *mkline()
{
    Line *l = malloc(sizeof(Line));
    if(!l)
       error();
    return l;
}

main()
{
    Line *lines = mkline();
    Line *lp = lines;
    while(fgets(lp->text, sizeof lp->text, stdin)!=NULL){
         lp->next = mkline();
         lp = lp->next;
    }
    lp->next = NULL;
}
1
Dave

Wenn Sie C verwenden, müssen Sie die Größenänderung des Arrays selbst vornehmen. C++ und die SDL haben dies für Sie erledigt. Es heißt vector. http://www.cplusplus.com/reference/stl/vector/

1
Ed Heal