Richtige Methode zum Initialisieren von C ++ - Strukturen

81

Unser Code enthält eine POD-Struktur (Plain Old Datastructure) (es ist eine grundlegende c ++ - Struktur, die andere Strukturen und POD-Variablen enthält, die zu Beginn initialisiert werden müssen.)

Basierend auf einem, was ich gelesen habe , scheint es, dass:

myStruct = (MyStruct*)calloc(1, sizeof(MyStruct));

sollte alle Werte auf Null initialisieren, ebenso wie:

myStruct = new MyStruct();

Wenn die Struktur jedoch auf die zweite Weise initialisiert wird, beschwert sich Valgrind später, dass "bedingte Sprünge oder Bewegungen von nicht initialisierten Werten abhängen", wenn diese Variablen verwendet werden. Ist mein Verständnis hier fehlerhaft oder wirft Valgrind falsch positive Ergebnisse?

Shadow503
quelle
1
Beachten Sie außerdem, dass new MyStruct()in C ++ 03 keine Auffüllbytes festgelegt werden mussten. In C ++ 0x ist es. Alle Füllbits werden in C ++ 0x auf 0 gesetzt.
Johannes Schaub - litb
Danke für den Haken, ich habe meine Frage bearbeitet.
Shadow503
Freigegebene FAQ: stackoverflow.com/questions/620137/…
Robᵩ
Soweit ich weiß, sollten Sie diese Fehlermeldung nicht erhalten. Vielleicht ist Ihre Datenstruktur kein POD? Können Sie Ihren Code auf das kleinste Programm reduzieren, das dieses Verhalten noch demonstriert, und dieses destillierte Programm veröffentlichen? (Siehe sscce.org ).
Robᵩ
Welcher Compiler? Können Sie die Definition von MyStruct veröffentlichen?
Alan Stokes

Antworten:

119

In C ++ sind Klassen / Strukturen identisch (in Bezug auf die Initialisierung).

Eine Nicht-POD-Struktur kann auch einen Konstruktor haben, um Mitglieder zu initialisieren.
Wenn Ihre Struktur ein POD ist, können Sie einen Initialisierer verwenden.

struct C
{
    int x; 
    int y;
};

C  c = {0}; // Zero initialize POD

Alternativ können Sie den Standardkonstruktor verwenden.

C  c = C();      // Zero initialize using default constructor
C  c{};          // Latest versions accept this syntax.
C* c = new C();  // Zero initialize a dynamically allocated object.

// Note the difference between the above and the initialize version of the constructor.
// Note: All above comments apply to POD structures.
C  c;            // members are random
C* c = new C;    // members are random (more officially undefined).

Ich glaube, Valgrind beschwert sich, weil C ++ früher so funktioniert hat. (Ich bin nicht genau sicher, wann C ++ mit der Standardkonstruktion ohne Initialisierung aktualisiert wurde). Am besten fügen Sie einen Konstruktor hinzu, der das Objekt initialisiert (Strukturen sind Konstruktoren zulässig).

Als Randnotiz:
Viele Anfänger versuchen, init zu bewerten:

C c(); // Unfortunately this is not a variable declaration.
C c{}; // This syntax was added to overcome this confusion.

// The correct way to do this is:
C c = C();

Eine schnelle Suche nach "Most Vexing Parse" liefert eine bessere Erklärung als ich.

Martin York
quelle
1
Haben Sie eine Referenz für diese Nullinitialisierung von Variablen in C ++? Zuletzt habe ich gehört, dass es immer noch das gleiche Verhalten wie C. IIRC hat. Der MSVC-Compiler hat sogar irgendwann nicht initialisierte nicht initialisierte Mitglieder gelöscht (aus Leistungsgründen, nehme ich an), was anscheinend ziemlich schwerwiegende Probleme für die anderen MS-Abteilungen verursachte :)
Marc Mutz - mmutz
1
Nein, das C(), aber es scheint, dass Sie Recht haben .
Marc Mutz - mmutz
4
Mit einer POD - Klasse T, T()hat immer Null die skalare Subobjekte initialisiert. Der Name der Toplevel-Initialisierung wurde in C ++ 98 anders als in C ++ 03 genannt ("Standardinitialisierung" vs. "Wertinitialisierung"), aber der Endeffekt für PODs ist der gleiche. Sowohl in C ++ 98 als auch in C ++ 03 wären alle Werte Null.
Johannes Schaub - litb
1
@RockyRaccoon ist C c();leider keine Variablendeklaration, sondern eine Vorwärtsdeklaration einer Funktion (Eine Funktion namens 'c', die keine Parameter akzeptiert und ein Objekt vom Typ C zurückgibt). Google: Most Vexing Parse
Martin York
1
@RockyRaccoon Der Unterschied hier und mit der Frage, auf die Sie in Ihrem Kommentar verweisen; ist, dass in diesem Fall keine Parameter im Construtor verwendet werden. Daher fällt es dem Compiler schwer, dies zu analysieren und den Unterschied zwischen Variablendeklaration und Vorwärtsdeklaration einer Funktion zu erkennen. Es wählt die Vorwärtsfunktionsdeklaration. Wenn Sie einen Parameter im Konstruktor benötigen, kann der Compiler sehen, dass dies eine Variablendeklaration ohne Probleme ist. C c(4);Ist eine gültige Variablendeklaration.
Martin York
2

Nach dem, was Sie uns gesagt haben, scheint es in Valgrind ein falsches Positiv zu sein. Die newSyntax mit ()sollte das Objekt mit einem Wert initialisieren, vorausgesetzt, es ist POD.

Ist es möglich, dass ein Teil Ihrer Struktur kein POD ist und dies die erwartete Initialisierung verhindert? Können Sie Ihren Code in ein postbares Beispiel vereinfachen, das den Valgrind-Fehler weiterhin kennzeichnet?

Alternativ kann es sein, dass Ihr Compiler POD-Strukturen nicht wirklich wertinitialisiert.

In jedem Fall besteht die wahrscheinlich einfachste Lösung darin, Konstruktoren zu schreiben, die für die Struktur / Unterteile erforderlich sind.

Mark B.
quelle
1

Ich schreibe einen Testcode:

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

using namespace std;

struct sc {
    int x;
    string y;
    int* z;
};

int main(int argc, char** argv)
{
   int* r = new int[128];
   for(int i = 0; i < 128; i++ ) {
        r[i] = i+32;
   }
   cout << r[100] << endl;
   delete r;

   sc* a = new sc;
   sc* aa = new sc[2];
   sc* b = new sc();
   sc* ba = new sc[2]();

   cout << "az:" << a->z << endl;
   cout << "bz:" << b->z << endl;
   cout << "a:" << a->x << " y" << a->y << "end" << endl;
   cout << "b:" << b->x << " y" << b->y <<  "end" <<endl;
   cout << "aa:" << aa->x << " y" << aa->y <<  "end" <<endl;
   cout << "ba:" << ba->x << " y" << ba->y <<  "end" <<endl;
}

g ++ kompilieren und ausführen:

./a.out 
132
az:0x2b0000002a
bz:0
a:854191480 yend
b:0 yend
aa:854190968 yend
ba:0 yend
Yadong
quelle
Ok, ich bin nicht so vertraut mit new sc;vs new sc();. Ich hätte gedacht, dass die fehlenden Parens ein Fehler waren. Dies scheint jedoch zu zeigen, dass die Antwort korrekt ist (sie wird auf 0 gesetzt, wenn Sie den Standardkonstruktor verwenden.)
Nycynik
0

Sie müssen alle Mitglieder initialisieren, die Sie in Ihrer Struktur haben, z.

struct MyStruct {
  private:
    int someInt_;
    float someFloat_;

  public:
    MyStruct(): someInt_(0), someFloat_(1.0) {} // Initializer list will set appropriate values

};
ralphtheninja
quelle
In diesem Fall verwende ich keine Klasse, sondern eine Struktur.
Shadow503
Eine Struktur ist eine Klasse. Bearbeitet meinen Beitrag :)
ralphtheninja
2
Sie müssen dies nicht für einen POD tun
Alan Stokes
0

Da es sich um eine POD-Struktur handelt, können Sie sie jederzeit auf 0 setzen. Dies ist möglicherweise der einfachste Weg, um die Felder zu initialisieren (sofern dies angemessen ist).

Scott C Wilson
quelle
1
Ob es sich um eine Struktur oder eine Klasse handelt, hat nichts mit der Fähigkeit zu tun, ein Memset zu erstellen oder zu erstellen. Siehe z. B. stackoverflow.com/questions/2750270/cc-struct-vs-class
Erik
Normalerweise würden Sie eine Klasse nach dem Erstellen nicht memseten, da ihr Konstruktor (vermutlich) die Initialisierung durchgeführt hat.
Scott C Wilson
1
Es gibt keinen Unterschied zwischen "Klasse" und "Struktur" in C ++, mit Ausnahme des Standardschutzes (privat bzw. öffentlich). Eine Struktur kann ein Nicht-POD sein und eine Klasse kann ein POD sein. Diese Konzepte sind orthogonal.
Marc Mutz - mmutz
@mmutz und @Erik - vereinbart - haben meine Antwort verkürzt, um Verwirrung zu vermeiden.
Scott C Wilson
memset () ist nicht die beste Option. Sie können eine POD-Struktur auf Null initialisieren, indem Sie einfach ihren Standard-ctor aufrufen.
Nils
0

Das scheint mir der einfachste Weg zu sein. Strukturelemente können mit geschweiften Klammern '{}' initialisiert werden. Das Folgende ist beispielsweise eine gültige Initialisierung.

struct Point 
{ 
   int x, y; 
};  

int main() 
{ 
   // A valid initialization. member x gets value 0 and y 
   // gets value 1.  The order of declaration is followed. 
   struct Point p1 = {0, 1};  
}

Es gibt gute Informationen zu Strukturen in c ++ - https://www.geeksforgeeks.org/structures-in-cpp/

Michael Klishevich
quelle