Compiler-Error bei Arraydefinition



  • Yohallo,
    also wie die Überschrift schon vermuten lässt, benötige ich ein wenig Hilfestellung bei der Klärung eines Fehlers, den Visual C++ bzgl. eines Arrays ausgibt.
    Vorangestellt sei, dass ich ein ziemlicher Neuling in Visual C++ (und in Programmierung überhaupt) bin, und noch nicht sooooo viel praxis besitze, es ist halt lediglich ein hobby...

    in einer meiner funktionen deklariere ich ein integer-array, das genau so viele feldelemente besitzen soll, wie es der benutzer mit einer eingabe vorschreibt, konkreter fall:

    int pot[i];
    // Viel Code ist das ja nicht *g*
    

    ...somit verspreche ich mir mehr flexibilität... so, das klappt auch wunderbar im Rahmen einer einfachen konsolenanwendung (compiliert mit Bloodshed Dev-C++),
    aber nu hab ich mit dem gleichen Code eine WindowsApp zurechtgebastelt, und da spuckt Visual C++ immer die 3 Fehler aus:

    error C2057: Konstanter Ausdruck erwartet
    error C2466: Reservierung eines Feldes der konstanten Groesse 0 nicht moeglich
    error C2133: 'pot' : Unbekannte Groesse

    ...liegt das daran, dass ich für die definition eine variable verwende???

    wär schade, wenn das unter visual c++ nich ginge...

    bis denne,
    midian



  • Midian schrieb:

    ..liegt das daran, dass ich für die definition eine variable verwende???

    Ja, du must das Array dynamisch erstellen also int *pot = new int[i];



  • Hallo.

    Stell es dir mal so vor: dein Compiler sorgt dafür, dass das Programm für jede Variable genug Speicher bekommt. Eine int-Variable bekommt z.B. 4 Byte (unter 32bit-Systemen). Wenn du also schreibst

    int i = 0;
    

    Sorgt dein Compiler dafür, dass 4 Byte speicher reserviert werden. Dieser Speicherbeeich wird mit dem Variablennamen i "verknüpft". Außerdem weißt er dem Speicherbereich dann noch gleich den Wert 0 zu. Er füllt also die 4 Byte mit 0en auf:

    00000000 00000000 00000000 00000000
    

    Soweit erstmal klar, oder?

    Bei Arrays sieht das anders aus. Da muss der Compiler ja wissen, wieviel Speicher er reservieren muss. Bei

    int array[100];
    

    Sind das 100x 4 Byte, also 400 Byte. Diese werden hintereinader im Speicher abgelegt.
    Jede Speicherzelle hat eine Adresse. Das muss man wissen. Der Compiler sorgt also dafür, dass 400 Byte Speicher reserviert werden und "verknüpft" die Variable array mit der Anfangsadresse der 400 Byte.

    Problematisch wird es, wenn du dem Compiler nicht genau angibst, wie viele Elemente er nun anlegen soll. Soll er das selbst entscheiden? kann und sollte er besser nicht. Die Lösung sind sog. Zeiger (pointer).
    Zeigder sind Variablen, die keine "echten" Werte (Zahlen, Zeichen) speichern, sondern die Adresse einer anderen Variable enthalten. Wenn man durch das Konzept erst mal durchblickt, ist's an sich ganz einfach. Ansonsten die Hölle auf Erden 😃

    Ein Zeiger wird so deklariert:

    int * ptr = new int;
    

    Dabei legt der Compiler einen neuen Zeiger an und reserviert ("allokiert") im gleichen Atemzug noch einen Speicherbreich für ein int-Variable (4 Byte). Die Adresse dieses Speicherbereiches bekommt dann der Zeiger als "Wert" zugewiesen.
    Manuell erhält man übrgens die Adresse einer Variable so:

    int * ptr = &myvar;
    

    Damit weißt du ptr die Adresse von myvar (einer int-Variable) zu.

    Ein angelegter Zeiger sollte auch wieder gelöscht werden (da er sonst im Speicher belibt (auch nach dem Ende des Programmes), womit ein "Speicherleck" (memory leak) ensteht). Dazu verwendenet man delete:

    delete ptr;
    

    Damit wird der Speicher, den der Array belegt, wieder freigegeben.

    Nun zu deinem Array:

    Der Wert von i ist bei dir ja variabel (wird vom Benutzer eingegeben), weswegen zur Laufzeit der Speicher dynamisch reserviert (und bestenfalls auch wieder freigegeben) werden muss. Dazu legst du dir als erstes einen Zeiger an:

    int * ptr = NULL;
    

    Die Zuweisung von NULL ist sehr wichtig, sonst zeigt dein zeiger irgendwo hin, und das ist äußerst gefährlich!!!
    Nun enthält der Zeiger ptr also als Adresse noch NULL, also nichts. Jetzt weisen wir ihm die Adresse eines neuen Arrays zu:

    ptr = new int[i];
    

    So wird ein neuer Speicherbereich angelegt (der dann i Elemente vom Typ int enthält), und die Anfangsadresse dieses Speicherbereiches wird ptr zugewiesen. Bravo! Du hast deine erste dynamische Speicherreservierung (Allokation) durchgeführt.

    Nun musst du nur noch auf die einzelnen Elemente zugreifen können. Dies geschith z.B. so:

    for(int x = 0; x < i; x++)
    {
       *(ptr + x) = x;
    }
    

    Wichtig dabei: Hier wird der *-Operator nicht zur Erstellung eines neuen zeigers verwendet, sondern zur sog. Dereferenzierung, d.h., man greift auf den Wert (und nicht auf die Adresse) zu, der sich hinter der Adresse, die der Zeiger enthält, befindet. Ist jetzt schwer zu erklären... 🙄

    Der von dir angelegte Array sollte möglichst noch freigegeben werden, diesmal statt mit delete mit delete[] (der "Array"-Version):

    delete[] ptr;
    

    Da das Thema Zeiger echt schwer zu verstehen ist, wenn man sich nur in Foren helfen lässt (das war jetzt nicht so gemeint, wie es geklungen hat), such dir am besten ausführliche Tutorials oder kauf dir bestenfalls ein wirklich gutes Buch (kein Einsteigerbuch, da diese das Thema Arrays oft nicht oder nur unzureichend erklären).

    @all: Hab ich da jetzt grobe Schnitzer in der Erklärung? Ist nur ne kurze Version, ich hab keine Zeit gehabt 😉

    Hoffentlich hilft dir das ein wenig... an sich reicht die Info von newvet ja aus, um dein problem zu lösen. Aber das Verstehen ist wohl wichtiger, sonst kommst du nie über dein jetziges "Nivaeu" hinaus 👍

    Bye.



  • Happosai schrieb:

    @all: Hab ich da jetzt grobe Schnitzer in der Erklärung? Ist nur ne kurze Version, ich hab keine Zeit gehabt 😉

    Naja, deine Erklärung, warum das nicht geht, unterschlägt leider, dass es im Bloodshed funktioniert. Im VC++ sind halt einfach keine VLAs implementiert, im gcc schon. Da die Dinger auch nicht Standard-, wenigstens nicht C++-, konform sind, stellt das kein großes Problem dar.



  • Was ist VLA? 🙄



  • Happosai schrieb:

    Was ist VLA? 🙄

    "Variable-length array". Sorry.



  • Ich stell mir sowas gefährlich vor. Wenn man womöglich keine Ahnung von Arrays und/oder Zeigern hat. Sollte man die Dinger wirklich nutzen, um sich Arbeit zu ersparen? (Kenner der Materie können das ja machen, aber Anfänger...)



  • Wenn man keine Ahnung von Arrays und Pointern hat, sollte man das entweder lernen oder das Programmieren sein lassen. Es gibt keine Entschuldigung, die Grundlagen nicht zu beherrschen, schon gar nicht in einer Sprache wie C++, die sich "trust the programmer" auf die Fahnen geschrieben hat.

    Was das mit VLAs zu tun haben soll, versteh ich allerdings nicht ganz. Bei auf dem Heap allozierten Arrays kann man viel mehr falsch machen.



  • Ich meinte nur, dass sowas für Anfänger gefährlich sein kann, da es vielleicht dazu verleitet, alles dem Compiler und der IDE zu überlssen, anstatt sich hinzusetzen und die Grundlagenzu lernen.
    ist so ähnlich wie beim VC-Anwendungsassistent: Erfahrenen Programmierern nimmt der die lästige Aufgabe ab, immer wieder ein Anwendungsgerüst zu schreiben, während er allerdings Anfänger dazu verleiten kann, in 5 Minuten ohne große Kenntnisse von C++ kleine Progs zu schreiben (und dann als Folge meist Foren mit unsinnigen Fragen zu überschwemmen).
    Alles hat seine Vor- und Nachteile. Solche Hilfsmittel (die ich zugegebenermaßen in diesem Fall gar nicht kenne) können dafür sorgen, dass man nie über ein bestimmtes Nivaeu hinaus kommt, da man sich nie dazu genötigt fühlt, mehr zu lernen. "Die IDE macht's schon..."
    Mal ganz abgesehen davon, was passiert, wenn man von Bloodshed wechselt und dann plötzlich keine VLAs mehr hat. 😉

    Bye.



  • Ach so meinst du das. Nee, VLAs sind kein Hilfsmittel, was irgendwas erleichtert. Jedenfalls nicht, wenn du meintest, dass die im Hintergrund heimlich new und delete machen (das wär ja eher sowas wie vector). Du kannst halt einfach nur Arrays mit variabler Größe auf dem Stack anlegen. Wenn sie aus dem Scope gehen, werden sie wieder abgeräumt. Das bringt einige Schwierigkeiten mit sich, z.B. dass der sizeof-Operator nicht mehr immer eine Compilezeit-Konstante zurückliefert, oder dass Stackframes variabel groß sind, aber das betrifft hauptsächlich den Compilerbauer.

    Vielleicht gibts bei C++ noch mehr Probleme, die ich grade nicht sehe, VLAs sind ja eigentlich nur in C99 enthalten.



  • wow, na das war doch echt umfangreich, thx, an euch- vor allem natürlich an happosai! pointer kenn ich zwar, und benutze ich zum üben auch nich ausschließlich diese community (lese z.Z. diverse Bücher von Markt Technik 😉 ), aber bislang hab ich zeiger nur zum vertauschen von feldelementen verwendet, jetz seh ich mal, was man damit noch so tolles machen kann... 😃
    danke nochmal!



  • In der MFC gibt es eine Klasse CArray und dort die Funktion SetSize()

    evtl. nützt die dir etwas.



  • Wie arbeitet denn die Funktion? (mit der MFC komm ich noch nicht sooooo gut klar...)- davon mal abgesehen; das mit der allokation hat wunderbar geklappt...



  • CArray<int, int>arMeinFeld;
    //dann irgendwo im code wo du deine variable hast:
    arMeinFeld.SetSize(i);



  • jau, alles klar, aber die lösung mit der speicherreservierung liegt mir mehr (universell einsetzbar und hat insgesamt mehr stil 😃 )

    e>>> wie schafft es denn die funktion, einen benutzerdefinierten umfang des arrays festzulegen? führt die auch eine speicherreservierung aus? der einzige vorteil läge ja dann im Einsparen von Schreibarbeit...


Anmelden zum Antworten