2016-05-12 7 views
0

Ich habe C schon lange auf Mikrocontrollern benutzt.C++ globale Objektinitialisierung schlägt fehl - warum? und ist es möglich, Objekte im .DATA-Bereich zu platzieren?

Kürzlich (endlich!) Habe ich es geschafft, GCC dazu zu bringen, einen C++ Code für meinen μC zu kompilieren und einen Code zu portieren, der zuvor nur OOP zum echten Deal "simuliert" hat.

Nach langem Debuggen und Kämpfen und Googeln habe ich nur noch ein Problem, aber dieses Rätsel verwirrt mich sehr. Ausführen dieses Codes (GCC/Arm):

class Test {                    

public: 
    int val; 

    Test(int val){                  
     this->val = val;                 
    } 
}; 

Test test1(5);                   

static void _test() { 

    Test test2(7);                  

    Debug::printf("-test1: %d-\n", test1.val);           
    Debug::printf("-test2: %d-\n", test2.val); 
} 

int main() { _init(); /*Hardware*/ _test(); while(1){} } 

ich als Ausgabe:

-test1: 0- 
-test2: 7- 

Also irgendwie ist test1 nicht initialisiert, wie ich es erwartet hätte zu sein.

Warum ist das so? Und was passiert hier wirklich? (Und warum beklagt sich der Compiler nicht?)

Was ich gerne tun würde: Da dies auf Mikrocontrollern läuft, möchte ich vermeiden, "malloc/new" zu verwenden, sondern dies in .DATA Abschnitte wie ich tun würde mit C:

struct Test { 
    int val; 
} 

Test test1 = { 5 }; 

Ist das überhaupt möglich?

+0

Wenn ich mich nicht irre, sollten Sie den Operator -> nicht verwenden, es sollte nur ein 'Test.val' sein, nicht' Test-> val', da Ihre Testobjektinstanz kein 'Test ist * ' – Funkyguy

+1

Wahrscheinlich ist etwas falsch mit dem Start der Anwendung Ihrer Umgebung, und es läuft nicht der übliche Vorläufer-Code zu' main() ', der statische Daten initialisiert und so weiter –

+0

@MM: Es stellte sich heraus, dass dies der Fall ist. S. die Kommentare unten R Sahus Antwort. – Scheintod

Antwort

0

In eingebetteten Systemen Programmierung, ist es sehr schlechte Praxis mit statischer Speicherdauer auf Konstruktor ruft für Objekte zu verlassen.

Das ist, weil viele Compiler die Option haben, einen nicht standardmäßigen "minimalen Start" zu erstellen, was bedeutet, dass der Compiler die Anforderungen der C- und C++ - Standards und die Initialisierung aller statischen Speicherdauervariablen einschließlich Aufruf ignoriert Konstruktoren für solche Objekte.

Auch im Allgemeinen C++ müssen Sie vorsichtig sein, nicht auf die Reihenfolge der Initialisierung von statischen Speicherdauervariablen zu verlassen. Wenn Sie Objekt A und B haben und Objekt A zuerst initialisiert wird, darf in As Konstruktor, der auf Initialisierung von B angewiesen ist, kein Code vorhanden sein. More info in the C++ FAQ.

+0

Sie sagen also: Ignorieren Sie dies und setzen Sie eine 'init (...) 'method in? – Scheintod

+0

@Scheintod Normalerweise ist das in der Tat die beste Idee, aber Sie werden trotzdem Konstruktoren/Destruktoren für den Fall haben wollen, wenn Sie Objekte im lokalen Bereich zuweisen – Lundin

+0

In meinem Fall ist das kein Compiler flag but missing linker/startup-code.Siehe untenstehende Kommentare R Sahus Antwort.Ich würde mit der Verwendung von init gehen, aber es gibt ein Jucken, das ich kratzen muss, weil ich gesehen habe, dass dies in Arduino/Energia IDE funktioniert, die beide gcc verwenden bin highlighte d der Teil, der meiner Meinung nach die richtige Antwort ist, obwohl Thomas und R Sahu ebenfalls hilfreich waren. – Scheintod

0

Also irgendwie test1 wird nicht initialisiert, wie ich es erwartet hätte.

Die Reihenfolge der Initialisierung ist ein Problem, mit dem Sie beim Erstellen von Variablen im globalen Gültigkeitsbereich umgehen müssen. Eine Möglichkeit, das Problem zu vermeiden, besteht darin, sie mit Accessor-Funktionen zu kapseln.

Der folgende Code sollte unabhängig von der Reihenfolge der Initialisierung globaler Objekte funktionieren, da er nicht von einem globalen Objekt abhängt.

static Test& getTest1() 
{ 
    static Test t(5); 
    return t; 
}                   

static void _test() { 

    Test test2(7);                  

    Debug::printf("-test1: %d-\n", getTest1().val);           
    Debug::printf("-test2: %d-\n", test2.val); 
} 
+0

Hallo. Danke für deine Antwort. Aber ich würde erwarten, dass globale Variablen immer initialisiert werden, bevor "normaler" Code ausgeführt wird (ich denke, alles andere ist sinnlos). – Scheintod

+0

Globale Objekte werden initialisiert, bevor irgendetwas in 'main' ausgeführt wird. –

+0

Wann rufst du '_test()'? –

1

eine einfache struct oder class Verwendung ist in der Tat möglich:

class Test { // or use struct without explicit public accessibility 
public: 
    int val; 
}; 

Test test1 = { 5 }; 

Sie:

  • keine Lust Bauarbeiten mit dieser Ebene Layout und
  • nicht zu tun haben jedes Mitglied, das eine Konstruktion benötigt.

Andernfalls müssen Sie nicht über new oder malloc Sorge, wenn Sie programmieren, wie Sie in Ihrem Beispiel tat test2 billig auf dem Stapel reserviert wird, obwohl ein Konstruktor aufgerufen wird.

nur als Randnotiz: betrachten initializer Listen verwenden, wenn Sie verwenden Konstrukteure tun:

class Test { 
    int m_val; 
public: 
    Test(int val) : m_val(val) {} 
};