PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C Problem mit typen



t.knopp
18-03-2005, 14:17
Hallo!

Ich Programmiere gerade an einem Projekt mit C.

Ich habe nun 2 Structs struct1 und struct2

typedef struct1_ {
int a;
...
} struct1;

typedef struct2_ {
int a;
...
} struct2;

ich würde nun gerne soetwas machen:

(void*) s;
if(..)
s = (struct1*) ...,
else
s= (struct2*) ...,

s.a=3;

Als eigendlich möchte ich eine abstrakten Basisstruct. Aber es muß halt C sein.

jemand ne idee, wie man das elegant lösen kann.
Gibt es in C vielleicht eine Typvariable, das man sowas machen kann:

Typ t =struct1;

((t*)s).a = 3

?

Tobias

locus vivendi
18-03-2005, 15:50
Wenn es wirklich C sein muss, dann kannst du deine Structs in eine Union packen. Dann ist es erlaubt, einen gemeinsamen Anfang der Structs über den einen Typ anzusprechen, und über einen anderen Typ auszulesen. Wenn du wirklich nur einen einzigen gemeinsamen Member am Anfang des Structs hast, dann geht es auch über einen Cast. Aber das ist für mein Verständnis schon eine ziemlich obskure Verwendung der Sprache. Vielleicht kannst du ja doch C++ verwenden? Denn für mich schreit das geradezu nach Vererbung.

t.knopp
18-03-2005, 16:18
Das Problem bei einem union ist folgendes:

union {
struct1 x;
struct2 y;
} u;

jetzt kann ich aber auch nur über u.x.a und u.y.a drauf zugreifen.
Ich würde gerne nur über u.a darauf zugreifen, aber so geht das nicht.
Ich glaube mittlerweile, das das Problem so in C nicht gelöst werden kann

Pingu
18-03-2005, 17:21
Schreibe Dir doch ein Makro:

#define TYPE_INT 1
#define TYPE_UNSIGNED 2

union {
struct _x x;
struct _y y;
} a;

struct {
unsigned char type;
union a a;
} b;

#define Set(var, val) do { if (var.type == TYPE_INT) {var.a.x = val; } else {var.a.y = val; } } while (0)


Alternativ wenn Du mit Pointern arbeitest muß es natürlich "->" statt "." sein.

Pingu

t.knopp
18-03-2005, 18:34
Ja das wäre eine Möglichkeit, aber ich möchte keine if Abfragen, da der Code sehr oft ausgeführt wird. Ich werde es jetzt anders machen und die structs nicht mehr gemeinsam betrachten.

panzi
18-03-2005, 19:09
Ja das wäre eine Möglichkeit, aber ich möchte keine if Abfragen, da der Code sehr oft ausgeführt wird. Ich werde es jetzt anders machen und die structs nicht mehr gemeinsam betrachten.
Eine if Abfrage erhöht die laufzeit um einen konstanten Betrag: in der Regel ist sowas nicht der kritische Punkt an einen Algorythmus.

Aber mach es doch so ähnlich wie es bei den Adressen bei sockets gemacht wird:


struct {
// alle überschneidenden Variablen
} base_s;

struct {
// alle überschneidenden Variablen (in der selben reihenfolge!)
// alle spezifischen Variablen
} foo_s;


struct {
// alle überschneidenden Variablen (in der selben reihenfolge!)
// alle spezifischen Variablen
} bar_s;

struct foo_s foo;
struct bar_s bar;
struct base_s * ptr1 = (struct base_s *)foo;
struct base_s * ptr2 = (struct base_s *)bar;

// somit kann man auf ptr1->var und ptr2->var gleichermasen zugreifen.

t.knopp
19-03-2005, 21:15
Hm also das ist mal eine total krasse idee. Wird mir denn zugesichert, das die Variablen in der richtigen Reihenfolge stehen (in einem struct). Ist das nicht sehr unsauberer Programmierstil? Aber im Prinzip genau das was ich will. Danke.
Bei dieser Software geht es gerade nur um Schnelligkeit. Darum keine if-Abfragen

panzi
20-03-2005, 12:18
Hm also das ist mal eine total krasse idee. Wird mir denn zugesichert, das die Variablen in der richtigen Reihenfolge stehen (in einem struct). Ist das nicht sehr unsauberer Programmierstil? Aber im Prinzip genau das was ich will. Danke.
Bei dieser Software geht es gerade nur um Schnelligkeit. Darum keine if-Abfragen
Wenn du sie in der selben Reihenfolge reinschreibst, dann sind sie auch in der selben Reihenfolge im Speicher.
Ich finde es nicht 100%ig schönen Programmierstiel, aber er scheint unter C sehrwohl gebräuchlich zu sein, siehe z.B.: http://unixhelp.ed.ac.uk/CGI/man-cgi?ip+7 unter ADDRESS FORMAT. Das sin_family und sin_port hat jede Adresse, aber das sin_addr ist von Protokoll zu Proptokoll verscheiden, deswegen müssen auch an einige Funktionen die größe der Adresse mitübergeben werden (damits wohl ordnungsgemäß kopiert werden kann, auch wenn die aufgerufenen Funktion nur einen "Basisklassen"-Pointer bekommt).

locus vivendi
20-03-2005, 13:40
Wenn du sie in der selben Reihenfolge reinschreibst, dann sind sie auch in der selben Reihenfolge im Speicher.
Ich finde es nicht 100%ig schönen Programmierstiel, aber er scheint unter C sehrwohl gebräuchlich zu sein, [ ... ]
Das mit den Socket Address Formats ist aber meines Erachtens noch ein anderer Sachverhalt als dein Vorschlag. Bei den Address Formats gibt es effektiv nur eine Member-Variable die den gemeinsamen Anfang darstellt, nämlich "sa_family". Deine Vorschlag suggeriert das der gemeinsame Anfang auch mehrere Member-Variablen umfassen könnte. Ich vermute das dies nicht erlaubt ist. C definiert zwar "kompatible Typen" und gibt einige Garantien wie man durch einen kompatiblen Typ auf ein Objekt eines anderen kompatiblen Typs zugreifen kann. Aber meines Wissens sind Structs die eine unterschiedliche Anzahl von Member-Variblen haben nicht kompatibel. Ich bin mir allerdings auch nicht 100%-ig sicher, wenn du Hinweise darauf liefern könntest das ich mich täusche, dann wäre ich dir dafür sehr dankbar.

panzi
20-03-2005, 16:49
Das mit den Socket Address Formats ist aber meines Erachtens noch ein anderer Sachverhalt als dein Vorschlag. Bei den Address Formats gibt es effektiv nur eine Member-Variable die den gemeinsamen Anfang darstellt, nämlich "sa_family". Deine Vorschlag suggeriert das der gemeinsame Anfang auch mehrere Member-Variablen umfassen könnte. Ich vermute das dies nicht erlaubt ist. C definiert zwar "kompatible Typen" und gibt einige Garantien wie man durch einen kompatiblen Typ auf ein Objekt eines anderen kompatiblen Typs zugreifen kann. Aber meines Wissens sind Structs die eine unterschiedliche Anzahl von Member-Variblen haben nicht kompatibel. Ich bin mir allerdings auch nicht 100%-ig sicher, wenn du Hinweise darauf liefern könntest das ich mich täusche, dann wäre ich dir dafür sehr dankbar.
es geht auch nicht um eine zuweisung mit = oder so. es geht um zugriff per pointern. und da ist der erste teil der beiden structs gleicht. also wenn man mit einen pointer auf den speicher des structs zugreift, der nur den teil kennt, der gleich ist, dann sollte es funktionieren.natürlich wird dann der teil, der nicht bekannt ist, ignoriert. der kann "manuell" nur kopiert werden (wenn man die größe kennt per malloc und memcpy), aber darum gehts ja eh net, nur um den zugriff auf gemeinsame sachen.

locus vivendi
21-03-2005, 17:30
es geht auch nicht um eine zuweisung mit = oder so. es geht um zugriff per pointern. und da ist der erste teil der beiden structs gleicht. also wenn man mit einen pointer auf den speicher des structs zugreift, der nur den teil kennt, der gleich ist, dann sollte es funktionieren.natürlich wird dann der teil, der nicht bekannt ist, ignoriert. der kann "manuell" nur kopiert werden (wenn man die größe kennt per malloc und memcpy), aber darum gehts ja eh net, nur um den zugriff auf gemeinsame sachen.
Ich habe schon mehrfach gelesen, dass dies der Fall ist. Allerdings frage ich mich, wo dies tatsächlich garantiert wird, oder woraus dies folgt. Alles was ich finden kann, steht dem eher entgegen. Z.b. im C-Standard (genauer einem Entwurf) in Abschnitt 6.7.2.1, Absatz 12:
[#12] Within a structure object, the non-bit-field members
and the units in which bit-fields reside have addresses that
increase in the order in which they are declared. A pointer
to a structure object, suitably converted, points to its
initial member (or if that member is a bit-field, then to
the unit in which it resides), and vice versa. There may be
unnamed padding within a structure object, but not at its
beginning.

Es wird also die Aussage gemacht, das irgendwo in einer Struktur "Padding" sein kann. Wo wird also festgelegt, das in einem gemeinsamen Anfang mehrer Strukturen nicht an unterschiedlichen Stellen Padding sein könnte? Ein zweites Beispiel aus dem Standard scheint mir ebenfalls eher negativ zu sein:
6.3.2.3 Pointers

[#7] A pointer to an object or incomplete type may be
converted to a pointer to a different object or incomplete
type. If the resulting pointer is not correctly aligned50)
for the pointed-to type, the behavior is undefined.
Otherwise, when converted back again, the result shall
compare equal to the original pointer. When a pointer to an
object is converted to a pointer to a character type, the
result points to the lowest addressed byte of the object.

Die Formulierung ", when converted back again, the result shall compare equal" legt doch nahe das nach der ersten der beiden Konvertierungen der Zeiger einen unspezifizierten Wert hat. Wie soll dann der Elementzugriff erlaubt sein? Falls man übrigens C++ betrachtet, da ist das sogar noch genau so definiert. Im C++-Standard steht explizit folgendes (Teil 5.2.10, Absatz 7):

"A pointer to an object can be explicitly converted to a pointer to an object of different type.65) Except that
converting an rvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object type
and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type
yields the original pointer value, the result of such a pointer conversion is unspecified."

Da ich wie gesagt auch woanders schon gelesen habe das dein Vorschlag tatsächlich erlaubt ist, würde mich eine Quelle die das belegt wirklich erfreuen. Vielleicht weisst du ja eine? (gerne auch C++, nicht nur C)