Wie deklariere ich ein Array mit undefinierter oder keiner Anfangsgröße?

Lesezeit: 7 Minuten

Benutzeravatar von arscariosus
arscariosus

Ich weiß, dass es mit getan werden könnte mallocaber ich weiß noch nicht, wie man es benutzt.

Zum Beispiel wollte ich, dass der Benutzer mehrere Zahlen eingibt, indem er eine Endlosschleife mit einem Wächter verwendet, um einen Stopp einzufügen (z. B. -1), aber da ich noch nicht weiß, wie viele er/sie eingeben wird, muss ich eine deklarieren Array ohne Anfangsgröße, aber ich bin mir auch bewusst, dass es so nicht funktioniert int arr[]; zur Kompilierzeit, da es eine bestimmte Anzahl von Elementen haben muss.

Deklarieren Sie es mit einer übertriebenen Größe wie int anr[1000]; würde funktionieren, aber es fühlt sich dumm an (und verschwendet Speicher, da es diese 1000 ganzzahligen Bytes in den Speicher zuweisen würde) und ich würde gerne mehr wissen elegant Weg, dies zu tun.

  • Wie wäre es mit malloc? Arrays sind keine Zeiger und Zeiger sind keine Arrays. Aber auf Zeiger kann bequem wie auf Arrays zugegriffen werden, und ein Array ohne Skript wird zu einem Zeiger auf das erste Element ausgewertet.

    Benutzer166390

    4. Dezember 2010 um 9:06 Uhr


  • @pst: Hast du die Frage überhaupt gelesen? “Ich weiß, dass man es mit malloc machen könnte, aber ich weiß noch nicht, wie man es benutzt.”

    – Sasha Chedygov

    4. Dezember 2010 um 9:06 Uhr


  • Ich sagte, Sir, dass ich mir dessen bewusst bin, aber ich weiß noch nicht, wie ich es benutzen soll, und zu erklären, wie es die beste Antwort für mich wäre: D

    – Arcariosus

    4. Dezember 2010 um 9:07 Uhr

  • malloc() ist die einzige Möglichkeit, dies zu tun. C unterstützt von Haus aus keine Arrays beliebiger Länge.

    – Sasha Chedygov

    4. Dezember 2010 um 9:08 Uhr

Dies kann durch die Verwendung eines Zeigers und die Zuweisung von Speicher auf dem Heap mit erfolgen malloc. Beachten Sie, dass es keine Möglichkeit gibt, später zu fragen, wie groß dieser Speicherblock ist. Sie müssen die Array-Größe selbst im Auge behalten.

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

int main(int argc, char** argv)
{
  /* declare a pointer do an integer */
  int *data; 
  /* we also have to keep track of how big our array is - I use 50 as an example*/
  const int datacount = 50;
  data = malloc(sizeof(int) * datacount); /* allocate memory for 50 int's */
  if (!data) { /* If data == 0 after the call to malloc, allocation failed for some reason */
    perror("Error allocating memory");
    abort();
  }
  /* at this point, we know that data points to a valid block of memory.
     Remember, however, that this memory is not initialized in any way -- it contains garbage.
     Let's start by clearing it. */
  memset(data, 0, sizeof(int)*datacount);
  /* now our array contains all zeroes. */
  data[0] = 1;
  data[2] = 15;
  data[49] = 66; /* the last element in our array, since we start counting from 0 */
  /* Loop through the array, printing out the values (mostly zeroes, but even so) */
  for(int i = 0; i < datacount; ++i) {
    printf("Element %d: %d\n", i, data[i]);
  }
}

Das ist es. Was folgt, ist eine ausführlichere Erklärung, warum dies funktioniert 🙂

Ich weiß nicht, wie gut Sie C-Zeiger kennen, aber Array-Zugriff in C (wie array[2]) ist eigentlich eine Abkürzung für den Zugriff auf den Speicher über einen Zeiger. Um auf den Speicher zuzugreifen, auf den von gezeigt wird datadu schreibst *data. Dies wird als Dereferenzieren des Zeigers bezeichnet. Seit data ist vom Typ int *dann *data ist vom Typ int. Nun zu einer wichtigen Information: (data + 2) bedeutet “Addieren Sie die Bytegröße von 2 Ints zu der Adresse, auf die gezeigt wird data“.

Ein Array in C ist nur eine Folge von Werten im angrenzenden Speicher. array[1] ist gleich daneben array[0]. Wenn wir also einen großen Speicherblock zuweisen und ihn als Array verwenden möchten, brauchen wir eine einfache Möglichkeit, die direkte Adresse für jedes Element darin zu erhalten. Glücklicherweise lässt uns C die Array-Notation auch für Zeiger verwenden. data[0] bedeutet dasselbe wie *(data+0)nämlich “Zugriff auf den Speicher, auf den von gezeigt wird data“. data[2] meint *(data+2)und greift auf die dritte zu int im Speicherblock.

  • Vielen Dank. Dies ist die umfassende Erklärung, die ich brauche. Vielen Dank für den zusätzlichen Aufwand, Sir. Ich kann jetzt codieren, um ein paar Dinge auf der Grundlage Ihrer Erklärung auszuprobieren. Letzte Frage, wie würde ich die Array-Größe erhöhen, vorausgesetzt, ich möchte sie über 50 Elemente hinaus erweitern?

    – Arcariosus

    4. Dezember 2010 um 9:58 Uhr


  • Um die Arraygröße zu erhöhen, müssen Sie verwenden realloc. Dies könnte Kopieren und verschieben Sie den gesamten Speicherblock, also ist es nicht billig. linux.die.net/man/3/realloc

    – Gnud

    4. Dezember 2010 um 11:04 Uhr

Benutzeravatar von NPE
NPE

Die Art und Weise, wie es oft gemacht wird, ist wie folgt:

  • Weisen Sie ein Array mit einer anfänglichen (ziemlich kleinen) Größe zu;
  • lesen Sie in dieses Array ein und behalten Sie im Auge, wie viele Elemente Sie gelesen haben;
  • Sobald das Array voll ist, weisen Sie es neu zu, verdoppeln die Größe und bewahren (dh kopieren) den Inhalt;
  • wiederholen bis fertig.

Ich finde, dass dieses Muster ziemlich häufig vorkommt.

Das Interessante an dieser Methode ist, dass man damit einfügen kann N Elemente in ein leeres Array eins nach dem anderen in Amortisation O(N) Zeit ohne es zu wissen N im Voraus.

  • Guter Trick. Dies scheint eine einfache und daher vernünftige Antwort zu sein.

    – JW.

    30. Januar 2015 um 20:09 Uhr


Modernes C, auch bekannt als C99, hat Arrays mit variabler Länge, VLA. Leider unterstützen dies nicht alle Compiler, aber wenn Ihrer dies tut, wäre dies eine Alternative.

  • Leider können Sie die Größe nicht ändern, wenn Sie die anfängliche Schätzung überschreiten.

    – wnoise

    4. Dezember 2010 um 9:34 Uhr

  • Danke, ich habe gerade davon erfahren und es ist hilfreich. Ich werde trotzdem versuchen, herauszufinden, wie man das in C89 macht, nur um der Portabilität willen. Danke vielmals.

    – Arcariosus

    4. Dezember 2010 um 9:35 Uhr

  • Gott sei Dank unterstützen das nicht alle Compiler!

    – Undefiniertes Verhalten

    12. Januar 2019 um 16:42 Uhr

Versuchen Sie, dynamische Datenstrukturen wie a zu implementieren verknüpfte Liste

Hier ist ein Beispielprogramm, das liest stdin in einen Speicherpuffer, der nach Bedarf wächst. Es ist so einfach, dass es einen Einblick geben sollte, wie Sie mit solchen Dingen umgehen könnten. Eine Sache, die in einem echten Programm wahrscheinlich anders gemacht würde, ist, wie das Array bei jeder Zuordnung wachsen muss – ich habe es hier klein gehalten, um die Dinge einfacher zu halten, wenn Sie in einem Debugger schrittweise durchgehen möchten. Ein echtes Programm würde wahrscheinlich ein viel größeres Zuweisungsinkrement verwenden (häufig wird die Zuweisungsgröße verdoppelt, aber wenn Sie dies tun, sollten Sie das Inkrement wahrscheinlich auf eine angemessene Größe “begrenzen” – es macht möglicherweise keinen Sinn, dies zu verdoppeln Zuweisung, wenn Sie in die Hunderte von Megabyte kommen).

Außerdem habe ich hier als Beispiel den indizierten Zugriff auf den Puffer verwendet, aber in einem echten Programm würde ich das wahrscheinlich nicht tun.

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


void fatal_error(void);

int main( int argc, char** argv)
{
    int buf_size = 0;
    int buf_used = 0;

    char* buf = NULL;
    char* tmp = NULL;    

    char c;
    int i = 0;

    while ((c = getchar()) != EOF) {
        if (buf_used == buf_size) {
             //need more space in the array

             buf_size += 20;
             tmp = realloc(buf, buf_size); // get a new larger array
             if (!tmp) fatal_error();

             buf = tmp;
        }

        buf[buf_used] = c; // pointer can be indexed like an array
        ++buf_used;
    }

    puts("\n\n*** Dump of stdin ***\n");

    for (i = 0; i < buf_used; ++i) {
        putchar(buf[i]);
    }

    free(buf);

    return 0;
}

void fatal_error(void)
{
    fputs("fatal error - out of memory\n", stderr);
    exit(1);
}

Dieses Beispiel in Kombination mit Beispielen in anderen Antworten sollte Ihnen eine Vorstellung davon geben, wie so etwas auf niedriger Ebene gehandhabt wird.

khachiks Benutzeravatar
khachik

Eine Möglichkeit, die ich mir vorstellen kann, besteht darin, eine verknüpfte Liste zu verwenden, um ein solches Szenario zu implementieren, wenn Sie alle eingegebenen Zahlen benötigen, bevor der Benutzer etwas eingibt, das die Beendigung der Schleife anzeigt. (Posten als erste Option, da ich dies noch nie für Benutzereingaben getan habe, es schien nur interessant zu sein. Verschwenderisch, aber künstlerisch)

Eine andere Möglichkeit ist die gepufferte Eingabe. Weisen Sie einen Puffer zu, füllen Sie ihn, ordnen Sie ihn neu zu, wenn die Schleife fortgesetzt wird (nicht elegant, aber für den gegebenen Anwendungsfall am rationalsten).

Das Beschriebene halte ich allerdings nicht für elegant. Wahrscheinlich würde ich den Anwendungsfall ändern (das rationellste).

1400720cookie-checkWie deklariere ich ein Array mit undefinierter oder keiner Anfangsgröße?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy