Gast
#4264212
Hallo, ich habe eine kleine Frage zu programmiersparech C: Ist es in C erlaubt bzw. sauber so etwas zu schreiben?
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
Dann würde ich mit
1 | |
auf zahl zugreifen.
|
Anzeige
|
Ist das in C erlaubt?
Gast
#4264212
Hallo, ich habe eine kleine Frage zu programmiersparech C: Ist es in C erlaubt bzw. sauber so etwas zu schreiben?
Dann würde ich mit
auf zahl zugreifen.
Gast
#4264221
Machen kannst du in C (zu) viel. Ich denke es kompiliert und in deinem einfachen Fall könnte es sogar gehen. Was machst du aber nun wenn du ne zweite Zahl hast? Schön ist es daher nicht. Wenn du schon unbedingt nen void-Pointer nutzen musst caste es wieder auf den richtigen Typ zurück.
Gast
#4264223
Ja, theoretisch ists erlaubt. Nein, es ist nicht sauber. Hat es einen Grund warum du deinen Zeiger zu void* konvertierst? Und warum kann die Rückkonvertierung nicht zu Struktur sein? Heute grad in Rätsellaune? Hab ich auch noch eins: Ändert sich etwas, wenn man im obigen Beispiel uint8_t zu uint16_t verändert? Also am Prinzip der Frage, nicht am Wertebereich.
Gast
#4264227
Linda schrieb: > Dann würde ich mit*((uint8_t*)test_ptr)auf zahl zugreifen. Bis in ein paar Wochen jemand ein Structmember vor zahl einfügt.
Gast
#4264242
Linda schrieb: > Hallo, > ich habe eine kleine Frage zu programmiersparech C: > Ist es in C erlaubt bzw. sauber so etwas zu schreiben? >
> > Dann würde ich mit >
> auf zahl zugreifen.
Man kann das auch etwas ausführlicher kommentieren:
Das wird funktionieren, aber nur für den besonderen Fall, des Zugriffs
auf das erste Element der Struktur. Es gibt noch ein paar
Nebenbedingungen, wie die Frage ob das erste Element wirklich an der
selben Adresse liegt, wie die Struktur insgesamt. Das ist nämlich im
Sprachstandard nicht zugesichert. (Siehe auch pragma pack oder was es
das sonst an netten gcc-Schmankerln gibt - ich verliere da so langsam
den Überblick).
Sowie, Du auf eines der Nachfolgeelemente zugreifen willst, geht das
nicht mehr so.
Sauberer insgesamt ist das hier:
Solche void-Tricks kann man machen und auch relativ sauber. Dann aber irgendwie so:
Sieht zwar geschwätzig aus, ist aber ein Idiom, dass jeder versteht. Und wie gesagt: Immer auf "packing" aufpassen, sonst gibts Beulen. :-)
Gast
#4264249
Klaus schrieb: > Es gibt noch ein paar > Nebenbedingungen, wie die Frage ob das erste Element wirklich an der > selben Adresse liegt, wie die Struktur insgesamt. Das ist nämlich im > Sprachstandard nicht zugesichert. (Siehe auch pragma pack oder was es > das sonst an netten gcc-Schmankerln gibt - ich verliere da so langsam > den Überblick). Doch genau das ist zugesagt. 6.7.2.1 Absatz 13 des C99 Standard > 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) Prinzipiell dürfen Implementationen zwar ziemlich viel alignen, aber am Anfang alignen ist untersagt.
Gast
#4264256
Ano schrieb: > Klaus schrieb: >>... Das ist nämlich im >> Sprachstandard nicht zugesichert. > Doch genau das ist zugesagt. > 6.7.2.1 Absatz 13 des C99 Standard >> 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) Ah. Jetzt wo Du es sagst :-) Hatte ich vergessen. Vielen Dank.
Gast
#4264264
@Linda Falls das fraglich ist: Meine Ansicht, dass es mit einem Struktur * "offensichtlicher" ist, halte ich aufrecht. Ich hatte ja auch geschrieben, dass es auf dem anderen Weg durchaus geht. Allerdings war, wie Ano richtig festgestellt hat, meine Einschränkung falsch. Damit geht es so grundsätzlich immer. "Schön" finde ich es trotzdem nicht und notwendig ist es erst recht nicht.
Gast
#4264268
Harold schrieb: > xzq = *((Struktur*)test_ptr).zahl; > oder > xzq = ((Struktur*)test_ptr)->zahl; Das obere geht nicht und das untere kenne ich schon. Mit geht es um genau um den oben genannten Ausdruck. Ano schrieb: > Und warum kann die Rückkonvertierung nicht zu Struktur sein? Weil ich zu der Zeit noch nicht weiß was sich hinter dem void-Pointer verbirgt. Tom schrieb: > Bis in ein paar Wochen jemand ein Structmember vor zahl einfügt. Wenn jemand an meinen Code rumpfuscht ohne zu wissen was er tut geschieht es ihm eh recht wenn dann nichts mehr funktioniert. Ano schrieb: > Prinzipiell dürfen Implementationen zwar ziemlich viel alignen, aber am > Anfang alignen ist untersagt. Das ist mal eine gute Nachricht. Danke :-) Jetzt hab ich es noch kurz durch den GCC mit -pedantic gejagt. Die einzige Warnung war der // Kommentar. Sollte also passen.
Gast
#4264395
Ein paar Fragen hätt ich noch: Mit einem Enum an Stelle der uint8_t würde das auch klappen? Was genau ist so eine Enum eigentlich, wie viele bits ist es breit, sind alle Enums gleich breit?
Gast
#4264430
Linda schrieb: > Wenn jemand an meinen Code rumpfuscht ohne zu wissen was er tut Du baust da eine dämliche und völlig unnötige Falle ein. Aber es soll sich jeder für schlauer halten und pfuschen, wie er meint... Was spricht gegen Klaus' Lösung oder ein void *foo = &test.zahl; ?
Gast
#4264452
> Was spricht gegen Klaus' Lösung oder ein > void *foo = &test.zahl; > ? Wenn es so einfach wäre hätte ich auch einfach den Struktur* Pointer verwenden können. Ich will die Struktur zusammen mit ein paar anderen in einem Array aus void-Pointer halten und dann anhand der ID im ersten Element herausfinden können welche Struktur sich hinter dem Pointer verbirgt. Dabei fällt mir ein, eigentlich konnte ich auch ein Array aus
Pointer machen. P.S. Nur weil du es nicht verstehst ist es noch lange nicht gepfuscht. Linda schrieb: > Dabei fällt mir ein, eigentlich konnte ich auch ein Array aus >
> Pointer machen. Das ist schon wesentlich besser. Merkregel: du willst um void Pointer einen grossen Bogen machen. void Pointer willst du nur benutzen wenn: * du ein Modul hast, das Pointer zu internen Datenstrukturen ausliefern muss, die so zu verwenden sind, dass derselbe Pointer für jegliche Bearbeitung wieder in eine Funktion aus dem Modul zurück gegeben werden muss. Der void Pointer ist also nur ein Transportmedium, mit dem sich ein Modul selber Daten in verschiedenen Funktionen zuspielen kann. Für den Benutzer dieses Moduls hat der Pointer kein besondere Bedeutung, insbesondere soll er ihn nicht dereferenzieren * wenn du Funktionen hast, die eine allgemeine Byteschnittstelle haben sollen, wie zb memcpy. Aus praktischen Gründen hat memcpy als Argumente void Pointer, weil das der einzige Pointer Typ ist, an den alle anderen Pointer Typen zuweisbar sind. > P.S. Nur weil du es nicht verstehst ist es noch lange nicht gepfuscht. Du hast ja auch erst jetzt erläutert was der Sinn der Sache ist. Eine andere Möglichkeit wäre es, alle möglichen Strukturen in einer union zu vereinen und dann aus dieser union (oder einem Pointer darauf) und dem Typ-Member eine weitere Struktur zu bauen, die immer gleich aufgebaut ist. Damit hast du deinen konstanten Pointer Datentyp, den du in ein Array aufnehmen kannst. Aber wenn irgendwie möglich, willst du die Datentypprüfung des Compiler nicht abschalten. Mit void Pointern und wildem rumgecaste tust du aber genau das. Linda schrieb: > Was genau ist so eine Enum eigentlich, Steht im C-Buch. > wie viele bits ist es breit, sind > alle Enums gleich breit? http://stackoverflow.com/questions/1113855/is-the-sizeofenum-sizeofint-always
Gast
#4264522
Linda schrieb: > Ein paar Fragen hätt ich noch: > Mit einem Enum an Stelle der uint8_t würde das auch klappen? Ja. > Was genau ist so eine Enum eigentlich, wie viele bits ist es breit, sind > alle Enums gleich breit? Um mal wieder den C-Standard zu zitieren (6.7.2.2 Absatz 4) > Each enumerated type shall be compatible with char, > a signed integer type, or an unsigned integer type. > The choice of type is implementation-defined, 110) > but shall be capable of representing the values of > all the members of the enumeration. The enumerated > type is incomplete until after the } that terminates > the list of enumerator declarations. Im Klartext: Der Compiler darf entscheiden wie breit er enums macht. Das heißt insbesondere auch verschiedene enums dürfen verschieden breit sein. Du kannst nur eine Minimalbreite vorgeben, da alle Werte mit dem gewählten Typen darstellbar sein müssen. Randnotiz: In C++ gibts seit C++11 mit enum classes die Möglichkeit dort breiten selbst festzulegen, hilft aber in C nicht weiter. Linda schrieb: > Dabei fällt mir ein, eigentlich konnte ich auch ein Array aus > struct > { > uint8_t id; > } > Pointer machen. Müsste da nicht zumindest noch ein separater Zeiger auf den Inhalt drin sein? Karl H. schrieb: > Eine andere Möglichkeit wäre es, alle möglichen Strukturen in einer > union zu vereinen und dann aus dieser union (oder einem Pointer darauf) > und dem Typ-Member eine weitere Struktur zu bauen, die immer gleich > aufgebaut ist. Damit hast du deinen konstanten Pointer Datentyp, den du > in ein Array aufnehmen kannst. Aber wenn irgendwie möglich, willst du > die Datentypprüfung des Compiler nicht abschalten. Mit void Pointern und > wildem rumgecaste tust du aber genau das. Unions haben den unschönen Nachteil, dass immer mindestens der Platz des größten im union liegenden Elements gebraucht wird. Möchte man nicht unbedingt immer.
Gast
#4264532
Karl H. schrieb: > Linda schrieb: >> P.S. Nur weil du es nicht verstehst ist es noch lange nicht gepfuscht. > > Du hast ja auch erst jetzt erläutert was der Sinn der Sache ist. Was mich darin bestärkt, Fragen nur noch beantworten, wenn die Absicht im ersten Posting vollständig und klar erklärt ist - wie es übrigens auch in der Netiquette steht. Man bekommt immer dumme Antworten, egal ob man nun den einfachsten Fall annimmt und eine dazu passende Antwort gibt oder ob man nachfragt.
Gast
#4264566
> Müsste da nicht zumindest noch ein separater Zeiger auf den Inhalt drin > sein? Nein, ich hätte den Pointer auf die Struktur aus dem Eröffnungspost in einen Pointer auf diese Struktur gecastet und sobald ich anhand der id weiß welche Struktur das war wieder zurückcasten. Da das erste Element immer gleich aligned sein muss sollte das auch funktionieren. > Was mich darin bestärkt, Fragen nur noch beantworten, wenn die Absicht > im ersten Posting vollständig und klar erklärt ist Oder du beantwortest die Frage beantworten ohne lang zu versuchen irgendetwas hineinzuinterpretieren. Wenn ich wissen will ob ich den Cast so machen darf, dann ist das (in diesem Fall) mit einem einfachen "Ja", ab besten mit Begründung wie zum Beispiel dem Zitat aus dem C-Standard, vollständig beantwortet. Linda schrieb: > Oder du beantwortest die Frage beantworten ohne lang zu versuchen > irgendetwas hineinzuinterpretieren. dann könnten wir mindestens 80% aller Fragen nicht beantworten. Linda schrieb: > Oder du beantwortest die Frage beantworten ohne lang zu versuchen > irgendetwas hineinzuinterpretieren. Kennst du diesen Dialog? A: "Haben Sie die Uhrzeit?" B: "Ja", und geht weiter. Ein Forenklassiker: Es kommt recht oft vor, dass die falsche Frage gestellt wird. Also jemand eine scheinbar fast fertige Lösung im Kopf hat, zu der nur eine Kleinigkeit fehlt, und danach wird nun gefragt. Dann aber partout nicht akzeptieren will, dass er völlig auf dem Holzweg ist und der richtige Weg ein paar Abzweige früher abging. Und nun stinksauer wird, wenn man vorsichtig versucht, darauf hinzuweisen. Deshalb ist es recht nützlich, etwas Kontext mitzuliefern. Damit die Frage einen Sinn ergibt. Denn meistens erntet man mit der Frage "ist Blau lauter als Gelb" bloss dumme Gesichter. Casts sind zwar Teil der Sprache, aber man sollte sich darüber klar sein, dass man sich damit selbst Fallstricke spannt. So kann der Optimizer für Überraschungen sorgen, weil der Compiler davon ausgehen darf, dass Pointer auf verschiedene Typen nicht auf das gleiche Objekt zeigen. Mir scheint, was hier zu implementieren versucht wird, wäre in einer union besser untergebracht als in Casts.
Gast
#4264632
Linda schrieb: >> Müsste da nicht zumindest noch ein separater Zeiger auf den > Inhalt drin >> sein? > Nein, ich hätte den Pointer auf die Struktur aus dem Eröffnungspost in > einen Pointer auf diese Struktur gecastet und sobald ich anhand der id > weiß welche Struktur das war wieder zurückcasten. Da das erste Element > immer gleich aligned sein muss sollte das auch funktionieren. Das verstößt wiederum gegen die strict aliasing rule. Verschiedene structs sind verschiedene Typen und Zeiger verschiedener Typen dürfen nur in Ausnahmefällen auf die gleiche Speicherstelle zeigen. Und damit wird dir der Optimierer einen Bug einbauen wenn du es am wenigsten erwartest. Ausnahme ist alles was im Standard als "Compatible Type" bezeichnet wird. Deine beiden struct sind zwar jeweils compatible zu uint8_t, aber trotzdem nicht zueinander. Structs sind nur zueinander compatible wenn du wirklich das gleiche hast. (Gleicher Name, gleiche Member) Vgl. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf 6.2.7 Absatz 1 (tag meint den Namen eines structs und erlaubt typedefs) 6.7.2.2 (Dort ist compatible zu uint8_t beschrieben, schonmal hier zitiert) http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html Mein Vorschlag wäre entweder die von Karl Heinz vorgeschlagene Methode
oder
und dann ein Array von message zu speichern.
Gast
#4264669
> struct message > { > uint8_t id; > void* data; > }; Das würde sich von der ursprünglichen Idee mit dem Array aus void-Pointern auch nicht sehr unterscheiden, ich glaube ich werde dabei bleiben. > lern erstmal deutsch und dann erst C. Dir würde eine Lektion in Groß- und Kleinschreibung auch nicht schaden, wie war das nochmal mit dem Glashaus und den Steinen? Linda schrieb: >> Was mich darin bestärkt, Fragen nur noch beantworten, wenn die Absicht >> im ersten Posting vollständig und klar erklärt ist > Oder du beantwortest die Frage beantworten ohne lang zu versuchen > irgendetwas hineinzuinterpretieren. Wenn ich wissen will ob ich den Cast > so machen darf, dann ist das (in diesem Fall) mit einem einfachen "Ja", > ab besten mit Begründung wie zum Beispiel dem Zitat aus dem C-Standard, > vollständig beantwortet. Es bleibt dabei aber das ungute Gefühl, daß da was falsch läuft, weil das Vorhaben an sich schon unsinnig klingt. Wie die Frage: "Wie kann ich Billard-Kugeln so abflachen, daß sie aufeinander stehen bleiben?" Man kann sicher eine passende Antwort finden, aber es bleibt die Frage im Hinterkopf, wozu um alles in der Welt der Fragesteller Billardkugeln stapeln will und was er danach mit den bearbeiteten "Kugeln" noch anfangen kann. Wenn man nachfragt, lautet die Antwort dann z.B., daß derjenige sie einfach nur lagern will und nicht den Platz hat, um sie nebeneinander zu legen. Das eigentliche Problem ist also nicht die Bearbeitung der Kugeln zum Stapeln, sondern die Lagerung unter bestimmten Platzbedingungen. Der vermeintliche Lösungsweg, der dann zur Frage geführt hat, ist bereits unsinnig. Vergleichbarse erlebt man hier im Forum praktisch täglich. Daher: Immer von Anfang an das eigentliche Problem posten und nicht eine Frage, die schon auf einem kreativ erdachten Lösungsansatz basiert, denn dieser Ansatz kann schon der falsche sein. Ich finde auch nicht, daß man den Leuten hier übelnehmen sollte, wenn sie nicht einfach nur stur wie eine Maschine die Frage beantworten, sondern auch mal die Sinnhaftigkeit in Frage stellen, wenn diese zweifelhaft ist.
Gast
#4264906
@ Rolf Magnus die Beste Antwort bis jetzt ;) Ano schrieb: > Mein Vorschlag wäre entweder die von Karl Heinz vorgeschlagene Methode >
> oder >
> und dann ein Array von message zu speichern.
Ich halte beide Vorschläge für suboptimal. Wie bereits oben erwähnt,
wird bei der Verwendung einer Union, in die man sämtliche Daten
klatscht, der maximal notwendige Platz verschwendet.
Die Idee mit der Union ist ja schon richtig, aber da gehören nicht die
Daten, sondern Pointer auf die Daten rein.
Also:
Dann kann man sauber über fooptr, barptr usw. auf die einzelnen Daten zugreifen, ohne einen Cast zu bemühen. Und es ist sehr ähnlich zu Lindas Datenstruktur mit dem void-Pointer. Im Speicher sollte es sogar gleich aussehen. Die Programm-Effizienz ist auch dieselbe. Frank M. schrieb: > Die Idee mit der Union ist ja schon richtig, aber da gehören nicht die > Daten, sondern Pointer auf die Daten rein. Dann brauchst du aber noch eine Speicherverwaltung, und malloc() wird hier ja oft gescheut wie der Teufel das Weihwasser. ;-) Hängt natürlich davon ab: wenn die Elemente alle so einigermaßen gleich groß sind, wird man der Einfachheit (des Codes) wegen u. U. lieber die union-Variante bevorzugen. Jörg W. schrieb: > Frank M. schrieb: >> Die Idee mit der Union ist ja schon richtig, aber da gehören nicht die >> Daten, sondern Pointer auf die Daten rein. > > Dann brauchst du aber noch eine Speicherverwaltung, und malloc() > wird hier ja oft gescheut wie der Teufel das Weihwasser. ;-) Nun, wenn man sich das eingehende Posting anschaut, liegen die Daten offenbar (wie auch immer diese entstanden sind) bereits im Speicher vor. Wenn Linda selber void-Pointer verwenden will, scheint sie für die eigentlichen Daten ja schon eine Lösung zu haben. Wenn ich das richtig verstehe, gehts nur noch um die Verwaltung der Daten und nicht mehr um die Daten an sich. Die union über die Pointer sorgt letztendlich nur dafür, dass Linda sich die Casts spart. Das nimmt ihr dann der Compiler ab und der erzeugte Code ist exakt derselbe. > Hängt natürlich davon ab: wenn die Elemente alle so einigermaßen > gleich groß sind, wird man der Einfachheit (des Codes) wegen u. U. > lieber die union-Variante bevorzugen. Ja, natürlich. Aber wenn Linda die Daten schon im Speicher hat, warum dann jetzt alles umorganisieren? Kann natürlich sein, dass ich mich irre. Dann fehlt da aber eine entscheidende Info. Antwort schreibenBitte melde dich an, um einen Beitrag zu schreiben. |
Anzeige
|