Warum bedeutet Leer in C nicht Leer?


25

In stark typisierten Sprachen wie Java und C # scheint void(oder Void) als Rückgabetyp für eine Methode Folgendes zu bedeuten:

Diese Methode gibt nichts zurück. Nichts. Keine Rückkehr. Sie erhalten von dieser Methode nichts.

Was wirklich seltsam ist, ist, dass in C voidals Rückgabetyp oder sogar als Methodenparametertyp bedeutet:

Es könnte wirklich alles sein. Sie müssten den Quellcode lesen, um dies herauszufinden. Viel Glück. Wenn es ein Zeiger ist, sollten Sie wirklich wissen, was Sie tun.

Betrachten Sie die folgenden Beispiele in C:

void describe(void *thing)
{
    Object *obj = thing;
    printf("%s.\n", obj->description);
}

void *move(void *location, Direction direction)
{
    void *next = NULL;

    // logic!

    return next;
}

Offensichtlich gibt die zweite Methode einen Zeiger zurück, der per Definition beliebig sein kann.

Da C älter als Java und C # ist, warum haben diese Sprachen die voidBedeutung "nichts" angenommen, während C sie als "nichts oder irgendetwas (wenn ein Zeiger)" verwendete?


138
In Ihrem Fragentitel und Text wird darüber gesprochen, voidwährend das Codebeispiel void*etwas völlig anderes verwendet.

4
Beachten Sie auch, dass Java und C # dasselbe Konzept haben. Sie nennen es einfach Objectin einem Fall, um eine eindeutige Unterscheidung zu treffen.
Mooing Duck

4
@Mephy sind sie nicht gerade stark getippt? Meinen Sie mit C # unter Verwendung von Duck Typing den dynamicTyp, der selten verwendet wird?
Axarydax

9
@Mephy: Letztes Mal habe ich in Wikipedia elf einander widersprechende Bedeutungen für "stark typisiert" aufgelistet . Es ist daher bedeutungslos zu sagen, dass "C # nicht stark typisiert ist".
Eric Lippert

8
@LightnessRacesinOrbit: Nein, der Punkt ist, dass es keine maßgebliche Definition des Begriffs gibt. Wikipedia ist keine verschreibungspflichtige Ressource, die angibt, was die richtige Bedeutung eines Begriffs ist. Es ist eine beschreibende Ressource. Die Tatsache, dass es elf verschiedene widersprüchliche Bedeutungen beschreibt, ist ein Beweis dafür, dass der Begriff nicht in Gesprächen verwendet werden kann, ohne ihn sorgfältig zu definieren . Wenn Sie etwas anderes tun, sind die Chancen sehr gut, dass die Gesprächspartner nicht miteinander reden, sondern aneinander vorbeigehen.
Eric Lippert

Antworten:


58

Das Schlüsselwort void(kein Zeiger) bedeutet in diesen Sprachen "nichts". Das ist konsequent.

Wie Sie bemerkt haben, void*bedeutet "Zeiger auf alles" in Sprachen, die rohe Zeiger unterstützen (C und C ++). Dies ist eine unglückliche Entscheidung, denn wie Sie bereits sagten, bedeutet voiddies zwei verschiedene Dinge.

Ich konnte den historischen Grund für die Wiederverwendung nicht finden void, um "nichts" und "irgendetwas" in verschiedenen Kontexten zu bedeuten, C tut dies jedoch an mehreren anderen Stellen. Zum Beispiel statichat unterschiedliche Zwecke in unterschiedlichen Kontexten. Es ist offensichtlich ein Präzedenzfall in der Sprache C, Schlüsselwörter auf diese Weise wiederzuverwenden, unabhängig davon, was man von der Praxis hält.

Java und C # unterscheiden sich genug, um eine saubere Pause zu machen, um einige dieser Probleme zu beheben. Java und „sicher“ C # auch nicht zulassen , dass rohe Zeiger und tun muß , nicht einfach C - Kompatibilität (Unsafe C # tut Zeiger erlauben , aber die überwiegende Mehrheit von C # -Code nicht in diese Kategorie fallen). Auf diese Weise können sie Änderungen vornehmen, ohne sich um die Abwärtskompatibilität sorgen zu müssen. Eine Möglichkeit besteht darin, eine Klasse Objectan der Wurzel der Hierarchie einzuführen, von der alle Klassen erben, sodass eine ObjectReferenz dieselbe Funktion erfüllt, void*ohne dass Typprobleme und die Verwaltung des unformatierten Speichers unangenehm sind.


2
Ich werde eine Referenz bereitstellen, wenn ich zu Hause

4
They also do not allow raw pointers and do not need easy C compatibility.Falsch. C # hat Zeiger. Sie sind normalerweise (und sollten es normalerweise sein) ausgeschaltet, aber sie sind da.
Magus

8
voidGründe für die Wiederverwendung : Ein offensichtlicher Grund wäre, die Einführung eines neuen Schlüsselworts zu vermeiden (und möglicherweise vorhandene Programme zu beschädigen). Hätten sie ein spezielles Schlüsselwort (z. B. unknown) eingeführt, void*wäre dies ein bedeutungsloses (und möglicherweise unzulässiges) Konstrukt und unknownwäre nur in Form von legal unknown*.
Jamesdlin

20
Selbst in void *, voidbedeutet „nichts“, nicht „alles“. Sie können a nicht dereferenzieren void *, Sie müssen es zuerst in einen anderen Zeigertyp umwandeln.
oefe

2
@oefe Ich denke, es ist sinnlos, dieses Haar zu spalten, weil voidund void*verschiedene Arten sind (eigentlich voidist "kein Typ"). Es macht genauso viel Sinn, zu sagen, "das intIn int*bedeutet etwas". Nein, sobald Sie eine Zeigervariable deklariert haben, sagen Sie: "Dies ist eine Speicheradresse, die etwas aufnehmen kann." Im Fall von void*sagen Sie: "Diese Adresse enthält etwas, aber aus dem Zeigertyp lässt sich nichts ableiten."

32

voidund void*sind zwei verschiedene Dinge. voidIn C bedeutet genau das Gleiche wie in Java, das Fehlen eines Rückgabewerts. A void*ist ein Zeiger ohne Typ.

Alle Zeiger in C müssen dereferenziert werden können. Wenn Sie eine dereferenziert hätten void*, welchen Typ würden Sie erwarten? Denken Sie daran, dass C-Zeiger keine Informationen zum Laufzeit-Typ enthalten. Daher muss der Typ zur Kompilierungszeit bekannt sein.

In diesem Kontext können Sie eine dereferenzierte Datei nur logisch void*ignorieren. Dies ist genau das Verhalten, das der voidTyp angibt.


1
Ich stimme dir zu, bis du zum "Ignoriere es" -Bit kommst. Es ist möglich, dass das zurückgegebene Objekt Informationen zur Interpretation enthält. Mit anderen Worten, es könnte das C-Äquivalent einer BASIC-Variante sein. "Wenn Sie das bekommen, schauen Sie, um herauszufinden, was es ist - ich kann Ihnen nicht im Voraus sagen, was Sie finden werden".
Floris

1
Oder anders ausgedrückt, ich könnte hypothetisch einen vom Programmierer definierten "Typdeskriptor" in das erste Byte an der durch den Zeiger adressierten Speicherstelle einfügen, der mir sagt, welchen Datentyp ich in den folgenden Bytes erwarten kann.
Robert Harvey

1
Natürlich, aber in C wurde beschlossen, diese Details dem Programmierer zu überlassen, anstatt sie in der Sprache zu backen. Aus sprachlicher Sicht weiß es nichts anderes als zu ignorieren. Dies vermeidet den Aufwand, Typinformationen immer als erstes Byte einzuschließen, wenn Sie sie in den meisten Anwendungsfällen statisch bestimmen können.
Karl Bielefeldt

4
@RobertHarvey ja, aber dann dereferenzieren Sie keinen leeren Zeiger. Sie setzen den leeren Zeiger auf einen Zeichenzeiger und dereferenzieren dann den Zeichenzeiger.
user253751

4
@ Floris nicht gcceinverstanden mit Ihnen. Wenn Sie versuchen, den Wert zu verwenden, gccerstellen Sie eine Fehlermeldung mit der Aufschrift error: void value not ignored as it ought to be.
Kasperd

19

Vielleicht wäre es sinnvoller, sich voidden Rückgabetyp vorzustellen. Dann würde Ihre zweite Methode "eine Methode, die einen untypisierten Zeiger zurückgibt" lesen .


Das hilft. Als jemand, der (endlich!) C nach Jahren in objektorientierten Sprachen lernt, sind Zeiger für mich ein sehr neues Konzept. Es scheint, dass die Rückgabe eines Zeigers in voidetwa *der Rückgabe von Sprachen wie ActionScript 3 entspricht, was einfach "jeder Rückgabetyp" bedeutet.
Naftuli Kay

5
Na ja, voidist keine Wildcard. Ein Zeiger ist nur eine Speicheradresse. Wenn Sie einen Typ vor *"Hier ist der Datentyp, den Sie an der Speicheradresse erwarten können, auf die dieser Zeiger verweist" setzen. Putting voidvor dem* sagt den Compiler „Ich habe den Typen nicht kennen; return mir nur den rohen Zeiger, und ich werde herausfinden , was mit den Daten zu tun , dass es auf.“
Robert Harvey

2
Ich würde sogar sagen, dass a void* Punkt auf eine "Leere" verweist. Ein nackter Zeiger, auf den nichts zeigt. Sie können es trotzdem wie jeden anderen Zeiger setzen.
Robert

11

Lassen Sie uns die Terminologie ein wenig verschärfen.

Aus dem Online-Standard C 2011 :

6.2.5 Typen
...
19 Der voidTyp besteht aus einer leeren Menge von Werten; Es ist ein unvollständiger Objekttyp, der nicht abgeschlossen werden kann.
...
6.3 Konvertierungen
...
6.3.2.2 ungültig

1 Der (nicht vorhandene) Wert eines voidAusdrucks (ein Ausdruck mit Typ void) darf in keiner Weise verwendet werden, und implizite oder explizite Konvertierungen (außer bis void) dürfen nicht angewendet werden ein solcher Ausdruck. Wenn ein Ausdruck eines anderen Typs als void Ausdruck ausgewertet wird , wird sein Wert oder Bezeichner verworfen. (Ein voidAusdruck wird auf seine Nebenwirkungen untersucht.)

6.3.2.3 Zeiger

1 Ein Zeiger aufvoidkann in oder von einem Zeiger auf einen beliebigen Objekttyp konvertiert werden. Ein Zeiger auf einen beliebigen Objekttyp kann in einen Zeiger umgewandelt werden, der ungültig macht und wieder zurückkehrt. Das Ergebnis soll gleich dem ursprünglichen Zeiger sein.

Ein voidAusdruck hat keinen Wert (obwohl er Nebenwirkungen haben kann). Wenn ich eine Funktion definiert habe, die zurückgegeben werden soll void, wie folgt:

void foo( void ) { ... }

dann der Anruf

foo();

bewertet nicht zu einem Wert; Ich kann das Ergebnis nicht zuordnen, da es kein Ergebnis gibt.

EIN Zeiger auf voidist im Wesentlichen ein "generischer" Zeigertyp. Sie können void *jedem anderen Objektzeigertyp einen Wert zuweisen, ohne dass eine explizite Umwandlung erforderlich ist (weshalb alle C - Programmierer Sie anschreien, weil Sie das Ergebnis von umgewandelt haben)malloc ).

Sie können a nicht direkt dereferenzieren void *. Sie müssen es zuerst einem anderen Objektzeigertyp zuweisen, bevor Sie auf das Objekt zugreifen können, auf das verwiesen wird.

voidZeiger werden verwendet, um (mehr oder weniger) generische Schnittstellen zu implementieren; Das kanonische Beispiel ist dasqsort Bibliotheksfunktion, mit der Arrays aller Art sortiert werden können, sofern Sie eine typbezogene Vergleichsfunktion bereitstellen.

Ja, die Verwendung des gleichen Schlüsselworts für zwei verschiedene Konzepte (kein Wert im Vergleich zum generischen Zeiger) ist verwirrend, aber es ist nicht so, als gäbe es keinen Präzedenzfall. statichat mehrere unterschiedliche Bedeutungen in C und C ++.


9

In Java und C # scheint void als Rückgabetyp für eine Methode zu bedeuten: Diese Methode gibt nichts zurück. Nichts. Keine Rückkehr. Sie erhalten von dieser Methode nichts.

Diese Aussage ist richtig. Es ist auch für C und C ++ korrekt.

In C bedeutet void als Rückgabetyp oder sogar als Methodenparametertyp etwas anderes.

Diese Aussage ist falsch. voidAls Rückgabetyp in C oder C ++ bedeutet dies dasselbe wie in C # und Java. Sie verwechseln voidmit void*. Sie sind ganz anders.

Ist das nicht verwirrend voidund void*bedeutet das zwei völlig verschiedene Dinge?

Ja.

Was ist ein Zeiger? Was ist ein Leerzeiger?

Ein Zeiger ist ein Wert, der dereferenziert werden kann . Durch die Referenzierung eines gültigen Zeigers wird ein Speicherort des Typs angegeben, auf den verwiesen wird . Ein Leerzeiger ist ein Zeiger, der keinen bestimmten Typ hat, auf den gezeigt wird. Es muss in einen spezifischeren Zeigertyp konvertiert werden , bevor der Wert dereferenziert wird , um einen Speicherort zu erzeugen .

Sind leere Zeiger in C, C ++, Java und C # gleich?

Java hat keine leeren Zeiger; C # macht. Sie sind die gleichen wie in C und C ++ - ein Zeigerwert, dem kein bestimmter Typ zugeordnet ist, der in einen spezifischeren Typ konvertiert werden muss, bevor er dereferenziert wird, um einen Speicherort zu erzeugen.

Da C älter als Java und C # ist, warum haben diese Sprachen void als "nichts" angenommen, während C es als "nichts oder nichts (wenn ein Zeiger)" verwendete?

Die Frage ist inkohärent, weil sie Unwahrheiten voraussetzt. Lassen Sie uns einige bessere Fragen stellen.

Warum haben Java und C # die Konvention übernommen, voiddie ein gültiger Rückgabetyp ist, anstatt beispielsweise die Visual Basic-Konvention zu verwenden, wonach Funktionen niemals ungültig und Subroutinen immer ungültig sind?

Um Programmierern vertraut zu sein, die aus Sprachen mit voidRückgabetyp zu Java oder C # kommen .

Warum hat C # die verwirrende Konvention übernommen, void*die eine völlig andere Bedeutung hat als void?

Programmierern, die aus Sprachen mit Zeigertyp zu C # kommen, vertraut sein void*.


5
void describe(void *thing);
void *move(void *location, Direction direction);

Die erste Funktion gibt nichts zurück. Die zweite Funktion gibt einen ungültigen Zeiger zurück. Ich hätte diese zweite Funktion als deklariert

void* move(void* location, Direction direction);

Könnte helfen, wenn Sie "nichtig" als "ohne Bedeutung" ansehen. Der Rückgabewert von describeist "ohne Bedeutung". Die Rückgabe eines Wertes aus einer solchen Funktion ist unzulässig, da Sie dem Compiler mitgeteilt haben, dass der Rückgabewert ohne Bedeutung ist. Sie können den Rückgabewert dieser Funktion nicht erfassen, da er ohne Bedeutung ist. Sie können keine Variable vom Typ deklarieren, voidda sie ohne Bedeutung ist. Zum Beispiel void nonsense;ist Unsinn und ist in der Tat illegal. Beachten Sie auch, dass eine Reihe von Leerstellen void void_array[42];ebenfalls Unsinn ist.

Ein Zeiger auf void ( void*) ist etwas anderes. Es ist ein Zeiger, kein Array. Lesen Sie void*als Bedeutung einen Zeiger, der auf etwas "ohne Bedeutung" zeigt. Der Zeiger ist "ohne Bedeutung", was bedeutet, dass es keinen Sinn macht, einen solchen Zeiger zu dereferenzieren. Dies zu versuchen ist in der Tat illegal. Code, der einen ungültigen Zeiger dereferenziert, wird nicht kompiliert.

Also, wenn Sie einen leeren Zeiger nicht dereferenzieren können und keine Reihe von Leerräumen erzeugen können, wie können Sie sie verwenden? Die Antwort ist, dass ein Zeiger auf einen beliebigen Typ in und aus umgewandelt werden kann void*. Wenn Sie einen Zeiger auf void*und einen Zeiger auf den ursprünglichen Typ zurücksetzen, erhalten Sie einen Wert, der dem ursprünglichen Zeiger entspricht. Der C-Standard garantiert dieses Verhalten. Der Hauptgrund dafür, dass Sie in C so viele leere Zeiger sehen, ist, dass diese Funktion eine Möglichkeit bietet, das Ausblenden von Informationen und die objektbasierte Programmierung in einer weitgehend nicht objektorientierten Sprache zu implementieren.

Schließlich sehe der Grund , warum Sie oft moveals deklariert void *move(...)statt , void* move(...)weil neben dem Namen ein Sternchen setzen , anstatt die Art eine sehr gängige Praxis in C ist Der Grund dafür ist , dass die folgende Erklärung Sie in großen Schwierigkeiten bekommen: int* iptr, jptr;Das würden Sie machen Ich glaube, Sie deklarieren zwei Zeiger auf einen int. Du bist nicht. Diese Erklärung macht jptreine inteher als einen Zeiger auf ein int. Die richtige Deklaration lautet: int *iptr, *jptr;Sie können so tun, als ob der Stern zum Typ gehört, wenn Sie nur eine Variable pro Deklarationsanweisung deklarieren. Aber denken Sie daran, dass Sie so tun, als ob.


"Code, der einen Nullzeiger dereferenziert, wird nicht kompiliert." Ich denke, Sie meinen Code, der Dereferenzen, die ein ungültiger Zeiger nicht kompiliert. Der Compiler schützt Sie nicht vor der Dereferenzierung eines Nullzeigers. das ist ein Laufzeitproblem.
Cody Grey

@CodyGray - Danke! Das wurde behoben. Code, der einen Nullzeiger dereferenziert, wird kompiliert (sofern dies nicht der Fall ist void* null_ptr = 0;).
David Hammen

2

Einfach ausgedrückt gibt es keinen Unterschied . Lassen Sie mich einige Beispiele nennen.

Angenommen, ich habe eine Klasse namens Auto.

Car obj = anotherCar; // obj is now of type Car
Car* obj = new Car(); // obj is now a pointer of type Car
void obj = 0; // obj has no type
void* = new Car(); // obj is a pointer with no type

Ich glaube, hier beruht die Verwirrung darauf, wie Sie voidvs definieren würden void*. Ein Rückgabetyp voidbedeutet "Ich gebe nichts zurück", während ein Rückgabetyp void*"Ich gebe einen Zeiger vom Typ nichts zurück" bedeutet.

Dies liegt daran, dass ein Zeiger ein Sonderfall ist. Ein Zeigerobjekt zeigt immer auf eine Stelle im Speicher, unabhängig davon, ob dort ein gültiges Objekt vorhanden ist oder nicht. Es void* carspielt keine Rolle, ob ich auf ein Byte, ein Wort, ein Auto oder ein Fahrrad verweise, da ich void*auf etwas verweise, für das kein Typ definiert ist. Es liegt an uns, es später zu definieren.

In Sprachen wie C # und Java wird der Speicher verwaltet und das Konzept der Zeiger in die Object-Klasse übernommen. Anstatt einen Verweis auf ein Objekt vom Typ "nichts" zu haben, wissen wir, dass unser Objekt zumindest vom Typ "Objekt" ist. Lass mich erklären warum.

Wenn Sie in herkömmlichem C / C ++ ein Objekt erstellen und seinen Zeiger löschen, ist es unmöglich, auf dieses Objekt zuzugreifen. Dies wird als Speicherverlust bezeichnet .

In C # / Java ist alles ein Objekt. Dadurch kann die Laufzeit jedes Objekt nachverfolgen. Es muss nicht unbedingt bekannt sein, dass mein Objekt ein Auto ist, es muss lediglich einige grundlegende Informationen enthalten, die bereits von der Object-Klasse behandelt werden.

Für uns Programmierer bedeutet dies, dass ein Großteil der Informationen, die wir in C / C ++ manuell bearbeiten müssten, von der Object-Klasse für uns verwaltet wird, sodass der Sonderfall, der der Zeiger ist, nicht mehr erforderlich ist.


"meine Leere * zeigt auf etwas ohne definierten Typ" - nicht genau. Es ist richtiger zu sagen, dass es sich um einen Zeiger auf einen Wert der Länge Null handelt (fast wie ein Array mit 0 Elementen, jedoch ohne die dem Array zugeordneten Typinformationen).
Scott Whitlock

2

Ich werde versuchen zu sagen, wie man sich diese Dinge in C vorstellen kann. Die offizielle Sprache in der C-Norm ist nicht so voideinfach zu verstehen , und ich werde nicht versuchen, ganz damit übereinzustimmen.

Der Typ voidist kein erstklassiger Bürger unter den Typen. Obwohl es sich um einen Objekttyp handelt, kann es sich nicht um den Typ eines Objekts (oder Werts), eines Felds oder eines Funktionsparameters handeln. Es kann sich jedoch um den Rückgabetyp einer Funktion und damit um den Typ eines Ausdrucks handeln (im Grunde genommen um Aufrufe solcher Funktionen, aber mit dem bedingten Operator gebildete Ausdrücke können auch den Typ void haben). Aber auch die minimale Verwendung von voidals Werttyp, für die das Obige die Tür offen lässt, nämlich das Beenden einer Funktion, die voidmit einer Anweisung der Form zurückkehrt, return E;in der Eein Ausdruck vom Typ ist void, ist in C explizit verboten (in C ++ ist dies jedoch zulässig). aber aus Gründen, die für C) nicht zutreffen.

Wenn Objekte vom Typ voidzulässig wären, hätten sie 0 Bits (das ist die Informationsmenge im Wert eines Ausdrucks vom voidTyp). Es wäre kein großes Problem, solche Objekte zuzulassen, aber sie wären ziemlich nutzlos (Objekte der Größe 0 würden Schwierigkeiten beim Definieren von Zeigerarithmetik bereiten, was vielleicht am besten einfach verboten wäre). Die Menge der unterschiedlichen Werte eines solchen Objekts hätte ein (triviales) Element; sein Wert könnte angenommen werden, trivial, weil er keine Substanz enthält, aber nicht modifiziert werden kann (ohne einen anderen Wert). Die Norm wird daher verwirrt, voidwenn gesagt wird, dass sie eine leere Menge von Werten umfasst; Ein solcher Typ könnte nützlich sein, um Ausdrücke zu beschreiben, die die dies nicht könnenausgewertet werden (wie Sprünge oder nicht beendende Anrufe), obwohl die C-Sprache einen solchen Typ tatsächlich nicht verwendet.

Als Werttyp nicht zulässig, voidwurde auf mindestens zwei nicht direkt verwandte Arten verwendet, um "verboten" zu kennzeichnen: Die Angabe (void)von Parametern in Funktionstypen bedeutet, dass Argumente für diese nicht zulässig sind (während "()" im Gegenteil alles bedeuten würde erlaubt ist, und ja, es ist sogar verboten , einen voidAusdruck als Argument für Funktionen mit (void)Parameterspezifikation bereitzustellen, und deklarieren void *pbedeutet, dass der dereferenzierende Ausdruck *pverboten ist (aber nicht, dass es sich um einen gültigen Ausdruck vom Typ handelt void). Sie haben also Recht, dass diese Verwendungen von voidnicht wirklich konsistent sindvoid als gültige Art von Ausdrücken . Jedoch ein Objekt vom Typvoid*ist eigentlich kein Zeiger auf Werte jeglicher Art, sondern ein Wert, der als Zeiger behandelt wird, obwohl er nicht dereferenziert werden kann und Zeigerarithmetik damit verboten ist. Das "als Zeiger behandelt" bedeutet dann wirklich nur, dass es von und zu jedem Zeigertyp ohne Informationsverlust umgewandelt werden kann. Es kann jedoch auch verwendet werden, um ganzzahlige Werte von und nach zu konvertieren, sodass es überhaupt nicht auf irgendetwas verweisen muss.


Streng genommen kann es ohne Informationsverlust von und zu jedem Objektzeigertyp gewandelt werden, jedoch nicht immer von und zu . Im Allgemeinen kann ein Cast von void*einem anderen Zeigertyp Informationen verlieren (oder sogar UB haben, an das ich mich nicht erinnern kann), aber wenn der Wert von void*aus dem Casting eines Zeigers desselben Typs stammt, auf den Sie jetzt casten, dann tut es nicht
Steve Jessop

0

In C # und Java wird jede Klasse von einer ObjectKlasse abgeleitet. Wenn wir also einen Verweis auf "etwas" übergeben möchten, können wir einen Verweis vom Typ verwenden Object.

Da wir in diesen Sprachen für diesen Zweck voidkeine Leerzeiger verwenden müssen , muss dies hier nicht "etwas oder nichts" bedeuten.


0

In C ist es möglich, einen Zeiger auf beliebige Dinge zu haben [Zeiger verhalten sich als Referenztyp]. Ein Zeiger kann ein Objekt eines bekannten Typs oder ein Objekt eines beliebigen (unbekannten) Typs identifizieren. Die Schöpfer von C entschieden sich für die Verwendung der gleichen allgemeinen Syntax für "Zeiger auf einen beliebigen Typ" wie für "Zeiger auf einen bestimmten Typ". Da für die Syntax im letzteren Fall ein Token zur Angabe des Typs erforderlich ist, muss bei der ersteren Syntax etwas dort platziert werden, wo dieses Token hingehört, wenn der Typ bekannt wäre. C hat sich dafür entschieden, das Schlüsselwort "void" zu verwenden.

In Pascal ist es wie in C möglich, Zeiger auf beliebige Typen zu haben. populärere Dialekte von Pascal erlauben auch "Zeiger auf einen beliebigen Typ", aber anstatt die gleiche Syntax zu verwenden, die sie für "Zeiger auf einen bestimmten Typ" verwenden, bezeichnen sie den ersteren einfach als Pointer.

In Java gibt es keine benutzerdefinierten Variablentypen. Alles ist entweder eine primitive oder eine Heap-Objektreferenz. Man kann Dinge vom Typ "Referenz auf ein Heap-Objekt eines bestimmten Typs" oder "Referenz auf ein Heap-Objekt eines beliebigen Typs" definieren. Ersteres verwendet einfach den Typnamen ohne spezielle Interpunktion, um anzuzeigen, dass es sich um eine Referenz handelt (da es nichts anderes sein kann). Letzterer verwendet den Typnamen Object.

Die Verwendung von void*als Nomenklatur für einen Zeiger auf etwas von beliebigem Typ ist meines Wissens einzigartig für C und direkte Ableitungen wie C ++; Andere Sprachen, die ich kenne, behandeln das Konzept, indem sie einfach einen eindeutig benannten Typ verwenden.


-1

Sie können sich das Schlüsselwort voidwie ein Leerzeichen vorstellenstruct ( In C99 sind leere Strukturen anscheinend nicht zulässig , in .NET und C ++ belegen sie mehr als 0 Speicher, und zuletzt erinnere ich mich, dass alles in Java eine Klasse ist):

struct void {
};

... was bedeutet, dass es ein Typ mit der Länge 0 ist. So funktioniert es, wenn Sie einen Funktionsaufruf ausführen, der zurückgibt void... anstatt 4 Bytes oder so auf dem Stapel für einen ganzzahligen Rückgabewert zuzuweisen, ändert es den Stapelzeiger überhaupt nicht (inkrementiert ihn um eine Länge von 0) ).

Also, wenn Sie einige Variablen deklariert haben wie:

int a;
void b;
int c;

... und dann haben Sie vielleicht die Adresse dieser Variablen genommen bund ckönnten sich an derselben Stelle im Speicher befinden, weil sie bdie Länge Null hat. A void*ist also ein Zeiger im Speicher auf den eingebauten Typ mit der Länge Null. Die Tatsache, dass Sie möglicherweise wissen, dass unmittelbar danach etwas für Sie von Nutzen sein kann, ist eine andere Angelegenheit und erfordert, dass Sie wirklich wissen, was Sie tun (und was gefährlich ist).


Der einzige mir bekannte Compiler, der dem Typ void eine Länge zuweist, ist gcc, und gcc weist ihm eine Länge von 1 zu.
Joshua
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.