2017-06-08 1 views
2

Bei der Zuweisung von Speicher für einen Zeiger auf einen Datensatz benötige ich auch Platz für einen ganzzahligen Zeiger, der unmittelbar vor dem zugewiesenen Datensatz liegt. Dieser Zeiger kann nicht Teil des Datensatzes selbst sein und kann nicht nach dem Datensatz platziert werden. Mein derzeitiger Ansatz ist der folgende:Datensatz mit Variable bei negativem Offset zuweisen

#include <stdlib.h> 

static int n; 
struct { int f; } *p; 

p = malloc(sizeof (int *) + sizeof *p); 
if (p != NULL) { 
    p = (void *) ((int **) p + 1); 
    *((int **) p - 1) = &n; 
} 

Sind die Gussformen gut definiert? Wenn nicht, was sollte ich stattdessen tun?

Edit:

Was ich versuche zu erreichen ist erweiterbar Aufzeichnungen (OOP) und den Integer-Zeiger implementieren stellen eine Art ID. Ein erweiterter Datensatz sollte mit seinem Basistyp kompatibel sein. Ich benötige jedoch nur Typ ID: s für Zeiger auf Datensätze. Hier ist ein komplettes Beispiel:

#include <assert.h> 
#include <stdlib.h> 

struct T0 { 
    int f; 
}; 
int T0ID; 

struct T1 { 
    struct T0 base; 
    int g; 
}; 
int T1ID; 

int main(void) 
{ 
    struct T0 *x; 
    struct T1 *y; 

    y = malloc(sizeof (int *) + sizeof *y); 
    if (y != NULL) { 
     *((int **) y) = &T1ID; 
     y = (void *) ((int **) y + 1); 
     ((struct T0 *) y)->f = 1; 
     y->g = 2; 
    } 
    x = (struct T0 *) y; 
    assert(x->f == 1); 
    return 0; 
} 
+4

Sie müssen Rechnung Alignement berücksichtigen. –

+0

Können Sie eine Struktur von Zeiger auf int und die Struktur, die Sie verwenden möchten, machen? Dann für diese Struktur zuweisen und einen Zeiger auf struct member erhalten? – Yunnosch

+0

Können Sie Ihr Code-Snippet auf ein [MCVE] aktualisieren, was ein wenig mehr zeigt, wie Sie das Konstrukt verwenden möchten? – Yunnosch

Antwort

1

Ihr Code, in dem Sie werfen (willkürliche) Speicheradressen auf bestimmte Objekttypen, ist möglicherweise nicht definiertes Verhalten ergeben aufgrund falscher Ausrichtung (vgl C standard draft):

6,3 .2.3 Zeiger

(7) Ein Zeiger auf einen Objekttyp kann in einen Zeiger auf einen anderen Objekttyp konvertiert werden. Wenn der resultierende Zeiger nicht korrekt auf den referenzierten Typ ausgerichtet ist, ist das Verhalten nicht definiert.

Sie sind mit einem Zeiger struct{int}* zu einem (größeren Speicherblock), der auf seine vom Typ deklarierte, und dann den Zeiger Gießen und arithmetische Operationen auf den (Gießen) Zeiger zu tun. Da nicht garantiert ist, dass int * und struct{int}* auf die gleiche Weise ausgerichtet sind, ist nicht garantiert, dass das Verhalten definiert ist.

Um dies zu vermeiden, kapseln Sie Ihre Struktur und die vorhergehende ganze Zahl in einer anderen Struktur, z. wie folgt:

static int n; 

struct data_struct { 
    int f; 
}; 

struct enclosing_struct { 
    int *nPtr; 
    struct data_struct data; 
}; 


int main() { 

    struct enclosing_struct *e = malloc (sizeof(struct enclosing_struct)); 
    e->nPtr = &n; 
    struct data_struct *dataPtr = &e->data; 

    return 0; 
} 
+0

Wäre es möglich, eine einzelne Wrapper-Struktur zu definieren, die für alle Zeigertypen funktioniert? –

2

Ich bin mir nicht sicher, ob Ihr Ansatz gut ist. Besonders besorgt bin ich über den Wert von . Sie benötigen diesen Wert später, wenn Sie den Speicher freigeben müssen.

Ich würde die int* und die Struktur zusammen in einer anderen Struktur wickeln. Etwas wie:

static int n; 
struct someData { int f; }; 

struct wrapper {int* pn; struct someData data;}; 

struct someData* pd; // Pointer to the data struct 


struct wrapper* pw = malloc(sizeof *pw); 

if (pw != NULL) { 
    pw->pn = &n; 
    pd = &pw->data; 
} 
+0

Vermutlich möchte die OP auch zu 'pw' zurückkehren können, wenn' pd' gegeben wird. Vielleicht möchten Sie auch zeigen, wie das geht. – pat

+0

OP, ich würde gerne wissen, ob @pat richtig ist. Es scheint eine wahrscheinliche implizite Anforderung zu sein. – Yunnosch

+3

Es ist nicht schwer - '(struct wrapper *) ((char *) pd - offsetof (Struktur Wrapper, Daten))'. Deshalb existiert 'offsetof'. –