2009-03-08 5 views
1

Ich habe eine Definition Prüfung struct wie folgt:Zugriff auf Strukturkomponenten direkt

struct test{ 
    int a, b, c; 
    bool d, e; 
    int f; 
    long g, h; 
}; 

Und irgendwo ich es auf diese Weise verwenden:

test* t = new test; // create the testing struct 
int* ptr = (int*) t; 
ptr[2] = 15;   // directly manipulate the third word 
cout << t->c;   // look if it really affected the third integer 

Dies funktioniert korrekt auf meinem Windows - es 15 druckt wie erwartet aber ist es sicher? Kann ich wirklich sicher sein, dass die Variable an Ort und Stelle im Speicher ist, den ich möchte - besonders im Fall solcher kombinierter Strukturen (zum Beispiel ist f auf meinem Compiler das fünfte Wort, aber es ist eine sechste Variable)?

Wenn nicht, gibt es eine andere Möglichkeit, Strukturelemente direkt zu manipulieren, ohne tatsächlich im Konstruktor das Konstrukt struct-> member zu haben?

Antwort

13

Es sieht aus wie Sie zwei Fragen

Ist es sicher & Test als 3 Länge int arrray zu behandeln?

Es ist wahrscheinlich am besten, dies zu vermeiden. Dies kann eine definierte Aktion im C++ - Standard sein, aber selbst wenn es so ist, ist es unwahrscheinlich, dass jeder, mit dem Sie arbeiten, versteht, was Sie hier tun. Ich glaube, dass dies nicht unterstützt wird, wenn Sie den Standard wegen der Möglichkeit zu Pad-Strukturen lesen, aber ich bin mir nicht sicher.

Gibt es einen besseren Weg, um auf ein Mitglied ohne seinen Namen zuzugreifen?

Ja. Versuchen Sie es mit dem Makro/Operator offsetof. Dadurch wird der Speicher-Offset eines bestimmten Elements innerhalb einer Struktur bereitgestellt und Sie können einen Punkt korrekt auf diesem Element positionieren.

size_t offset = offsetof(mystruct,c); 
int* pointerToC = (int*)((char*)&someTest + offset); 

Ein anderer Weg, obwohl wäre, nehmen Sie nur die Adresse c direkt

int* pointerToC = &(someTest->c); 
+0

Dies scheint zu sein, was ich gesucht habe, danke –

+0

Danke eine Million. Ich wusste nicht "offsetof"! Ich habe mich in den letzten 6 Jahren selbst C++ Programmierer genannt. –

+0

Der 'offsetof'-Trick ruft undefiniertes Verhalten auf. –

5

Nein, Sie können nicht sicher sein. Der Compiler ist frei, Padding zwischen Strukturelementen einzuführen.

1

Es ist wahrscheinlich nicht sicher und ist 100% un-lesbar; Dadurch wird diese Art von Code im realen Produktionscode inakzeptabel.

2

Sie suchen wahrscheinlich nach dem offsetof Makro. Dadurch erhalten Sie den Byte-Offset des Elements. Sie können dann das Element an diesem Offset bearbeiten. Beachten Sie jedoch, dass dieses Makro implementierungsspezifisch ist. Fügen Sie stddef.h ein, damit es funktioniert.

0

Verwenden Sie set-Methoden und boost :: bind für create Funktor, die diese Variable ändern wird.

4

zu JaredPar's answer, eine weitere Option in C hinzuzufügen ++ nur (nicht im Klar c) ist ein Zeiger auf Mitglied-Objekt zu erstellen:

struct test 
{ 
    int a, b, c; 
    bool d, e; 
    int f; 
    long g, h; 
}; 

int main(void) 
{ 
    test t1, t2; 

    int test::*p; // declare p as pointing to an int member of test 
    p = &test::c; // p now points to 'c', but it's not associating with an object 
    t1->*p = 3; // sets t1.c to 3 
    t2->*p = 4; // sets t2.c to 4 

    p = &test::f; 
    t1->*p = 5; // sets t1.f to 5 
    t2->*p = 6; // sets t2.f to 6 
} 
0

Neben padding/Ausrichtungsprobleme anderen Antworten haben gebracht, Ihr Code verletzt strenge Aliasing-Regeln, was bedeutet, dass er für optimierte Builds brechen kann (nicht sicher, wie MSVC das tut, aber GCC -O3 wird bei dieser Art von Verhalten brechen). Da test *t und int *ptr unterschiedliche Typen sind, kann der Compiler im Wesentlichen annehmen, dass sie auf verschiedene Teile des Speichers zeigen und Operationen neu anordnen können.

diese geringfügige Modifikation vor:

test* t = new test; 
int* ptr = (int*) t; 

t->c = 13; 
ptr[2] = 15; 
cout << t->c; 

Der Ausgang am Ende könnte entweder 1315 oder, abhängig von der Reihenfolge der Operationen der Compiler verwendet werden.

+0

Nein - wie die gültige Anweisung 'int * ptr = & (t-> c);' zeigt, kann ein int * einen Test alias machen, wenn der Strukturtest (direkt oder indirekt) mindestens einen int enthält. – MSalters

+0

@MSalters - das macht keinen Sinn. & (t-> c) hat den Typ int *, was nicht dasselbe ist wie die Verwendung von Aliasing, um in eine Struktur "hineinzuspähen". Zum Beispiel, was ist wenn (char *) & (t-> c) - (char *) t ist kein Vielfaches von sizeof (int)? – Tom

0

Nach Absatz 9.2.17 des Standards ist es in der Tat legal einen Zeiger-to-struct auf einen Zeiger auf sein erstes Element zu werfen, das struct Vorsehen POD:

Ein Zeiger auf ein POD-struct Objekt, eine reinterpret_cast unter Verwendung geeigneter Weise umgewandelt, weist auf seinen anfänglichen Mitglied (oder, wenn das Element ein Bitfeld, dann an die Einheit, in der sie sich befindet ) und umgekehrt. [Hinweis: Es könnte daher unbenannt Padding in einem POD-struct-Objekt, , aber nicht am Anfang, wie erforderlich , um eine entsprechende Ausrichtung zu erreichen.

]

jedoch die Norm keine Garantien über das Layout von structs macht - auch structs POD - außer, dass die Adresse eines späten Mitglied größer sein wird als die Adresse eines früheren Mitglieds, vorausgesetzt, es gibt keine Zugriffsspezifizierer (private:, protected: oder public:) zwischen ihnen. So ist die Behandlung des ersten Teils Ihrer struct test als ein Array von 3 ganzen Zahlen technisch undefiniert Verhalten.

Verwandte Themen