2009-07-30 10 views
0

erregenden Lets sagen, ich habe diese Struktur:c struct Daten durch Offset

typedef struct nKey { 
    int num; 
    widget* widget; 
} NUMBER_KEY; 

und eine Funktion:

void dialKey(widget* widget) { 
    // Need to print 'num' of the struct that this widget resides in 
} 

Wie gehe ich um dies zu erreichen? Ich habe versucht, so etwas wie:

printf("%d", * (int *) widget - sizeof(int)); // Failure.org 

edit: es ist davon auszugehen, dass das Widget in der Tat geführt wird, ist ein Mitglied einer NUMBER_KEY struct

bearbeiten: auf der Suche nach einer Lösung für das Problem nicht eine andere Methode.

+0

Ihr Code wird nicht unbedingt so funktionieren, wie es ist, da es möglicherweise implementation-definierte Auffüllung zwischen Mitgliedern einer Struktur gibt. –

+0

Das große Ding ist, dass der Zeiger zum Widget ist, nicht zum Widget-Zeiger. Subtrahieren von sizeof (int) von einem (int *) bewegt diesen Zeiger auch zurück zu sizeof (int) ints, nicht nur eins. Ganz zu schweigen von der Möglichkeit der Polsterung. –

Antwort

4

nur Widgit * Gegeben und kein Widgit ** in dialKey geführt wird, gibt es keine Möglichkeit, was Sie zu tun want (ein widgit * -Wert hat keine Beziehung zur NUMBER_KEY-Struktur).Unter der Annahme, dass Sie wirklich etwas bedeuten wie:

void dialKey(widget** ppWidget) 
{  
    // Need to print 'num' of the struct that this widget resides in 
} 

Microsoft hat eine raffinierte Makro für diese Art der Sache zu tun (es hilft mit Routinen zu haben, in der Lage, die verkettete Listen genericly in C manipulieren):

#define CONTAINING_RECORD(address, type, field) ((type *)(\ 
           (PCHAR)(address) - \ 
           (ULONG_PTR)(&((type *)0)->field))) 

Sie könnten so etwa so:

NUMBER_KEY* pNumberKey = CONTAINING_RECORD(*ppWidgit, NUMBER_KEY, widgit); 

printf("%d", pNumberKey->num); 
+0

+1 für 'widget *' vs. 'widget **', die alle vorhandenen Antworten verfehlt haben. 'CONTAINING_RECORD' ist eindeutig ein Hack, aber zumindest verbirgt es die blutigen Details ... – RBerteig

+0

Sehr guter Punkt, ich habe den Zeiger in der struct-Deklaration verpasst. Ich werde meine Antwort entsprechend korrigieren. –

+0

@RBertig - es kann ein Hack sein, aber es ist ein verdammt nützlicher. –

-1

Die sicherste Lösung ist in Widget

printf("%d", widget->myKey->num); 

Alle anderen Methoden sind unsicher

+0

Es ist eine übertriebene Übertreibung zu sagen "Alle anderen Methoden sind unsicher". – qrdl

5

Wie Michael in seiner Antwort erklärt, Zeiger auf zugehörige nKey Struktur zu halten, können Sie es nicht mit bestimmten Einschränkungen, weil keine da ist Weg zurück zum Zeigerdiagramm. Um die Dinge noch deutlicher zu machen, lassen Sie mich ein Diagramm von Objekten zeichnen (in C ausgedrückt, nicht in OOP Sinne) beteiligt: ​​

+- NUMBER_KEY --+ 
| ...   | points to  
| widget field -+-----------+ 
| ...   |   | 
+---------------+   | + widget + 
          +-->| ... | 
           | ... | 
          +-->| ... | 
          | +--------+ 
        points to | 
[widget argument]-----------+ 

Beachten Sie die Pfeile. Sie sind in einer Richtung - Sie können von einem Zeiger zu einem spitzen Wert "laufen", aber Sie können nicht zurück gehen. So können Sie widget Argument Ihrer Funktion, um auf das widget Objekt zu bekommen, aber sobald es gibt, gibt es keine Möglichkeit zu sagen, wer sonst Punkte darauf - einschließlich Instanzen von NUMBER_KEY Strukturen. Denken Sie darüber nach: Was ist, wenn Sie ein Dutzend andere Zeiger auf die gleichen widget, einige von ihnen aus verschiedenen NUMBER_KEY Objekte hatten? Wie könnte es das möglicherweise verfolgen, ohne eine Liste aller Zeiger innerhalb widget des Gegenstandes zu halten? Wenn Sie das wirklich brauchen, müssen Sie es tun - machen Sie widget zeigen Sie ihm NUMBER_KEY.

0

Das Speicherlayout einer Struktur wird von Ihrem Compiler definiert, Ihr Code würde nur funktionieren, wenn Sie zwischen den Strukturelementen einen Abstand von 0 Byte haben. Normalerweise wird dies nicht empfohlen, da 0 Byte Padding Ihre Struktur über die Standard-Lesegrößen der meisten Prozessoren verteilt.

einige nützliche Makros für Ihr Problem:

#define GET_FIELD_OFFSET(type, field) ((LONG)&(((type *)0)->field)) 
#define GET_FIELD_SIZE(type, field)  (sizeof(((type *)0)->field)) 

Beispiel:

NUMBER_KEY key; 
key.num = 55; 
int nOffSetWidget = GET_FIELD_OFFSET(NUMBER_KEY, widget); 
int *pKeyNumAddress = (int *) &(key.widget) - nOffSetWidget); 

printf("%d", * pKeyNumAddress); // Should print '55' 
+0

Wenn Sie # oder # enthalten, ist das offsetof() -Makro Standard. –

+0

Ich glaube nicht, dass es so funktioniert, wie Sie es erwarten. Sie übergeben die Adresse von key.widget an * int, so dass die Zeigermathematik mit use sizeof (int) als arithmetische Einheit verwendet wird. Anstatt also nOffSetWidget von der Anzahl der Bytes abzurechnen, ziehen Sie sizeof (int) * nOffSetWidget die Anzahl der Bytes ab. Und es gibt Standard Makro Offsetof - keine Notwendigkeit, es zu erfinden. – qrdl

0

C-Standard definiert offsetof() Makro (definiert in stddef.h Header), die in Ihrem Fall praktisch ist.

#include <stddef.h> 

void DialKey(widget** foo) { 
    printf("%d", *((char *)foo - offsetof(struct nKey, widget))); 
} 

Beachten Sie, dass Sie haben eine Adresse von struct Feld kein Wert zu übergeben!