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

MasterLee

  • Beiträge: 49
    • Profil anzeigen
Gespeichert
Die Überschrift sollte eigentlich werden
„cast von Zeiger auf nicht Zeiger und umgekehrt in C unter spezieller Berücksichtigung der Segmentierung im 80386 Protected mode“

Man könnte würde man in seinen Betriebssystem extensiver nutzen Zeiger in der Form Selector+Offset verwenden. Der Nachteil wäre nun das sich die Konstruktion nicht mehr ohne weiteres casten ließe. Die Frage die sich mir nun stellt würden dadurch größere Probleme entstehen wenn die möglichkeit Zeiger in andere Typen zu casten entfallen würde?.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
Nichts, was sich nicht lösen ließe. Andere Programmiersprachen, die keine Zeiger haben, wie zum Beispiel Java, kommen ja auch ohne Casts gut zurecht. Ansonsten gibts es immer noch als Behelfsmöglichkeit unions und 64-Bit-Datentypen, sofern unterstützt. Du kannst dir ja mal Open Watcom anschauen. Der kann das, soweit ich weiß.
« Letzte Änderung: 24. January 2012, 23:47 von Jidder »
Dieser Text wird unter jedem Beitrag angezeigt.

Dimension

  • Beiträge: 155
    • Profil anzeigen
Gespeichert
Meinst du
struct {
    unsigned short seg;
    unsigned int off;
};
, oder
#define PACK_POINTER(seg, off) ((seg) & 0xFFFF | (off) << 16)(Keine Garantie gegen Overflows)

32-Bit-Zeiger bis 4gb sind doch immer auch 32-Bit-int, 64-Bit-Zeiger dagegen 64-Bit-int. Das Datensegment steht in DS :lol:.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
32-Bit-Zeiger bis 4gb sind doch immer auch 32-Bit-int, 64-Bit-Zeiger dagegen 64-Bit-int. Das Datensegment steht in DS :lol:.
Das sind Near Pointer, die nur in das aktuelle Segment zeigen können. Wenn du einen Far Pointer willst, hat der im Protected Mode 48 Bit (16-Bit-Selektor plus 32-Bit-Offset) und im Long Mode vermutlich keinen Sinn. ;)
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
@Dimension: Wenn das auf mich bezogen war, dann meinte ich:

union pointer {
  void __far *ptr;
  struct raw {
    unsigned int offset;
    unsigned short segment;
  } raw;
};

void __far *maybe_make_farpointer(unsigned short seg, unsigned long off) {
  union pointer f;
  f.raw.offset = off;
  f.raw.segment = seg;
  return f.ptr;
}

bzw.
#define MAYBE_MAKE_FARPOINTER(segment, offset) ((void __far *)((offset) + (((unsigned long long)(segment)) << 32)))
Aber es kommt sicherlich in beiden Fällen auf den Compiler an, ob das tatsächlich funktioniert. (Das heißt der Code ist auch nur Freistil.) Ich habe die Frage so interpretiert, dass MasterLee darüber nachdenkt einen Compiler zu schreiben oder anzupassen. Im Fall von Open Watcom (und DOS-Compilern) ist das allerdings so, dass diese Casts auch direkt vom Compiler unterstützt werden, und die Makros MK_FP, FP_OFF und FP_SEG verwendet werden sollten, um zwischen den Darstellungen zu konvertieren.

Die Reihenfolge beim Ablegen von far-Pointern im Speicher sollte übrigens erst Offset (d.h. niederwertige Bytes), dann Segment sein. Das ist notwendig, wenn man auf Zeiger casten will, bzw. diese Werte direkt interpretieren will.
« Letzte Änderung: 25. January 2012, 13:10 von Jidder »
Dieser Text wird unter jedem Beitrag angezeigt.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
Die Reihenfolge beim Ablegen von far-Pointern im Speicher sollte übrigens erst Offset (d.h. niederwertige Bytes), dann Segment sein. Das ist notwendig, wenn man auf Zeiger casten will, bzw. diese Werte direkt interpretieren will.
Gibt es einen guten Grund, das zu tun? Ich würde es für wichtiger halten, die Pointer so anzuordnen, wie der Prozessor es für einen m16:32-Operanden erwartet, und ich glaube, da kommt das Segment erst.
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 Offset kommt zuerst. Im Intel Manual unter 4.3 Pointer Data Types gibt es ein Bild dazu. Keine Ahnung ob das ermöglichen soll Far-Pointer auch als Near-Pointer interpretieren zu können, oder ob es etwas damit zu tun hat, dass x86 Little-Endian ist. Aber eins von beiden nutze ich als Eselsbrücke.
Dieser Text wird unter jedem Beitrag angezeigt.

Dimension

  • Beiträge: 155
    • Profil anzeigen
Gespeichert
@Jidder: btw: priorityof("<<") < priorityof("+")
Sorry korigiert.
« Letzte Änderung: 27. January 2012, 01:03 von Dimension »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
Kannst du das genauer ausdrücken? Klammern kann man da nicht weglassen, falls du darauf hinauswillst.
Dieser Text wird unter jedem Beitrag angezeigt.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
Ah, in Volume 1 steht das. Ich hätte fast nicht gedacht, dass das auch für was gut ist. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Dimension

  • Beiträge: 155
    • Profil anzeigen
Gespeichert
hm? http://en.cppreference.com/w/cpp/language/operator_precedence

Edit: Moment... meine Referenz sagt mir zu den Gruppen 1..3 und 15..16 was anderes - Kein Anspruch auf Korrektheit.
OT: dafür dass mein Phone mir seit 2 stunden einen fehlenden Akku anzeigt, bin ich doch sehr real im Internet unterwegs.
« Letzte Änderung: 27. January 2012, 01:04 von Dimension »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
a+b<<c entspricht (a+b)<<c und nicht a+(b<<c), was dein Link ebenfalls besagt. Deswegen sind die Klammern notwendig.
Dieser Text wird unter jedem Beitrag angezeigt.

Dimension

  • Beiträge: 155
    • Profil anzeigen
Gespeichert
Ich glaube die Frage vom OP war eher, ob sein Compiler Standard-konform ist, wenn jeder Cast von bzw. zu far-pointern einen Error auslösen würde. Ob dies Sinn machen würde weiss ich nicht, aber 2 Werte in einem int wären schonmal nicht gegen Overflow geschützt.
void __far *ptr;
int num;
num = (int) ptr; // ERROR 1
ptr = (void __far *) num; // ERROR 2


Edit: Thema verfehlt, neuer Code.


Die letzten 3 Kommentare bitte ignorieren/ unter "irrelevant" abspeichern.
« Letzte Änderung: 27. January 2012, 01:25 von Dimension »

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
@Dimension: Dein Code macht überhaupt keinen Sinn (void*-Pointer dereferenzieren?)

Standard-C garantiert beim casten afaik sowieso nur, dass in (u)intptr_t gecastet werden kann und wieder zurück (und das Ergebnis davon gleich dem Zeiger vorher ist). Ich würde vermuten, dass es nicht garantiert ist, dass man auf den Integer verändert und dann zurückcastet, bin mir aber in keinster Weise sicher. Problematischer ist, dass bei far-Zeigern, dass zwei Zeiger ungleich sein können aber trotzdem den gleichen Speicher adressieren. Unabhängig davon ist ständiges wechseln der Segmentregister eine bescheidene Idee.
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

erik.vikinger

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


Die Frage die sich mir nun stellt würden dadurch größere Probleme entstehen wenn die möglichkeit Zeiger in andere Typen zu casten entfallen würde?.
Ja, ich denke schon. Streng genommen ist jedes C-Programm das Pointer in beliebige Nicht-Pointer oder andersherum castet nicht so wirklich konform zum C-Standard. Es wird zwar zugesichert das man einen Pointer nach (u)intptr_t und wieder zurück casten kann und das Ergebnis beim == Vergleich mit dem Original true ergibt aber mehr auch nicht. Selbst die kleinste Rechenoperation darf den in (u)intptr_t gespeicherten Pointer irreparabel zerstören. Gerade das Rechnen mit FAR-Pointern ergibt ja auch keinen Sinn (wenn man mal vom x86-Real-Mode absieht in dem ja jeder FAR-Pointer auf eine eineindeutige lineare 20Bit-Adresse zurückzuführen ist, zuzüglich den Gate-A20-Spielchen natürlich) da unterschiedliche Selectoren ja bedeuten das die jeweiligen Offsets in völlig verschiedenen und unabhängigen Welten eine Bedeutung haben und Selectoren eigentlich keinen mathematischen Bezug zueinander haben.

Bei der Frage welcher Umfang an Rechenmöglichkeiten und Castingmöglichkeiten bei FAR-Pointern überhaupt einen Sinn ergibt bin ich zu dem Ergebnis gekommen das sämtliche Rechnenoperationen mit 2 Pointern grundsätzlich nicht erlaubt sein sollten und nur die einfachen Strichrechenoperationen zwischen einem Pointer und einem Integer (der die selbe Größe wie der Offset-Teil des Pointers haben muss und dann auch nur das Offset im Pointer beeinflusst) überhaupt erlaubt werden dürfen, jegliche Form von Selector-Arithmetik mach IMHO bei richtiger dynamischer Segmentierung keinen Sinn. Casts dürften dann auch nur mit (u)intptr_t erlaubt sein (und das sollte eine vom Compiler angebotene Struktur sein für die keinerlei Rechenoperatoren überladen sind). Das Problem was ich sehe ist das wenn ein Compiler das wirklich strickt durchsetzt das dann vermutlich doch einige C-Programme plötzlich nicht mehr compilierbar sind. Aber ohne es zu versuchen kann ich auch kaum abschätzen wie viele Programme das wirklich trifft, ich habe da die Hoffnung das dies vor allem Treiber und andere systemnahe Programme trifft deren Portierung eh von nur begrenztem Nutzen ist oder eben spezielle Librarys die man dann neu schreiben müsste aber nach Außen das selbe Interface behalten.


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

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
Wenn du das machst, hast du kein C mehr.

C garantiert, dass uintptr_t ein Integertyp ist, also kann man damit rechnen. Und es erlaubt, zwei Pointer voneinander abzuziehen, wenn sie ins selbe Objekt zeigen.
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
Ich würde mal vermuten man darf dann auch die Differenz der beiden Pointer wieder zum kleineren addieren und dieser muss dann mit dem größeren übereinstimmen, oder?
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
Ja, natürlich. Das ist Pointerarithmetik, alles ordentlich definiert. Du kannst auch eins mehr wieder dazuaddieren, ohne dass es undefiniert wird. Schwierig wird es erst, wenn du mit einem gecasteten Integer rumrechnest und dann wieder einen Pointer draus machst.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

erik.vikinger

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


Wenn du das machst, hast du kein C mehr.
Deswegen vermute ich ja das dann einige Programme nicht mehr compilierbar sind. Die Frage die mich da noch interessiert ist: wie viele Programme trifft das wirklich? Wenn ich an meine eigenen C/C++-Programme denke (privat und beruflich) würde ich sagen das diese auch weiterhin noch compilierbar sind, einfach weil die IMHO solche Pointer-Tricks nicht benutzen (mir fallen auch nur wenige Fälle ein wo man sowas überhaupt benötigt und die liegen alle nicht im Bereich normaler C-Programme sondern eher sowas wie malloc/free oder die Speicherverwaltung in einem OS-Kernel).

C garantiert, dass uintptr_t ein Integertyp ist, also kann man damit rechnen.
Okay, aber welche Arten von Rechenoperationen sind denn damit sinnvoll? Mal ganz davon abgesehen das so ein Typ bei mir 48 oder 80 Bit groß wäre (was sich nur etwas umständlich auf die normalen Integer-Rechenbefehle der CPU abbilden lässt) so ergeben doch bei (u)intptr_t nur die selben sehr beschränkten Varianten einen Sinn wie bei Pointern direkt. Auch glaube ich nicht das es allzu viel real existierenden C-Code gibt in dem mit (u)intptr_t tatsächlich komplexe Rechenoperationen durchgeführt werden so das IMHO hier nur ein geringes Potential besteht ein vorhandenes Programm nicht mehr compilierbar werden zu lassen.

Und es erlaubt, zwei Pointer voneinander abzuziehen, wenn sie ins selbe Objekt zeigen.
Ist denn auch definiert was passieren soll wenn die zwei Pointer nicht ins selbe Objekt zeigen? Grundsätzlich gebe ich Dir ja Recht das es möglich sein sollte die Differenz zwischen 2 Pointern zu ermitteln (ebenso wie auch < , > , <= und >= Vergleiche zwischen 2 Pointern möglich sein sollten) aber bei einem segmentiertem Speichermodell ist das eben nicht immer möglich, nebst dessen dass das Ergebnis ein Integer mit der selben Größe wie der Offset-Teil sein muss und kein Pointer. Da ich mir es als recht schwierig vorstelle zur Laufzeit die entsprechenden Bedingungen zu prüfen und eine Verletzung auch korrekt zu behandeln bin ich daher dafür solche Operationen lieber gleich ganz zu verbieten. Ich denke auch nicht dass das eine nennenswerte Einschränkung darstellt, um z.B. die Offsets von Struktur-Membern zu ermitteln gibt es doch sicher auch andere Wege.


und dieser muss dann mit dem größeren übereinstimmen, oder?
Wimre sichert der C-Standard bei Pointer-Arithmetik gar nichts zu, das läuft alles unter "implementierungsspezifisch".


Du kannst auch eins mehr wieder dazuaddieren, ohne dass es undefiniert wird.
Ist dann auch definiert ob an dieser neuen Adresse auch tatsächlich Speicher vorhanden ist? Wimre nein.

Schwierig wird es erst, wenn du mit einem gecasteten Integer rumrechnest und dann wieder einen Pointer draus machst.
Solange man direkt bei Pointern bleibt hat der Compiler ja zumindest eine theoretische Chance das Gesamtconstruct zu verstehen und etwas sinnvolles daraus zu bauen. Das Problem an FAR-Pointern in simplen Integern ist eben das Selector-Arithmetik eigentlich keinen Sinn ergibt.


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

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
C garantiert, dass uintptr_t ein Integertyp ist, also kann man damit rechnen.
Okay, aber welche Arten von Rechenoperationen sind denn damit sinnvoll?
Wiederholt durch 16 teilen und jeweils den Rest auf den Bildschirm ausgeben? ;)

Zitat
Und es erlaubt, zwei Pointer voneinander abzuziehen, wenn sie ins selbe Objekt zeigen.
Ist denn auch definiert was passieren soll wenn die zwei Pointer nicht ins selbe Objekt zeigen?
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). Wenn du daraus also einen Segfault machen willst, ist das vermutlich erlaubt.

Zitat
Da ich mir es als recht schwierig vorstelle zur Laufzeit die entsprechenden Bedingungen zu prüfen und eine Verletzung auch korrekt zu behandeln bin ich daher dafür solche Operationen lieber gleich ganz zu verbieten. Ich denke auch nicht dass das eine nennenswerte Einschränkung darstellt, um z.B. die Offsets von Struktur-Membern zu ermitteln gibt es doch sicher auch andere Wege.
Was ist daran schwierig? Unterschiedliche Segmente -> Segfault. Gleiche Segmente -> Differenz der Offsets wird berechnet.

Abgesehen davon, dass ich keine andere Methode kenne, die Offsets zu berechnen (wobei ich mir nichtmal sicher bin, dass die normale definiert ist - an NULL liegt schließlich kein so ein Objekt), bedeutet jede Einschränkung, dass du das Ergebnis nicht mehr C nennen kann. Du musst dich also entscheiden, ob du C willst oder nicht. "Fast C" heißt "nicht C", das ist eine neue Sprache, für die du erstmal eine ordentliche Spec schreiben solltest.

Zitat
Wimre sichert der C-Standard bei Pointer-Arithmetik gar nichts zu, das läuft alles unter "implementierungsspezifisch".
Pointerarithmetik ist genau definiert, solange du innerhalb von einem Objekt (oder einem Element nach Arrayende) bleibst.

Zitat
Ist dann auch definiert ob an dieser neuen Adresse auch tatsächlich Speicher vorhanden ist? Wimre nein.
Wie groß dein Array war, musst du natürlich immer noch selber wissen. ;)

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.

Zitat
Solange man direkt bei Pointern bleibt hat der Compiler ja zumindest eine theoretische Chance das Gesamtconstruct zu verstehen und etwas sinnvolles daraus zu bauen. Das Problem an FAR-Pointern in simplen Integern ist eben das Selector-Arithmetik eigentlich keinen Sinn ergibt.
Ob der Typ ein Pointer oder ein Integer ist, spielt eigentlich nicht wirklich eine Rolle. Die interessante Beschränkung ist, dass man innerhalb eines Objekts bleiben muss. Zu char* konvertieren kannst du nämlich und darauf dann fröhlich rumrechnen und hinterher wieder in einen anderen Pointer zurückcasten. 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.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

 

Einloggen