C++: Zeiger Programmierung und Strukturen



  • Hallo,

    bei meinem Projekt (http://www.roboternetz.de/community/threads/69561-Mein-neues-Projekt-Die-kleine-Mischerei) geht es langsam aber sicher in die Programmierphase...
    Beruflich programmiere ich viel in der SPS Welt hauptsächlich in AWL. In der Techniker Schule habe ich C# kennengelernt und dort bin ich auch relativ fit drin.
    Naja C++ ist zwar sehr ähnlich, aber das sind die Programmiersprachen ja alle irgendwie. Nun geht es um die Zeigerprogrammierung. Dort hoffe ich das ganze verstanden zu haben, sicher bin ich aber nicht.
    Daher wollte ich mal fragen ob ich das ganze so programmieren kann. Das Programm wird nachher auf ein Arduino laufen.
    Ich habe auch schon viel im Internet gelesen, aber spezielle Fragen habe ich immer noch. Das Programm lässt sich auf jeden fall so kompilieren wie ich das jetzt hier im ersten Beispiel vorstelle.
    Mir geht es darum ob ich das ganze so realisieren kann, oder ob es da zu Datenverlust kommen kann.

    Fall 1:
    Klasse.h

    class Sortierer
    {
    
        ////////////////////////////////////////
        // Strukturen
        public: 
        struct strKoordinate
        {
            byte x;
            byte y;
        };
    
        struct strGrenzen
        {
            byte minimum;
            byte maximum;
        };
    
        struct strFarbGrenzen
        {
            strGrenzen rot;
            strGrenzen gruen;
            strGrenzen blau;
        };
    
        struct strStellung
        {
            strKoordinate koordinate;
            strFarbGrenzen farbe;
        };
    
        ////////////////////////////////////////
        // Variablen
        private:  
        strStellung adressenStellung[SO_STELLUNGEN];
        strStellung stellung[SO_STELLUNGEN];
    
        ////////////////////////////////////////
        // Konstruktoren
        public: 
        Sortierer(struct strStellung padressenStellung[SO_STELLUNGEN]);
    
        ////////////////////////////////////////
        // Methoden
        void setFarbGrenzen(byte pStellung, struct strFarbGrenzen *pFarbe);
        struct strFarbGrenzen     getFarbGrenzen(byte pStellung);
    
    };
    #endif // SORTIERER_H
    

    Meine Frage zu diesem Teil. Kann ich die Strukturen so realisieren, also eine Struktur in eine Struktur und noch tiefer schachteln?

    Klasse.cpp

    #include "Sortierer.h"
    #include <EEPROM.h>
    
    ////////////////////////////////////////
    // Konstruktoren
    Sortierer::Sortierer(struct Sortierer::strStellung padressenStellung[SO_STELLUNGEN])
    {
        for(int i = 0; i < SO_STELLUNGEN; ++i)
        {
            adressenStellung[i].koordinate = padressenStellung[i].koordinate; 
            adressenStellung[i].farbe = padressenStellung[i].farbe; 
        }
    }
    
    ////////////////////////////////////////
    // Methoden    
    void Sortierer::setFarbGrenzen(byte pStellung, struct Sortierer::strFarbGrenzen *pFarbe)
    {
        EEPROM.update(adressenStellung[pStellung].farbe.rot.minimum, pFarbe -> rot.minimum);
        EEPROM.update(adressenStellung[pStellung].farbe.rot.maximum, pFarbe -> rot.maximum);
        EEPROM.update(adressenStellung[pStellung].farbe.gruen.minimum, pFarbe -> gruen.minimum);
        EEPROM.update(adressenStellung[pStellung].farbe.gruen.maximum, pFarbe -> gruen.maximum);
        EEPROM.update(adressenStellung[pStellung].farbe.blau.minimum, pFarbe -> blau.minimum);
        EEPROM.update(adressenStellung[pStellung].farbe.blau.maximum, pFarbe -> blau.maximum);
        stellung[pStellung].farbe.rot = pFarbe -> rot;
        stellung[pStellung].farbe.gruen = pFarbe -> gruen;
        stellung[pStellung].farbe.blau = pFarbe -> blau;
    }
    
    struct Sortierer::strFarbGrenzen Sortierer::getFarbGrenzen(byte pStellung)
    {
        struct Sortierer::strFarbGrenzen pFarbe;
        pFarbe = stellung[pStellung].farbe;
        return pFarbe;
    }
    

    Hier die frage zum Zeiger. Der Übergabewert der Methode setFarbGrenzen ist ja ein Byte, und ein Zeiger der Struktur strFarbGrenzen.
    In der Methode will ich ja die Werte von dem Zeiger abgreifen was ich ja mit pFarbe -> rot mache. Momentan kann ich so aber nur die Elemente der Struktur übergeben aber nicht die ganze Struktur.
    Ist es möglich die ganze struktur zu übergeben. Also quasi:

    stellung[pStellung].farbe = pFarbe;

    pFarbe ist ja im diesen Fall die Zeigeradresse weswegen das so ja nicht geht. Gibt es irgendein Befehl um von der Zeigeradresse die ganze Struktur zu übergeben?

    also so in der Art:

    stellung[pStellung].farbe = pFarbe -> pFarbe;

    oder kann ich nur die Strukturelemente einzeln übergeben wie in der Methode geschrieben:

    stellung[pStellung].farbe.rot = pFarbe -> rot;
    stellung[pStellung].farbe.gruen = pFarbe -> gruen;
    stellung[pStellung].farbe.blau = pFarbe -> blau;

    Dann noch eine Frage zu den Methoden einer Klasse:

    Ist so etwas möglich?

    struct Sortierer::strFarbGrenzen *Sortierer::getFarbGrenzen(byte pStellung)
    {
    struct Sortierer::strFarbGrenzen pFarbe;
    pFarbe = stellung[pStellung].farbe;
    return &pFarbe;
    }

    Also ich gebe ein Zeiger zurück von einer Variable / Struktur die in einer Methode erzeugt wird.
    Wahrscheinlich nicht da die Variable nach dem Return nicht mehr existiert und der Zeiger somit ins leere zeigt.

    Ich müsste wahrscheinlich die Variable in der Klasse.h erzeugen damit dieses funktioniert also quasi so:

    struct Sortierer::strFarbGrenzen *Sortierer::getFarbGrenzen(byte pStellung)
    {
    farbe = stellung[pStellung].farbe; // farbe wird als private Variable in der Klasse erzeugt
    return &farbe;
    }

    Hauptprogramm

    ...
    Sortierer::strStellung fs_adressen[7] = {{1,2,3,4,5,6,7,8},  // EEPROM Adressen
                                            {9,10,11,12,13,14,15,16},
                                            {17,18,19,20,21,22,23,24},
                                            {25,26,27,28,29,30,31,32},
                                            {33,34,35,36,37,38,39,40},
                                            {41,42,43,44,45,46,47,48},
                                            {49,50,51,52,53,54,55,56}};
    
    Sortierer fs_sortierer(fs_adressen); //Objekt erstellen
    ...
    void loop() {
    ...
    Sortierer::strFarbGrenzen fs_Grenzen =  fs_sortierer.getFarbGrenzen(0);  //Grenzen von Stellung 0
    ...
    }
    

    So und jetzt die Fragen was ist möglich in der Loopschleife.

    Ich denke dieses hier sollte kein Problem sein:

    Sortierer::strFarbGrenzen fs_Grenzen = fs_sortierer.getFarbGrenzen(0);

    also der Rückgabewert (Struktur) der Methode wird nicht als Zeiger sondern als Struktur zurückgegeben.

    Ich denke so etwas geht nicht oder:

    Funktion(&fs_sortierer.getFarbGrenzen(0));

    Also der Übergabewert der Funktion ist ein Zeiger auf den Rückgabewert der Methode.
    Dies wird wahrscheinlich nicht Funktionieren, weil der Rückgabewert (die Struktur) nicht mehr bei dem Funktionsaufruf existiert und der Zeiger somit ins leere zeigt.
    Ist dieses richtig?

    Das Ganze müsste ich dann wahrscheinlich so lösen oder?

    Sortierer::strFarbGrenzen fs_Grenzen = fs_sortierer.getFarbGrenzen(0);

    Funktion(&fs_Grenzen);

    Ich hoffe der Code ist nicht zu schwer zu lesen und ich weiß auch das es schwierig ist / Zeit braucht fremden Code zu verstehen.
    Ich hoffe trotzdem das der ein oder andere sich Zeit nimmt und sich ein wenig rein denkt.
    Schon einmal vielen Dank für die Antworten.

    Gruß André



  • Hallo André,

    mehrere Objekte der selben Art speichern -> std::vector (variable Anzahl), std::array (konstante, bekannte Anzahl).
    Objekte (class/struct) als Funktionsparameter: pass-by-reference-to-const (immutable) bzw. pass-by-reference (mutable)
    built-in types wie int, char, float* -> pass-by-value i. d. R.

    Ja, Du kannst eine structur innerhalb einer structur definieren usw.

    stellung[pStellung].farbe = pFarbe;
    

    Dies ist sehr wohl möglich und sogar die bessere Methode. Der Compiler generiert im Hintergrund Construktoren/Destruktoren/Kopier-Konstrukturen etc., und so lange deine Klassen nicht selbst Ressourcen verwalten müssen (free-store, threads, sockets, ...) funktionieren die Compiler generierten auch einwandfrei.

    Ich hab jetzt mal nur der Kürze wegen alles etwas zusammen gestaucht, aber man könnte es z.B. so machen:

    #include <array>  // std::array
    class Sortierer {
    public:
    	struct Koordinate { byte x,y; };
    	struct Grenzen { byte min,max; };
    	struct FarbGrenzen { Grenzen r,g,b };
    	struct Stellung {
    		Koordinate koordinate;
    		FarbGrenzen farbe;
    	};
    
    	using Stellungen = std::array<Stellung, SO_STELLUNGEN>;
    
    	explicit Sortierer(const Stellungen& padressenStellung)
    		: adressenStellung{ padressenStellung } { } 
    
    	void setFarbGrenzen(byte pStellung, const FarbGrenzen& farbe) {
    		// inwiefern das EEPROM Ding funktioniert kenne ich mich nicht aus.
    		// evtl. EEPROM.update(adressenStellung[pStellung].farbe, farbe)
    		EEPROM.update(adressenStellung[pStellung].farbe.rot.minimum, farbe.rot.minimum);
    		EEPROM.update(adressenStellung[pStellung].farbe.rot.maximum, farbe.rot.maximum);
    		EEPROM.update(adressenStellung[pStellung].farbe.gruen.minimum, farbe.gruen.minimum);
    		EEPROM.update(adressenStellung[pStellung].farbe.gruen.maximum, farbe.gruen.maximum);
    		EEPROM.update(adressenStellung[pStellung].farbe.blau.minimum, farbe.blau.minimum);
    		EEPROM.update(adressenStellung[pStellung].farbe.blau.maximum, farbe.blau.maximum);
    		stellung[pStellung].farbe = farbe;
    	}
    
    	FarbGrenzen getFarbGrenzen(byte pStellung) { return stellung[pStellung].farbe; }
    private:
    	Stellungen adressenStellung;
    	Stellungen stellung;
    };
    

    Codeformatierung ist größtenteils Geschmackssache - auf Konsistenz kommt es an. Ich möchte jedoch anmerken, dass ich persönlich den 'str' Präfix für structs ziemlich störend fand... i. A. steht "str" für mich für "string" ^^ Es wird schwieriger den eigentlichen Typ Namen zu lesen und zu erkennen, worum es sich handelt.

    Ich hoffe ich konnte zmd. teilweise hilfreich sein.

    LG

    P.S.: class und struct sind equivalent, bis auf den Unterschied, dass bei struct standardmäßig alles public ist und bei class private


Anmelden zum Antworten