Mitglieder in einer C-Struktur verstecken

Lesezeit: 7 Minuten

Benutzeravatar von Marlon
Marlon

Ich habe über OOP in C gelesen, aber es hat mir nie gefallen, dass Sie keine privaten Datenelemente haben können, wie Sie es in C++ können. Aber dann kam mir in den Sinn, dass man 2 Strukturen erstellen könnte. Einer ist in der Header-Datei und der andere in der Quelldatei definiert.

// =========================================
// in somestruct.h
typedef struct {
  int _public_member;
} SomeStruct;

// =========================================
// in somestruct.c

#include "somestruct.h"

typedef struct {
  int _public_member;
  int _private_member;
} SomeStructSource;

SomeStruct *SomeStruct_Create()
{
  SomeStructSource *p = (SomeStructSource *)malloc(sizeof(SomeStructSource));
  p->_private_member = 42;
  return (SomeStruct *)p;
}

Von hier aus können Sie einfach eine Struktur in die andere werfen. Wird dies als schlechte Praxis angesehen? Oder wird es oft gemacht?

  • Warum es so kompliziert machen – Wenn das Tool nicht das tut, was Sie brauchen, verwenden Sie ein anderes.

    – Romain Hippeau

    20. April 2010 um 1:45 Uhr

  • Ich denke, dies würde zumindest in C99 gegen die Objekt-Aliasing-Regeln verstoßen. Ich weiß, dass es in C++ wäre.

    – James McNellis

    20. April 2010 um 1:46 Uhr

  • Es wäre schrecklich, dies zu schließen! Warum stimmen die Leute bei einer so gültigen Frage knapp ab? Weil die vergessen haben, wie man Dinge in C macht?

    – Heide Hunnicutt

    20. April 2010 um 1:47 Uhr

  • Versuchen Sie nicht, Dinge zu verbergen. Verwenden Sie einfach Namen für die “privaten” Mitglieder, die absolut klar machen, dass niemand sie berühren darf. Und wenn jemand sie benutzt, sagen Sie es ihnen. Oder ändern Sie die Namen, um sie zu erziehen.

    – gnasher729

    3. April 2014 um 0:04 Uhr

sizeof(SomeStruct) != sizeof(SomeStructSource). Dies Wille jemanden veranlassen, dich zu finden und dich eines Tages zu ermorden.

  • Und jede Jury würde sie danach gehen lassen.

    – Gnud

    20. April 2010 um 9:43 Uhr

  • „Codieren Sie immer so, als wäre die Person, die Ihren Code verwaltet, ein gewalttätiger Psychopath, der weiß, wo Sie leben.“ (Rick Osborne zugeschrieben)

    – Dietrich Ep

    2. November 2011 um 14:10 Uhr

Benutzeravatar von nos
Nr

Mir persönlich würde das eher gefallen:

typedef struct {
  int _public_member;
  /*I know you wont listen, but don't ever touch this member.*/
  int _private_member;
} SomeStructSource;

Es ist schließlich C, wenn Leute etwas vermasseln wollen, sollten sie das dürfen – keine Notwendigkeit, Sachen zu verstecken, außer:

Wenn Sie die ABI/API-Kompatibilität beibehalten möchten, gibt es zwei Ansätze, die nach meinen Beobachtungen häufiger vorkommen.

  • Geben Sie Ihren Clients keinen Zugriff auf die Struktur, geben Sie ihnen ein undurchsichtiges Handle (ein void* mit einem hübschen Namen), stellen Sie Init/Destroy- und Accessor-Funktionen für alles bereit. Dies stellt sicher, dass Sie die Struktur ändern können, ohne die Clients neu zu kompilieren, wenn Sie eine Bibliothek schreiben.

  • Stellen Sie einen undurchsichtigen Griff als Teil Ihrer Struktur bereit, den Sie beliebig zuweisen können. Dieser Ansatz wird sogar in C++ verwendet, um ABI-Kompatibilität bereitzustellen.

z.B

 struct SomeStruct {
  int member;
  void* internals; //allocate this to your private struct
 };

  • Ich finde ein wirklich schlechtes Design, das es dem Client ermöglicht, auf jedes Mitglied einer Struktur zuzugreifen. Die gesamte Struktur sollte privat sein. Der Zugriff auf seine Mitglieder sollte über Getter und Setter erfolgen.

    – Felipe Lavratti

    19. April 2013 um 12:42 Uhr

  • @fanl, dies in C zu tun, hat viele Auswirkungen, z. B. um eine Struktur auf diese Weise zu verbergen, wird es ziemlich schwierig, sie auf dem Stapel zuzuweisen oder als Mitglied einer anderen Struktur einzufügen. Der einfache Ausweg besteht darin, die Struktur dynamisch zuzuweisen und nur eine Leerstelle* oder ein Handle verfügbar zu machen, und obwohl dies in einigen Fällen in Ordnung sein kann, gibt es viele Fälle, in denen die Auswirkungen zu groß sind und Sie daran hindern, davon zu profitieren was C Ihnen bietet.

    – Nr

    19. April 2013 um 14:20 Uhr


  • IMHO sollte das zweite Beispiel hier angegeben werden das Antwort, denken Sie nur daran, eine Destruktorfunktion bereitzustellen.

    – Luis

    3. Oktober 2014 um 14:38 Uhr

  • Vermeiden Sie in leistungskritischen Situationen den Sprung void * impliziert und die privaten Daten einfach inline zuweist – Datenschutz sei verdammt (in diesem Fall ist alles, was Sie tun können, einen Unterstrich voranzustellen).

    – Techniker

    11. August 2015 um 11:42 Uhr


  • Dein erster Vorschlag ist eigentlich super. Was wir in unserem Unternehmen tun, ist struct S { int x; // PRIVATE_FIELD }; und dann haben wir unseren eigenen C-Code-Analysator, der die Kommentare überprüft, und wenn er einen Kommentar “PRIVATE_FIELD” sieht, hindert er Benutzer daran, etwas einzugeben S.x für dieses Feld.

    – Quecksilber0114

    8. Oktober 2020 um 22:09 Uhr


Benutzeravatar von Logan Capaldo
Logan Capaldo

Du hast es fast geschafft, bist aber noch nicht weit genug gegangen.

In der Kopfzeile:

struct SomeStruct;
typedef struct SomeStruct *SomeThing;


SomeThing create_some_thing();
destroy_some_thing(SomeThing thing);
int get_public_member_some_thing(SomeThing thing);
void set_public_member_some_thing(SomeThing thing, int value);

In der C:

struct SomeStruct {
  int public_member;
  int private_member;
};

SomeThing create_some_thing()
{
    SomeThing thing = malloc(sizeof(*thing));
    thing->public_member = 0;
    thing->private_member = 0;
    return thing;
}

... etc ...

Der Punkt ist, hier haben jetzt die Verbraucher nein Kenntnis der Interna von SomeStruct, und Sie können es ungestraft ändern, Mitglieder nach Belieben hinzufügen und entfernen, sogar ohne dass Verbraucher neu kompilieren müssen. Sie können Mitglieder auch nicht “versehentlich” direkt mungen oder SomeStruct auf dem Stack zuweisen. Dies kann natürlich auch als Nachteil angesehen werden.

  • Einige erwägen die Verwendung typedef Hinweise zu verstecken ist eine schlechte Idee, vor allem, weil es so offensichtlicher ist SomeStruct * muss irgendwie als befreit werden SomeThing, die wie eine gewöhnliche Stapelvariable aussieht. In der Tat können Sie immer noch deklarieren struct SomeStruct; und solange Sie es nicht definieren, werden die Leute gezwungen sein, es zu verwenden SomeStruct * Zeiger, ohne ihre Mitglieder dereferenzieren zu können, was den gleichen Effekt hat, ohne den Zeiger zu verbergen.

    – Chris Lutz

    20. April 2010 um 2:19 Uhr

Benutzeravatar von Felipe Lavratti
Felipe Lawratti

Ich empfehle nicht, das öffentliche Strukturmuster zu verwenden. Das richtige Entwurfsmuster für OOP in C besteht darin, Funktionen für den Zugriff auf alle Daten bereitzustellen, ohne den öffentlichen Zugriff auf Daten zuzulassen. Die Klassendaten sollten an der Quelle deklariert werden, damit sie privat sind und vorwärts referenziert werden, wo Create und Destroy macht Zuordnung und befreit von den Daten. Auf diese Weise wird das Dilemma öffentlich/privat nicht mehr existieren.

/*********** header.h ***********/
typedef struct sModuleData module_t' 
module_t *Module_Create();
void Module_Destroy(module_t *);
/* Only getters and Setters to access data */
void Module_SetSomething(module_t *);
void Module_GetSomething(module_t *);

/*********** source.c ***********/
struct sModuleData {
    /* private data */
};
module_t *Module_Create()
{
    module_t *inst = (module_t *)malloc(sizeof(struct sModuleData));
    /* ... */
    return inst;
}
void Module_Destroy(module_t *inst)
{
    /* ... */
    free(inst);
}

/* Other functions implementation */

Auf der anderen Seite, wenn Sie Malloc/Free nicht verwenden möchten (was in manchen Situationen ein unnötiger Overhead sein kann), schlage ich vor, dass Sie die Struktur in einer privaten Datei verstecken. Private Mitglieder werden zugänglich sein, aber das geht zu Lasten des Benutzers.

/*********** privateTypes.h ***********/
/* All private, non forward, datatypes goes here */
struct sModuleData {
    /* private data */
};

/*********** header.h ***********/
#include "privateTypes.h"
typedef struct sModuleData module_t; 
void Module_Init(module_t *);
void Module_Deinit(module_t *);
/* Only getters and Setters to access data */
void Module_SetSomething(module_t *);
void Module_GetSomething(module_t *);

/*********** source.c ***********/
void Module_Init(module_t *inst)
{       
    /* perform initialization on the instance */        
}
void Module_Deinit(module_t *inst)
{
    /* perform deinitialization on the instance */  
}

/*********** main.c ***********/
int main()
{
    module_t mod_instance;
    module_Init(&mod_instance);
    /* and so on */
}

TU das niemals. Wenn Ihre API irgendetwas unterstützt, das SomeStruct als Parameter verwendet (was ich erwarte), könnten sie einen auf einem Stapel zuweisen und ihn übergeben. Sie würden schwerwiegende Fehler erhalten, wenn Sie versuchen, auf das private Mitglied seit dem Compiler zuzugreifen allocates für die Client-Klasse enthält keinen Platz dafür.

Der klassische Weg, Mitglieder in einer Struktur zu verstecken, besteht darin, sie zu einer Leerstelle* zu machen. Es ist im Grunde ein Handle/Cookie, von dem nur Ihre Implementierungsdateien wissen. So ziemlich jede C-Bibliothek tut dies für private Daten.

Benutzeravatar von caf
Café

Etwas Ähnliches wie die von Ihnen vorgeschlagene Methode wird tatsächlich manchmal verwendet (siehe z. B. die verschiedenen Varianten von struct sockaddr* in der BSD-Socket-API), aber es ist fast unmöglich, es zu verwenden, ohne die strengen Aliasing-Regeln von C99 zu verletzen.

Sie können es jedoch tun sicher:

somestruct.h:

struct SomeStructPrivate; /* Opaque type */

typedef struct {
  int _public_member;
  struct SomeStructPrivate *private;
} SomeStruct;

somestruct.c:

#include "somestruct.h"

struct SomeStructPrivate {
    int _member;
};

SomeStruct *SomeStruct_Create()
{
    SomeStruct *p = malloc(sizeof *p);
    p->private = malloc(sizeof *p->private);
    p->private->_member = 0xWHATEVER;
    return p;
}

Ich würde eine versteckte Struktur schreiben und mit einem Zeiger in der öffentlichen Struktur darauf verweisen. Zum Beispiel könnte Ihre .h Folgendes haben:

typedef struct {
    int a, b;
    void *private;
} public_t;

Und deine .c:

typedef struct {
    int c, d;
} private_t;

Es schützt offensichtlich nicht vor Zeigerarithmetik und fügt ein wenig Overhead für die Zuweisung / Freigabe hinzu, aber ich denke, es geht über den Rahmen der Frage hinaus.

1416940cookie-checkMitglieder in einer C-Struktur verstecken

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy