Autor Thema: cast von Zeiger auf nicht Zeiger und umgekehrt in C unter spezieller Berücksi...  (Gelesen 14319 mal)

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
Hallo,


Wiederholt durch 16 teilen und jeweils den Rest auf den Bildschirm ausgeben? ;)
Etwas das von printf mit %p erledigt wird und da die libc ja auch zur Plattform passen muss ist es IMHO kein Problem da drin auch mal einen kleinen Trick zu benutzen der nicht so ganz sauber ist (in meiner Heap-Implementierung baue ich auch Pointer recht frei zusammen und wieder auseinander, ich sehe da auch kein Problem darin das mein Code nicht portabel ist).

Nicht, dass ich wüsste (ich habe aber auch keine explizite Aussage gefunden, dass es undefiniert ist, der Standard scheint einfach nichts dazu zu sagen).
Also ich persönlich halte mich da lieber an die Devise: "alles was nicht ausdrücklich und explizit zugesichert wird ist auch nicht zugesichert, fertig".

Wenn du daraus also einen Segfault machen willst, ist das vermutlich erlaubt.
Ich denke auch dass das so erlaubt ist aber dafür würde ich immer zusätzlichen Code benötigen und das möchte ich auch wieder nicht, daher möchte ich das lieber gleich ganz verbieten um so den geringsten Aufwand zu haben.

Was ist daran schwierig? Unterschiedliche Segmente -> Segfault. Gleiche Segmente -> Differenz der Offsets wird berechnet.
Vielleicht mache ich mir das einfach auch nur zu schwierig. Eines meiner Probleme ist das wenn eine solche Differenz später wieder zu einem Pointer dazu addiert wird das es dann zu einem Überlauf kommen kann und der wird, falls er direkt im Speicherzugriffsbefehl passiert, von der CPU mit einer Exception beantwortet, es kann also passieren das die Funktionsfähigkeit des selben C-Codes vom Optimierungsgrad des Compilers abhängt und das sollte IMHO lieber vermieden werden.

Abgesehen davon, dass ich keine andere Methode kenne, die Offsets zu berechnen
offsetof(), das ist IMHO auch deswegen nötig weil ja das tatsächliche Speicherlayout in Strukturen auch wieder implementierungsspezifisch ist. Das meistens dahinter im Endeffekt nur der selbe Hack steckt den Du auch direkt benutzen würdest ist da erst mal egal, immerhin muss dafür derjenige gerade stehen der die libc für die konkrete Plattform gebaut hat und der sollte genau wissen was geht und was nicht.

"Fast C" heißt "nicht C", das ist eine neue Sprache, für die du erstmal eine ordentliche Spec schreiben solltest.
Das ist mir schon klar, aber eine komplette Spec werde ich da trotzdem nicht schreiben sondern eher eine präzise Auflistung der Unterschiede. Auf der anderen Seite scheint der C-Standard genügend "Interpretationsspielraum" zu bieten das ich wohl trotzdem sagen kann das mein System 100% konform ist, aber da kommen wir dann in Bereiche die sonst eher bei der Interpretation von Bibel, Koran usw. benutzt werden.

Pointerarithmetik ist genau definiert, solange du innerhalb von einem Objekt (oder einem Element nach Arrayende) bleibst.
Sicher? Das würde ich arg bezweifeln aber ich lese (morgen) am besten noch mal genau nach. Ich kann mich jedenfalls nicht erinnern das im C-Standard sowas wie "( (&TYPE[i+2]) - (&TYPE[i+1]) ) == sizeof(TYPE)" als true zugesichert wird. Um wie viel ein Pointer bei (&TYPE)++ wirklich inkrementiert wird ist wimre auch implementierungsspezifisch und muss ebenfalls nicht zwangsläufig mit sizeof(TYPE) übereinstimmen.

Wie groß dein Array war, musst du natürlich immer noch selber wissen. ;)
Ja klar, aber was ist mit Padding und ähnlichen Dingen? Sowas darf der Compiler frei nach Befinden oder gewünschtem Optimierungsgrad entscheiden. Arrays sind nicht zwangsläufig kompakt, wimre noch nicht einmal char-Arrays.

Aber wenn du ein char a[42] hast und p = &a[12], q = &a[14], dann ist genau definiert, dass  p + 2 * (q - p) = &a[16] ist, zum Beispiel.
Hm, okay, trotzdem würde ich das nicht als guten C-Code bezeichnen, in einem Array sollte man IMHO nur mit dem Index zugreifen und nicht mit Pointer-Arithmetik.

In der Praxis ist das genau dasselbe wie mit einem Integer herumzurechnen. Der theoretische Unterschied ist, dass es mit char* definiert und mit uintptr_t implementation defined.
Sicher? So wie ich den C-Standard verstanden habe wird kein exakter und eineindeutiger Zusammenhang zwischen Pointern für Objekte und deren numerische Darstellung als Integer zugesichert. Theoretisch könnte eine CPU ja auch Speicheradressen in Gray-Code darstellen (bei einem Pointer++ müsste der Compiler dann einen anderen Rechenbefehl benutzen als für Integer++) und echter Standard-konformer C-Code müsste trotzdem funktionieren. Das die Differenz von 2 Pointern als Integer den numerischen Wert 12 hat bedeutet nicht das die 2 Objekte (auf die die beiden Pointer zeigen) auch wirklich 12 Bytes von einander entfernt im Speicher liegen, dass das trotzdem auf allen heute existierenden CPUs zutrifft ist noch keine Zusicherung.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
Meinst du denn, dass existierende Programme überhaupt die Fähigkeiten deiner Architektur ausnutzen können? Ich glaube nämlich nicht, dass du was anderes machen kannst als je ein Segment für Code, Stack und Daten anzulegen. Die Programme haben ja keine Möglichkeit mitzuteilen, wie die Daten logisch zusammenhängen. Existierende Programme können also die Vorteile der Segmentierung vermutlich nicht nutzen. Macht es überhaupt Sinn diese Programme mit mehr als diesem "Legacy-Modus" á la x86 zu unterstützen?

Pointerarithmetik ist genau definiert, solange du innerhalb von einem Objekt (oder einem Element nach Arrayende) bleibst.
Sicher? Das würde ich arg bezweifeln aber ich lese (morgen) am besten noch mal genau nach. Ich kann mich jedenfalls nicht erinnern das im C-Standard sowas wie "( (&TYPE[i+2]) - (&TYPE[i+1]) ) == sizeof(TYPE)" als true zugesichert wird. Um wie viel ein Pointer bei (&TYPE)++ wirklich inkrementiert wird ist wimre auch implementierungsspezifisch und muss ebenfalls nicht zwangsläufig mit sizeof(TYPE) übereinstimmen.
6.5.6 Additive Operators 9.

Deine Notation ist etwas ungenau. Es gilt:
TYPE foo[123];
int i;
&foo[i + 2] - &foo[i + 1] = (i + 2) - (i + 1) = 1
(uintptr_t)&foo[i + 2] - (uintptr_t)&foo[i + 1] = sizeof(foo[0])


Ja klar, aber was ist mit Padding und ähnlichen Dingen? Sowas darf der Compiler frei nach Befinden oder gewünschtem Optimierungsgrad entscheiden. Arrays sind nicht zwangsläufig kompakt, wimre noch nicht einmal char-Arrays.
Ein Array ist kompakt. (6.2.5 Types 20) Wenn du ein struct foo { int x; char y; } hast, kann der Compiler das auf 8 Bytes vergrößern, d.h. sizeof(struct foo) = 8, also auch Elemente, die nicht in Arrays sind, können mehr Platz einnehmen als nötig.
Dieser Text wird unter jedem Beitrag angezeigt.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
Hallo,


Meinst du denn, dass existierende Programme überhaupt die Fähigkeiten deiner Architektur ausnutzen können?
Äh, ja, also schon oft. Ich denke das alle Programme zumindest von dem Gewinn an Sicherheit mehr oder weniger profitieren können. Ein absolut fehlerfreies Programm (in dem es gar keine Buffer-Overflows usw. gibt) profitiert natürlich nicht davon aber auf wie viele Programme trifft das denn zu? Vom Geschwindigkeitsvorteil durch das (meistens) abgeschaltete Paging profitieren natürlich auch alle Programme.

Ich glaube nämlich nicht, dass du was anderes machen kannst als je ein Segment für Code, Stack und Daten anzulegen.
Das ist aber schon mehr als im klassischen Flat-Memory gemacht wird, dort liegt absolut alles (sogar der OS-Kernel) in einem einzigen Adressraum. Bei mir ist z.B. der OS-Kernel aus dem User-Mode mit wirklich gar keinem Pointer erreichbar (es gibt keine 48/80 Bit-Kombination die den Kernel aus dem User-Mode adressieren kann). Auch muss ein Programm bei mir ja nicht auf 4 Segmente (Code, Const, Data und Stack) beschränkt bleiben, ich habe z.B. vor das der Heap sich über viele Segmente verteilt. Mein malloc() wird sicher nicht jedes mal ein neues Segment erstellen (das wäre totale Speicherverschwendung, schon weil so ein Segment immer ganze Pages belegt und auch etwas an Verwaltungsdaten kostet) aber ich will für jede benutzte Objekt-Größe ein neues Segment erstellen und dieses Segment wie ein Array aus Elementen dieser Größe verwalten (die Verwaltung als Array ist auch ziemlich effizient implementierbar so das malloc und free bei mir ordentlich schnell werden).

Die Programme haben ja keine Möglichkeit mitzuteilen, wie die Daten logisch zusammenhängen.
Der logische Zusammenhang von verschiedenen Objekten braucht IMHO auch keinen Einfluss darauf haben wie diese Objekte über die Segmente verteilt sind (es wird ja überall mit FAR-Pointern gearbeitet), das einzigste was ich aber in jedem Fall vermeiden will ist das einzelne Objekte/Arrays über mehrere Segmente verteilt werden müssen (so wie im x86-RM bei Objekten/Arrays mit mehr als 64 kB) so das meine Segmente schon recht groß werden können.

Macht es überhaupt Sinn diese Programme mit mehr als diesem "Legacy-Modus" á la x86 zu unterstützen?
IMHO ja. Und selbst wenn es nur 5 Segmente sind (also der Heap nur ein einziges Segment benutzt) ist es ja schon deutlich besser als alles was Flat-Memory auch nur theoretisch anbieten kann. Dazu kommt das spätestens bei Multithreading noch mehr Vorteile zum tragen kommen: jeder Stack hat sein eigenes Segment und kann auch unabhängig von anderen Stacks wachsen so das man sich nicht schon vorher überlegen muss wie man eine potentiell unbekannte Anzahl von Stacks in den einen virtuellen Adressraum unterbringen will. Nebst dessen das für die Stacks auch immer virtueller Speicher verschwendet wird da ja nicht vorhersehbar ist welcher der Stacks eventuell etwas mehr wächst als andere. Bei Flat-Memory muss man die Größe der Stacks immer beschränken um eine vernünftige Koexistenz mehrerer Stack in einem virtuellen Adressraum sicherstellen zu können. Auch kann man bei Flat-Memory die Stack niemals 100% zuverlässig gegeneinander absichern wie ich da http://forum.lowlevel.eu/index.php?topic=2224.msg25456#msg25456 schon mal dargelegt hatte. Von daher bin ich in jedem Fall der Meinung das auch alle existierenden Programme von Segmentierung einen Nutzen haben können.

6.5.6 Additive Operators 9.
Danke, lese ich mir nachher mal durch.

&foo[i + 2] - &foo[i + 1] = (i + 2) - (i + 1) = 1
Also hier fehlt irgendwo noch "sizeof(TYPE)" weil der Abstand der Indizes ja nur 1 beträgt aber der Abstand der Pointer der Objekt-Größe entsprechen sollte.

(uintptr_t)&foo[i + 2] - (uintptr_t)&foo[i + 1] = sizeof(foo[0])
Also das ist IMHO auf jeden Fall falsch. Solange Du die Differenz aus zwei Pointern bildest muss auch eine Integer-Zahl (vom Type ptrdiff_t) bei raus kommen die auch wieder als Offset zu gebrauchen sein muss aber wenn Du beide Pointer vorher nach uintptr_t umwandelst dann ist das IMHO nicht mehr gewährleistet. Was ist wenn eine CPU mal tatsächlich eine andere Binärdarstellung für Adressen als für normale (Integer-)Zahlen benutzt? Dann würde Dein Code nicht mehr funktionieren, der C-Standard schreibt auch explizit nicht vor das für Integer das Zweierkomplement zu benutzen ist so das es jedem frei steht hier was anderes zu nutzen.

Ein Array ist kompakt.
Im Hinblick auf sizeof(Type) (also inklusive Padding) ja, da hast Du recht. Auch muss das Padding immer gleich sein, egal ob das Objekt in einem Array ist oder alleine benutzt wird oder Teil einer (größeren) Struktur ist. Wenn das nicht gewährleistet wäre könnte man solche Objekte ja nicht kopieren da dann eventuell mal zu viele oder zu wenige Bytes kopiert werden würden. Sowas wie "( (&TYPE[i+2]) - (&TYPE[i+1]) ) == sizeof(TYPE)" wird also doch immer true ergeben müssen, ebenso wie das ein (&TYPE)++ auch immer um sizeof(Type) Bytes vorrücken muss, da hab ich wohl gestern nicht zu Ende gedacht.

Aber das hier der Vergleich am Ende true ergibt ist IMHO nicht zugesichert :*TYPE p1,p2;
p1 = &type;
p2 = (*TYPE)(((uintptr_t)p1) + sizeof(TYPE));  // eines der Probleme hier ist das die Größe von uintptr_t und size_t (der Return-Type von sizeof) nicht identisch sein muss so dass das Ergebnis schon deswegen ungültig sein könnte
p1++;
if (p1 == p2)  // hier kommt nur dann true raus wenn für Pointer und für Integer die selbe Binärdarstellung in der CPU benutzt wird und das sichert der C-Standard nicht zu
So wie ich den C-Standard verstehe ist es nicht zugesichert das Rechenoperationen die direkt mit Pointern durchgeführt werden und Rechenoperationen die mit Integer-Werten von Pointern durchgeführt werden das selbe Ergebnis erzeugen. C-Code der auf sowas basiert ist IMHO nicht 100% konform und wenn der auf meiner Plattform nicht mehr funktioniert dann ist das ein Fehler im C-Code und nicht im Compiler. Deswegen möchte ich diese Dinge ja am liebsten gleich vom Compiler ablehnen lassen damit ich nicht später irgendwelchen Merkwürdigkeiten nachstellen muss, nebst dessen das ich für händische Pointer-Arithmetik (vor allem mit Integern) auch absolut keine Notwendigkeit sehe (sowas kann man immer auch anders machen, von ein paar Spezialfällen innerhalb der Speicherverwaltung mal abgesehen).


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Dimension

  • Beiträge: 155
    • Profil anzeigen
Gespeichert
Mein malloc() wird sicher nicht jedes mal ein neues Segment erstellen (das wäre totale Speicherverschwendung, schon weil so ein Segment immer ganze Pages belegt und auch etwas an Verwaltungsdaten kostet) aber ich will für jede benutzte Objekt-Größe ein neues Segment erstellen und dieses Segment wie ein Array aus Elementen dieser Größe verwalten (die Verwaltung als Array ist auch ziemlich effizient implementierbar so das malloc und free bei mir ordentlich schnell werden).

Wenn du deinen Arrays od. Buffern dann noch einen Header mit 2 Feldern für Elementgröße und Anzahl der Elemente spendierst und diese Angaben für jeden Pointer hast, wirst du absolut keine Overflows mehr brauchen (siehe: fat pointer / http://en.m.wikipedia.org/wiki/Cyclone_(programming_language)) Um diese dynamisch skalieren zu können ohne zu relokalisieren fallen mir, nur Vektortabellen ein.



Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
Mein malloc() wird sicher nicht jedes mal ein neues Segment erstellen (das wäre totale Speicherverschwendung, schon weil so ein Segment immer ganze Pages belegt und auch etwas an Verwaltungsdaten kostet) aber ich will für jede benutzte Objekt-Größe ein neues Segment erstellen und dieses Segment wie ein Array aus Elementen dieser Größe verwalten
Genau darauf wollte ich hinaus. malloc() hat ja keinen anderen Parameter als die Größe. Ich finde das macht das Argument, dass Buffer Overflows erkennbar sind, hinfällig. Buffer Overflows, die nur Objekte der gleichen Größe betreffen, können immer noch gleichwahrscheinlich auftreten, und haben die selben fatalen Folgen. Das mit den Stacks für die Threads ist zwar ganz nett, aber im Zeitalter von 64-Bit-Adressräumen haut mich das auch nicht mehr vom Hocker. Ich finde Segmentierung muss die strengen Garantien bezüglich der Limits auch an die Anwendungen weiterreichen können, sonst ist es nur Paging mit variabler Page-Größe und riesigem, statischem TLB (d.h. Deskriptortabelle) in der CPU. (Und die Anzahl der unentdeckten Buffer Overflows reduziert sich um ~1/n, wobei n die Anzahl der Objekte pro Segment ist.) Das heißt nicht, dass ich eine Lösung dafür habe, wie man existierenden Programme das beibringen kann, sondern ganz im Gegenteil glaube ich, dass der Versuch C-Programme nachträglich mit Garantien auszustatten, die nicht von C gewährt werden, zum Scheitern verurteilt ist.

Zu der Pointer-Sache: Ich glaube du vertauscht da etwas. Das erste Beispiel hat genau das Verhalten, das du im zweiten erwartest, und umgekehrt.
« Letzte Änderung: 29. January 2012, 14:38 von Jidder »
Dieser Text wird unter jedem Beitrag angezeigt.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
Ich denke auch dass das so erlaubt ist aber dafür würde ich immer zusätzlichen Code benötigen und das möchte ich auch wieder nicht, daher möchte ich das lieber gleich ganz verbieten um so den geringsten Aufwand zu haben.
Den zusätzlichen Code brauchst du genau dann, wenn jemand Pointerarithmetik benutzt. Du kannst dir natürlich auch die Arbeit machen, jeden Code zu patchen, der das tut, weil er sonst nicht kompiliert. Halte ich aber insgesamt gesehen für ineffizienter als ein, zwei Instruktionen mehr zu generieren.

Zitat
Eines meiner Probleme ist das wenn eine solche Differenz später wieder zu einem Pointer dazu addiert wird das es dann zu einem Überlauf kommen kann und der wird, falls er direkt im Speicherzugriffsbefehl passiert, von der CPU mit einer Exception beantwortet, es kann also passieren das die Funktionsfähigkeit des selben C-Codes vom Optimierungsgrad des Compilers abhängt und das sollte IMHO lieber vermieden werden.
Wieso sollte es zu einem Überlauf kommen, solange man innerhalb eines Objekts bleibt? Und wenn man es nicht tut, ist das undefined behaviour und eine Exception ist absolut in Ordnung.

Zitat
offsetof(), das ist IMHO auch deswegen nötig weil ja das tatsächliche Speicherlayout in Strukturen auch wieder implementierungsspezifisch ist. Das meistens dahinter im Endeffekt nur der selbe Hack steckt den Du auch direkt benutzen würdest ist da erst mal egal, immerhin muss dafür derjenige gerade stehen der die libc für die konkrete Plattform gebaut hat und der sollte genau wissen was geht und was nicht.
Und jetzt rate mal, wie offsetof implementiert ist? ;)

Das ist einfach ein Makro, das die Pointerarithmetik versteckt. Wenn das Makro also nicht mehr kompiliert, hast du auch kein offsetof. Als Funktion, die man dazulinken könnte, geht das nicht.

Zitat
Das ist mir schon klar, aber eine komplette Spec werde ich da trotzdem nicht schreiben sondern eher eine präzise Auflistung der Unterschiede. Auf der anderen Seite scheint der C-Standard genügend "Interpretationsspielraum" zu bieten das ich wohl trotzdem sagen kann das mein System 100% konform ist, aber da kommen wir dann in Bereiche die sonst eher bei der Interpretation von Bibel, Koran usw. benutzt werden.
Ohne weiter vom Thema abweichen zu wollen, vermute ich von dir, dass du dich weder mit Bibel noch Koran näher auseinandergesetzt hast und da womöglich Dinge missverstehst... (wobei ich vom Koran selber keine Ahnung habe)

Mein Vergleich wären eher juristische Spitzfindigkeiten über die Interpretation von Gesetzen - und man redet ja nicht umsonst von language lawyers.

Zitat
Sicher? Das würde ich arg bezweifeln aber ich lese (morgen) am besten noch mal genau nach. Ich kann mich jedenfalls nicht erinnern das im C-Standard sowas wie "( (&TYPE[i+2]) - (&TYPE[i+1]) ) == sizeof(TYPE)" als true zugesichert wird. Um wie viel ein Pointer bei (&TYPE)++ wirklich inkrementiert wird ist wimre auch implementierungsspezifisch und muss ebenfalls nicht zwangsläufig mit sizeof(TYPE) übereinstimmen.
Das ist falsch. Lies es nach, es ist alles wohldefiniert.

Zitat
Aber wenn du ein char a[42] hast und p = &a[12], q = &a[14], dann ist genau definiert, dass  p + 2 * (q - p) = &a[16] ist, zum Beispiel.
Hm, okay, trotzdem würde ich das nicht als guten C-Code bezeichnen, in einem Array sollte man IMHO nur mit dem Index zugreifen und nicht mit Pointer-Arithmetik.
Genau so ist die Pointerarithmetik definiert: Die Differenz zweier Pointer ist die Differenz der Indizes im Array. Und dass a nichts anderes als *(a + i) (und damit auch i[a]) ist, dürftest du ja selbst wissen.

Zitat
In der Praxis ist das genau dasselbe wie mit einem Integer herumzurechnen. Der theoretische Unterschied ist, dass es mit char* definiert und mit uintptr_t implementation defined.
Sicher? So wie ich den C-Standard verstanden habe wird kein exakter und eineindeutiger Zusammenhang zwischen Pointern für Objekte und deren numerische Darstellung als Integer zugesichert. Theoretisch könnte eine CPU ja auch Speicheradressen in Gray-Code darstellen (bei einem Pointer++ müsste der Compiler dann einen anderen Rechenbefehl benutzen als für Integer++) und echter Standard-konformer C-Code müsste trotzdem funktionieren. Das die Differenz von 2 Pointern als Integer den numerischen Wert 12 hat bedeutet nicht das die 2 Objekte (auf die die beiden Pointer zeigen) auch wirklich 12 Bytes von einander entfernt im Speicher liegen, dass das trotzdem auf allen heute existierenden CPUs zutrifft ist noch keine Zusicherung.
Ja. Jetzt hast du genau beschrieben, warum es implementation defined ist, wenn man auf einem uintptr_t rumrechnet. Wenn man es auf einem char* tut (und innerhalb eines Objekts bleibt, wie immer), dann ist es genau definiert und der Compile müsste auf deiner gray-codierten CPU mit dreiwertiger Logik und signed-Darstellung mit Basis -2 eben Zusatzaufwand treiben, um das gewünschte Ergebnis herzustellen.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
Zitat
offsetof(), das ist IMHO auch deswegen nötig weil ja das tatsächliche Speicherlayout in Strukturen auch wieder implementierungsspezifisch ist. Das meistens dahinter im Endeffekt nur der selbe Hack steckt den Du auch direkt benutzen würdest ist da erst mal egal, immerhin muss dafür derjenige gerade stehen der die libc für die konkrete Plattform gebaut hat und der sollte genau wissen was geht und was nicht.
Und jetzt rate mal, wie offsetof implementiert ist? ;)
Das ist einfach ein Makro, das die Pointerarithmetik versteckt. Wenn das Makro also nicht mehr kompiliert, hast du auch kein offsetof. Als Funktion, die man dazulinken könnte, geht das nicht.
Da wäre ich mir nicht so sicher, gcc hat dafür ein builtin, siehe hier. Aber keine Ahnung warum genau man das brauch, ich versteh auch nicht was es da so für Randfälle für den "member-designator" geben kann, die man eventuell mit Pointerarithmetik alleine nicht hinbekommt.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
Ah, nett. tyndur benutzt ein handgestricktes Makro. Könnte vielleicht deswegen sein, weil Pointerarithmetik wie gesagt nur innerhalb von Objekten klar definiert ist und man bei offsetof() keins hat, sondern auf (struct foo*) NULL zurückgreift. Andererseits weiß gcc ja auch, was er implementiert, insofern wäre da kein Builtin nötig. Irgendwie komisch.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Dimension

  • Beiträge: 155
    • Profil anzeigen
Gespeichert
Aber das hier der Vergleich am Ende true ergibt ist IMHO nicht zugesichert :*TYPE p1,p2;
p1 = &type;
p2 = (*TYPE)(((uintptr_t)p1) + sizeof(TYPE));  // eines der Probleme hier ist das die Größe von uintptr_t und size_t (der Return-Type von sizeof) nicht identisch sein muss so dass das Ergebnis schon deswegen ungültig sein könnte
p1++;
if (p1 == p2)  // hier kommt nur dann true raus wenn für Pointer und für Integer die selbe Binärdarstellung in der CPU benutzt wird und das sichert der C-Standard nicht zu
So wie ich den C-Standard verstehe ist es nicht zugesichert das Rechenoperationen die direkt mit Pointern durchgeführt werden und Rechenoperationen die mit Integer-Werten von Pointern durchgeführt werden das selbe Ergebnis erzeugen. C-Code der auf sowas basiert ist IMHO nicht 100% konform und wenn der auf meiner Plattform nicht mehr funktioniert dann ist das ein Fehler im C-Code und nicht im Compiler. Deswegen möchte ich diese Dinge ja am liebsten gleich vom Compiler ablehnen lassen damit ich nicht später irgendwelchen Merkwürdigkeiten nachstellen muss, nebst dessen das ich für händische Pointer-Arithmetik (vor allem mit Integern) auch absolut keine Notwendigkeit sehe (sowas kann man immer auch anders machen, von ein paar Spezialfällen innerhalb der Speicherverwaltung mal abgesehen).
id main(){
char * p1 = 0;
short * p2 = 0;
printf("%p %p %p %p", p1, p2, p1 + 1, p2 + 1);
}
Ergibt bei mir:
(nil) (nil) 0x1 0x2
Habe keinen gcc zur Hand, da mein Android ungerootet ist, und nehme codepad.org: http://codepad.org/fIVT8TDA

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
Und was genau soll das zeigen, Dimension?  :?
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

Dimension

  • Beiträge: 155
    • Profil anzeigen
Gespeichert
Korrigier mich wenn ich falsch liege, aber ging es nicht primär um Arithmetik mit Pointern und Integern? Ob GCC das spezifische Verhalten von GCC nun auf eine genaue Vorgabe in der Spezifikation hinweist, weiss ich nicht. Die Frage is doch wohl viel eher, ob GCC, Visual  Studio, Borland, Watcom, etc. Standardkonform compilieren.

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
Nein, die Frage ist was genau standardkonform überhaupt bedeutet. Und da hilft ein Test mit Compiler X auf Platform Y halt garnichts.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

MasterLee

  • Beiträge: 49
    • Profil anzeigen
Gespeichert
Manche APIs definieren Felder die sowohl für direkte Werte als auch für Zeiger genutzt werden. Nehmen wir z.B. folgendes Beispiel:
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
...
case WM_MOVE:
int xPos = (int)(short) LOWORD(lParam);   // horizontal position
int yPos = (int)(short) HIWORD(lParam);   // vertical position
...
case WM_CREATE:
CREATESTRUCT *pCS=(CREATESTRUCT*)lParam;
...
Das würde auf jeden Fall ein Problem machen da hier abhängig davon was LPARAM überhaupt ist entweder ein cast von einem Zeiger auf Zahl und umgekehrt von Zahl auf Zeiger(irgendwie muss es auch reingekommen sein) durchgeführt wird.

Das würde nicht funktionieren wenn ein Zeiger komplexer definiert würde zum Beispiel als Produkt aus Selektor und Offset. Da aber die Firma die obiges verbrochen hat dafür bekannt hat nicht gerade Standard konform zu arbeiten wollte ich wissen ob es generell erlaubt ist ein Zeiger nach Integer oder umgekehrt zu casten.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
Casts von Integer auf Pointer und umgekehrt sind im C-Standard wohldefiniert. Steht aber auch mehrfach in diesem Thread.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
Das würde nicht funktionieren wenn ein Zeiger komplexer definiert würde zum Beispiel als Produkt aus Selektor und Offset. Da aber die Firma die obiges verbrochen hat dafür bekannt hat nicht gerade Standard konform zu arbeiten wollte ich wissen ob es generell erlaubt ist ein Zeiger nach Integer oder umgekehrt zu casten.

LPARAM ist ja long, und da hat unter 16-Bit-Windows ein Selektor-Offset-Paar reingepasst. Wenn du long als 64-Bit-Integer definierst, kannst du deinen 16:32-far-pointer da auch problemlos reinstecken. Wenn jetzt 64-Bit-far-pointer oder so nutzen willst, dann funktioniert der Code natürlich nicht mehr. Aber ich würde auch nicht unbedingt einen Port von Windows auf meinen Compiler/Architektur/was-auch-immer-du-im-Sinn-hast anstreben. Wenn du hingegen so eine API nur grob imitieren willst, dann kannst du einfach einen Parameter als Pointer statt als Integer vorsehen. Casts vom Offset-Teil des Pointers auf Integer sollten ja immer gehen.
Dieser Text wird unter jedem Beitrag angezeigt.

 

Einloggen