Was bedeutet das: Ein Zeiger auf void wird niemals gleich einem anderen Zeiger sein?

Lesezeit: 9 Minuten

Benutzer-Avatar
InQusitive

Einer meiner Freunde wies auf den zweiten Aufzählungspunkt aus “Verstehen und Verwenden von C-Zeigern – Richard Reese, O’Reilly-Veröffentlichungen” hin, und ich konnte das nicht erklären Erste Satz daraus. Was vermisse ich?

Zeiger auf ungültig

Ein Zeiger auf void ist ein Allzweckzeiger, der verwendet wird, um Verweise auf beliebige Datentypen zu halten. Ein Beispiel für einen Zeiger auf void ist unten dargestellt:

void *pv;

Es hat zwei interessante Eigenschaften:

  • Ein Zeiger auf void hat die gleiche Darstellung und Speicherausrichtung wie ein Zeiger auf char.
  • Ein Zeiger auf void wird niemals gleich einem anderen Zeiger sein. Jedoch wurden zwei void-Zeiger a zugewiesen NULL Wert wird gleich sein.

Dies ist mein Code, nicht aus dem Buch, und alle Zeiger haben denselben Wert und sind gleich.

#include <stdio.h>

int main()
{
  int a = 10; 
  int *p = &a; 
  void *p1 = (void*)&a;
  void *p2 = (void*)&a;

  printf("%p %p\n",p1,p2);
  printf("%p\n",p);
  
  if(p == p1) 
    printf("Equal\n");
  if(p1 == p2) 
    printf("Equal\n");  
}

Ausgabe:

 0x7ffe1fbecfec 0x7ffe1fbecfec
 0x7ffe1fbecfec
 Equal
 Equal

  • Sieht so aus, als wäre das Buch schlecht geschrieben (oder zumindest dieser Teil davon). Was es wahrscheinlich bedeutet, dass a void pointer ist niemals gleich einem pointer, der auf ein anderes objekt zeigt. Aber ich kann nur raten.

    – Adrian Maulwurf

    1. August 2021 um 13:24 Uhr


  • Die Behauptung klingt falsch. Vielleicht haben sie eine gültige Bedeutung im Sinn, aber es ist mir nicht klar, was es sein könnte. Ein Zeiger auf void wird als universeller Zeigertyp verwendet (zumindest für Zeiger auf Daten im Gegensatz zu Funktionszeigern). Er kann jedem anderen Datenzeiger entsprechen, je nachdem, wie diese Zeiger definiert wurden.

    – Tom Karzes

    1. August 2021 um 13:25 Uhr

  • Buch ist einfach falsch

    – Matt Timmermans

    1. August 2021 um 13:31 Uhr

  • Da ist ein Errata dazu heißt es: “Ein Zeiger auf void wird niemals gleich einem anderen Zeiger sein.” Sollte lauten: „Ein Zeiger auf void wird niemals gleich einem anderen Zeiger sein zu entwerten” Begründung: Einem Zeiger auf void kann der Wert eines nicht-void-Zeigers zugewiesen werden. Bin mir aber nicht sicher, ob es dadurch wesentlich besser wird.

    – GSerg

    1. August 2021 um 13:35 Uhr


  • @EOF Sie denken wahrscheinlich an ein anderes Buch, C-Zeiger beherrschen von Robert J. Traister. Dieser ist in der Tat berühmt dafür, dass er fast völlig falsch ist und von jemandem geschrieben wurde, der kein Verständnis für C oder Programmierung im Allgemeinen hat.

    – Konrad Rudolf

    1. August 2021 um 14:16 Uhr

TL/DR: Das Buch ist falsch.

Was vermisse ich?

Nichts, soweit ich sehen kann. Sogar die in Kommentaren präsentierte Erratum-Version …

Ein Zeiger auf void wird niemals gleich einem anderen Zeiger auf void sein.

… wird von der C-Sprachspezifikation einfach nicht unterstützt. Soweit sich der Autor auf die Sprachspezifikation stützt, wäre der relevante Text Absatz 6.5.9/6:

Zwei Zeiger sind genau dann gleich, wenn beide Nullzeiger sind, beide Zeiger auf dasselbe Objekt (einschließlich eines Zeigers auf ein Objekt und ein Unterobjekt an seinem Anfang) oder Funktion sind, beide Zeiger auf eins nach dem letzten Element desselben Arrays sind Objekt, oder einer ist ein Zeiger auf einen hinter dem Ende eines Array-Objekts und der andere ist ein Zeiger auf den Anfang eines anderen Array-Objekts, das zufällig unmittelbar auf das erste Array-Objekt im Adressraum folgt.

void ist ein Objekttyp, wenn auch ein “unvollständiger”. Zeiger auf void die gültig und nicht null sind, sind Zeiger auf Objekte, und sie werden unter den durch die Spezifikation ausgedrückten Bedingungen miteinander verglichen. Üblicherweise werden solche Zeiger erhalten, indem ein Objektzeiger eines anderen (Zeiger-)Typs in konvertiert wird void *. Das Ergebnis einer solchen Konvertierung zeigt immer noch auf dasselbe Objekt wie der ursprüngliche Zeiger.

Meine beste Vermutung ist, dass das Buch die Spezifikation falsch interpretiert, um anzuzeigen, dass Zeiger auf void nicht als Zeiger auf Objekte interpretiert werden sollten. Obwohl es Sonderfälle gibt, die nur für Zeiger auf gelten voidbedeutet dies nicht, dass allgemeine Bestimmungen, die für Objektzeiger gelten, nicht auch für void-Zeiger gelten.

  • Siehe 6.3.2.3 Zeiger. Es spricht über die Konvertierung in einen anderen Typ und zurück, aber nie darüber, was passiert, wenn Sie zwei Zeiger auf X in zwei Zeiger auf Y und konvertieren Vergleichen Sie sie als Zeiger auf Y, wenn es tatsächlich kein Y gibt. In den Vergleichsbeschreibungen steht immer “und wieder zurück”. Es gibt kein Objekt vom Typ void dort. Zum char*, die Zeiger sprechen davon, dass sie auf das erste Zeichen in der Byte-Darstellung zeigen; das ist da. Dies mag ein Versehen im Standard sein, aber es sieht so aus, als würde es verhindern, dass Sie Zeiger vergleichen können zum falschen Typ legal sein…

    – Yakk – Adam Nevraumont

    2. August 2021 um 16:34 Uhr


  • Erstens, @Yakk-AdamNevraumont, keine C-Implementierung, von der ich so viel weiß, wie ein Auge darauf, zwei void-Zeiger miteinander auf Gleichheit zu vergleichen. Zweitens hat 6.3.2.3 wenig damit zu tun. Ein Vergleich von zwei void-Zeigern erfüllt die dritte Alternative in der Bedingung „einer der folgenden soll gelten“ in 6.5.9/2 (wo ich vermute, dass das Buch seinen Hauptfehler macht). Dann sagt 6.5.9/5, dass wenn einer der verglichenen Zeiger ein Zeiger auf ist void dann wird der andere für den Vergleich in denselben konvertiert (ein No-Op in dem Spezialfall, in dem beide Void-Zeiger sind) […]

    – Johannes Bollinger

    2. August 2021 um 17:07 Uhr

  • […] Das wäre sinnlos, wenn das resultierende Zeigerpaar nicht gleich miteinander verglichen werden könnte. In diesem Fall würde die Spezifikation direkt sagen, dass die beiden Zeiger ungleich sind. Die einzig plausible Interpretation von 6.5.9/6 ist also, dass solche Zeigerumwandlungen zu Zeigern auf dasselbe Objekt führen, sodass es möglich ist, dass sie gleich miteinander verglichen werden. Dieser Typ void * vermittelt keine Informationen über die Arten dieser Objekte ist unerheblich. Wir können (und C tut es) offensichtlich anhand der Zeigerwerte wissen, ob die Objekte, auf die sie zeigen, dasselbe Objekt sind.

    – Johannes Bollinger

    2. August 2021 um 17:11 Uhr


  • Eines der interessanten Dinge an C ist die Unterstützung für seltsame Architekturen. Betrachten Sie den segmentierten Modus auf dem 80286 – es ist möglich, zwei Zeiger auf dieselbe Adresse mit völlig unterschiedlichen Darstellungen zu haben, aufgrund der Segment + Offset-Natur eines Zeigers. Diese Zeiger würden wahrscheinlich nicht gleich vergleichen.

    – Markieren Sie Lösegeld

    3. August 2021 um 15:21 Uhr

  • @MarkRansom, ich erkenne an, dass es grundsätzlich möglich und in der Praxis beobachtbar ist, dass zwei Zeiger auf dasselbe Objekt unterschiedliche Darstellungen haben. Und die Ja wirklich Interessant ist, dass C verlangt, dass solche Zeigerpaare gleich sind, ungeachtet des Unterschieds in der Darstellung.

    – Johannes Bollinger

    3. August 2021 um 17:42 Uhr

C 2018 6.5.9 6 sagt:

Zwei Zeiger sind genau dann gleich, wenn beide Nullzeiger sind, beide Zeiger auf dasselbe Objekt (einschließlich eines Zeigers auf ein Objekt und ein Unterobjekt an seinem Anfang) oder Funktion sind, beide Zeiger auf eins nach dem letzten Element desselben Arrays sind Objekt, oder einer ist ein Zeiger auf einen hinter dem Ende eines Array-Objekts und der andere ist ein Zeiger auf den Anfang eines anderen Array-Objekts, das zufällig unmittelbar auf das erste Array-Objekt im Adressraum folgt.

Angenommen, wir haben:

int a;
void *p0 = &a;
void *p1 = &a;

Dann wenn p0 und p1 „auf dasselbe Objekt zeigen“, p0 == p1 muss als wahr gewertet werden. Man könnte den Standard jedoch so interpretieren, dass a void * zeigt auf nichts, solange es a ist void *; Es enthält nur die Informationen, die erforderlich sind, um es wieder in seinen ursprünglichen Typ zu konvertieren. Aber wir können diese Interpretation testen.

Betrachten Sie die Spezifikation, dass zwei Zeiger gleich sind, wenn sie auf ein Objekt und ein Unterobjekt an seinem Anfang zeigen. Das bedeutet, dass gegeben int a[1];, &a == &a[0] sollte als wahr gewertet werden. Allerdings können wir nicht richtig verwenden &a == &a[0]weil die Beschränkungen für == für Zeiger erfordern, dass die Operanden auf kompatible Typen zeigen oder dass einer oder beide a sind void * (mit Qualifikationsmerkmalen wie const erlaubt). Aber a und a[0] haben weder kompatible Typen noch sind sie es void.

Die einzige Möglichkeit für eine vollständig definierte Situation, in der wir Zeiger auf dieses Objekt und sein Unterobjekt vergleichen, besteht darin, dass mindestens einer der Zeiger in eines der beiden umgewandelt wurde void * oder auf einen Zeiger auf einen Zeichentyp (weil diese bei Konvertierungen besonders behandelt werden). Wir könnten den Standard so interpretieren, dass er nur letzteres meint, aber ich halte das für die vernünftigere Interpretation void * ist enthalten. Die Absicht ist das (void *) &a == (void *) &a[0] ist als Vergleich eines Zeigers auf das Objekt zu interpretieren a zu einem Zeiger auf das Objekt a[0] obwohl sich diese Zeiger im Formular befinden void *. Also diese beiden void * als gleich vergleichen sollten.

  • Man könnte den Standard jedoch so interpretieren, dass a void * zeigt auf nichts, solange es a ist void *. Meiner Meinung nach Die Definition von void unterstützt diese Auslegung nicht. “[A]n unvollständiger Objekttyp, der nicht vervollständigt werden kann” kann immer noch adressiert/gezeigt werden. Es kann nur nicht dereferenziert oder über a zugegriffen werden void *.

    – Andreas Henle

    1. August 2021 um 15:09 Uhr


  • @AndrewHenle: Ich glaube nicht, dass Sie dieser Antwort eigentlich nicht zustimmen. Die Antwort befürwortet nicht die Interpretation, die Sie zitieren. im Gegenteil, der gesamte Rest der Antwort widmet sich dem Zeigen, dass diese Interpretation nicht wirklich sinnvoll wäre.

    – ruach

    2. August 2021 um 4:08 Uhr

Der folgende Abschnitt aus diesem C11-Standardentwurf widerlegt die aufgestellte Behauptung vollständig (sogar mit der in den ‘Errata’ erwähnten Klarstellung, im Kommentar von GSerg).

6.3.2.3 Zeiger

1 Ein Zeiger auf void kann in oder von einem Zeiger auf einen beliebigen Objekttyp konvertiert werden. Ein Zeiger auf einen beliebigen Objekttyp kann in einen Zeiger auf umgewandelt werden void und wieder zurück; das Ergebnis soll mit dem ursprünglichen Zeiger verglichen werden.

Oder dieser Abschnitt aus demselben Normentwurf:

7.20.1.4 Integer-Typen, die Objektzeiger enthalten können

1 Der folgende Typ bezeichnet einen vorzeichenbehafteten Integer-Typ mit der Eigenschaft, auf die jeder gültige Zeiger zeigt void kann in diesen Typ konvertiert und dann wieder in einen Zeiger auf konvertiert werden voidund das Ergebnis wird mit dem ursprünglichen Zeiger verglichen:

intptr_t

  • Das spricht aber nicht wirklich das genaue Problem an, um das es geht, oder? Das sagt das Ergebnis der Konvertierung wieder zurück sollte mit dem ursprünglich typisierten Zeiger gleich sein, nicht dass er gleich sein sollte, während er noch vorhanden ist void*.

    – GSerg

    1. August 2021 um 13:51 Uhr


  • @GSerg Ich habe den zweiten Auszug hinzugefügt, der spezifischer für zwei Werte von ist void* Typ.

    – Adrian Maulwurf

    1. August 2021 um 13:52 Uhr

Ein Zeiger ist nur eine Adresse im Speicher. Zwei beliebige Zeiger sind gleich, wenn sie NULL sind oder auf dieselbe Adresse zeigen. Sie können immer weiter darüber reden, wie das mit der Sprache der Strukturen, Gewerkschaften und so weiter geschehen kann. Aber am Ende ist es einfach nur Algebra mit Speicherorten.

  • Ein Zeiger auf void wird niemals gleich einem anderen Zeiger sein. Jedoch wurden zwei void-Zeiger a zugewiesen NULL Wert wird gleich sein.

Seit NULL in dieser Aussage erwähnt wird, halte ich es für einen Tippfehler. Die Aussage sollte so etwas wie sein

  • Ein Zeiger auf void wird niemals gleich sein NULL Zeiger. Jedoch wurden zwei void-Zeiger a zugewiesen NULL Wert wird gleich sein.

Das bedeutet, dass jeder gültige Zeiger auf void niemals gleich ist NULL Zeiger.

1381580cookie-checkWas bedeutet das: Ein Zeiger auf void wird niemals gleich einem anderen Zeiger sein?

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

Privacy policy