Ich hatte Dinge wie geschrieben
char *x=NULL;
unter der Annahme, dass
char *x=2;
würde einen char
Zeiger auf Adresse 2 erstellen .
Im Programmier-Tutorial für GNU C heißt es jedoch, dass int *my_int_ptr = 2;
der ganzzahlige Wert 2
bei der my_int_ptr
Zuweisung an eine beliebige zufällige Adresse gespeichert wird.
Dies scheint zu implizieren, dass mein eigener char *x=NULL
Wert NULL
einer char
beliebigen Adresse im Speicher zuweist, was auch immer der Wert von cast für a ist.
Während
#include <stdlib.h>
#include <stdio.h>
int main()
{
char *x=NULL;
if (x==NULL)
printf("is NULL\n");
return EXIT_SUCCESS;
}
druckt in der Tat
ist Null
Wenn ich es kompiliere und ausführe, mache ich mir Sorgen, dass ich mich auf undefiniertes oder zumindest unterbestimmtes Verhalten verlasse und schreiben sollte
char *x;
x=NULL;
stattdessen.
c
pointers
initialization
fagricipni
quelle
quelle
int *x = whatever;
tut und dem, wasint *x; *x = whatever;
tut.int *x = whatever;
verhält sich eigentlich soint *x; x = whatever;
, nicht*x = whatever;
.Antworten:
TL; DR Ja, sehr.
Die tatsächliche Behauptung auf dem Leitfaden lautet wie folgt
Nun, sie sind falsch, du hast recht.
Für die Aussage ( ignoriert vorerst die Tatsache, dass der Zeiger auf die Ganzzahlkonvertierung ein implementierungsdefiniertes Verhalten ist )
my_int_ptr
ist eine Variable (vom Typ Zeiger aufint
), sie hat eine eigene Adresse (Typ: Adresse des Zeigers auf Ganzzahl), Sie speichern einen Wert von2
in dieser Adresse.Da
my_int_ptr
es sich nun um einen Zeigertyp handelt, können wir sagen, dass er auf den Wert von "Typ" an der Speicherstelle zeigt, auf die gezeigt wird die der Wert zeigt, inmy_int_ptr
. Also, Sie sind im Wesentlichen den Wert Zuordnung von der Zeigervariable, nicht der Wert des Speicherplatzes , auf den durch den Zeiger.Also zum Abschluss
initialisiert die Zeigervariable
x
aufNULL
, nicht auf den Wert an der Speicheradresse, auf die der Zeiger zeigt .Dies ist das gleiche wie
Erweiterung:
Nun, streng konform, eine Aussage wie
ist illegal, da es sich um eine Verletzung von Einschränkungen handelt. Deutlich sein,
my_int_ptr
ist eine Zeigervariable vom Typint *
2
hatint
per Definition einen Typ .und da es sich nicht um "kompatible" Typen handelt, ist diese Initialisierung ungültig, da sie gegen die Regeln der einfachen Zuweisung verstößt, die in Kapitel 6.5.16.1 / P1, beschrieben in Lundins Antwort, erwähnt werden .
Falls jemand interessiert ist, wie die Initialisierung mit einfachen Zuweisungsbeschränkungen verbunden ist, zitieren Sie
C11
, Kapitel §6.7.9, P11quelle
2
ist einint
, die Zuordnung ist ein Problem. Aber es ist mehr als das.NULL
kann auch einint
, ein seinint 0
. Es ist nur so, dasschar *x = 0;
es gut definiert ist undchar *x = 2;
nicht. 6.3.2.3 Zeiger 3 (Übrigens: C definiert kein ganzzahliges Literal , nur String-Literal und zusammengesetztes Literal .0
char *x = (void *)0;
, dass du dich anpasst ? oder ist es nur mit anderen Ausdrücken, die den Wert ergibt0
?0
sind etwas Besonderes: Sie konvertieren implizit in Nullzeiger, getrennt von den üblichen Regeln zum expliziten Umwandeln allgemeiner Ganzzahlausdrücke in Zeigertypen.int *p = somePtrExpression
ist meiner Meinung nach ziemlich schrecklich, da es so aussieht, als würde der Wert von festgelegt,*p
aber tatsächlich wird der Wert von festgelegtp
.Das Tutorial ist falsch. In ISO C
int *my_int_ptr = 2;
ist ein Fehler. In GNU C bedeutet dies dasselbe wieint *my_int_ptr = (int *)2;
. Dies konvertiert die Ganzzahl2
in eine Speicheradresse, wie vom Compiler festgelegt.Es wird nicht versucht, etwas an dem von dieser Adresse adressierten Ort (falls vorhanden) zu speichern. Wenn Sie weiter schreiben würden
*my_int_ptr = 5;
, würde es versuchen, die Nummer5
an dem von dieser Adresse adressierten Ort zu speichern .quelle
Um zu verdeutlichen, warum das Tutorial falsch ist,
int *my_int_ptr = 2;
handelt es sich um eine "Einschränkungsverletzung". Es handelt sich um Code, der nicht kompiliert werden darf, und der Compiler muss Ihnen bei Auftreten eine Diagnose stellen.Gemäß 6.5.16.1 Einfache Zuordnung:
In diesem Fall ist der linke Operand ein nicht qualifizierter Zeiger. Nirgends wird erwähnt, dass der richtige Operand eine ganze Zahl (arithmetischer Typ) sein darf. Der Code verstößt also gegen den C-Standard.
Es ist bekannt, dass sich GCC schlecht verhält, es sei denn, Sie weisen ausdrücklich an, dass es sich um einen Standard-C-Compiler handelt. Wenn Sie den Code als kompilieren
-std=c11 -pedantic-errors
, wird eine Diagnose korrekt angezeigt, wie dies erforderlich ist.quelle
void *
, wird als Nullzeigerkonstante bezeichnet.“ Beachten Sie den vorletzten Aufzählungspunkt in Ihrem Angebot. Daherint* p = 0;
ist eine legale Art zu schreibenint* p = NULL;
. Obwohl letzteres klarer und konventioneller ist.int m = 1, n = 2 * 2, * p = 1 - 1, q = 2 - 1;
legal.intptr_t
explizit in einen der zulässigen Typen auf der rechten Seite konvertieren . Das heißt,void* a = (void*)(intptr_t)b;
ist nach Punkt 4 legal,(intptr_t)b
ist jedoch weder ein kompatibler Zeigertyp noch einevoid*
oder eine Nullzeigerkonstante undvoid* a
ist weder ein arithmetischer Typ noch_Bool
. Der Standard besagt, dass die Konvertierung legal ist, aber nicht, dass sie implizit ist.int *my_int_ptr = 2
Das ist völlig falsch. Wenn dies tatsächlich geschrieben ist, besorgen Sie sich bitte ein besseres Buch oder Tutorial.
int *my_int_ptr = 2
Definiert einen ganzzahligen Zeiger, der auf Adresse 2 zeigt. Wenn Sie versuchen, auf die Adresse zuzugreifen, kommt es höchstwahrscheinlich zu einem Absturz2
.*my_int_ptr = 2
, dh ohne dasint
in der Zeile, speichert den Wert zwei an einer beliebigen zufälligen Adressemy_int_ptr
, auf die gezeigt wird. Nachdem Sie dies gesagt haben, können SieNULL
einem Zeiger zuweisen , wenn er definiert ist.char *x=NULL;
ist vollkommen gültig C.Bearbeiten: Während ich dies schrieb, wusste ich nicht, dass die Konvertierung von Ganzzahlen in Zeiger ein implementierungsdefiniertes Verhalten ist. Weitere Informationen finden Sie in den guten Antworten von @MM und @SouravGhosh.
quelle
Eine Menge Verwirrung über C-Zeiger kommt von einer sehr schlechten Wahl, die ursprünglich in Bezug auf den Codierungsstil getroffen wurde, was durch eine sehr schlechte kleine Wahl in der Syntax der Sprache bestätigt wird.
int *x = NULL;
ist richtig C, aber es ist sehr irreführend, ich würde sogar unsinnig sagen, und es hat das Verständnis der Sprache für viele Anfänger behindert. Man denkt, wir könnten später etwas tun,*x = NULL;
was natürlich unmöglich ist. Sie sehen, der Typ der Variablen ist nichtint
und der Name der Variablen ist nicht*x
, noch spielt die*
in der Deklaration eine funktionale Rolle in Zusammenarbeit mit der=
. Es ist rein deklarativ. Was also viel sinnvoller ist, ist Folgendes:int* x = NULL;
Das ist auch richtig C, obwohl es nicht dem ursprünglichen K & R-Codierungsstil entspricht. Es macht vollkommen klar, dass der Typint*
und die Zeigervariable istx
, so dass selbst für den Uneingeweihten, in dem der WertNULL
gespeichert wirdx
, der ein Zeiger ist, deutlich wirdint
.Darüber hinaus ist es einfacher, eine Regel abzuleiten: Wenn der Stern vom Variablennamen entfernt ist, handelt es sich um eine Deklaration, während der an den Namen angehängte Stern eine Zeiger-Dereferenzierung darstellt.
Jetzt wird es viel verständlicher, dass wir es weiter unten entweder tun können,
x = NULL;
oder*x = 2;
mit anderen Worten, es macht es für einen Anfänger einfacher zu sehen, wie esvariable = expression
zupointer-type variable = pointer-expression
und führtdereferenced-pointer-variable = expression
. (Für den Eingeweihten meine ich mit 'Ausdruck' 'Wert'.)Die unglückliche Wahl in der Syntax der Sprache ist, dass Sie beim Deklarieren lokaler Variablen sagen können,
int i, *p;
welche eine Ganzzahl und einen Zeiger auf eine Ganzzahl deklarieren, sodass man glaubt, dass dies*
ein nützlicher Teil des Namens ist. Dies ist jedoch nicht der Fall, und diese Syntax ist nur ein eigenartiger Sonderfall, der der Einfachheit halber hinzugefügt wurde. Meiner Meinung nach hätte sie niemals existieren dürfen, da sie die oben vorgeschlagene Regel ungültig macht. Soweit ich weiß, ist diese Syntax nirgendwo anders in der Sprache von Bedeutung, aber selbst wenn dies der Fall ist, weist sie auf eine Diskrepanz bei der Definition von Zeigertypen in C hin. Überall sonst, in Deklarationen mit einzelnen Variablen, in Parameterlisten, In Strukturelementen usw. können Sie Ihre Zeiger alstype* pointer-variable
anstelle von deklarierentype *pointer-variable
. es ist vollkommen legal und macht mehr Sinn.quelle
int *x = NULL; is correct C, but it is very misleading, I would even say nonsensical,
... Ich muss zustimmen, nicht zuzustimmen.It makes one think
.... hör auf zu denken, lies zuerst ein C-Buch, nichts für ungut.int* somePtr, someotherPtr
zwei Zeiger erklärt, in der Tat, habe ich zu schreiben ,int* somePtr
aber das führt zu dem Fehler , den Sie beschreiben.create
stattdessen verwendetcreat
. :) Der Punkt ist, es ist wie es ist und wir müssen uns formen, um uns daran anzupassen. Es läuft alles auf die persönliche Wahl am Ende des Tages hinaus, stimme zu.Ich möchte den vielen ausgezeichneten Antworten etwas Orthogonales hinzufügen. Tatsächlich ist das Initialisieren auf
NULL
keine schlechte Praxis und kann nützlich sein, wenn dieser Zeiger zum Speichern eines dynamisch zugewiesenen Speicherblocks verwendet werden kann oder nicht.Da gemäß der Norm ISO-IEC 9899
free
ein Nein ist, wenn das Argument lautetNULL
, ist der obige Code (oder etwas Bedeutenderes in der gleichen Richtung) legitim.quelle
void*
wird nach Bedarf konvertiert. Code, der mit einem C- und einem C ++ - Compiler funktioniert, kann jedoch Vorteile haben.const
Zeiger sein, die in medias res deklariert sind , aber selbst wenn ein Zeiger veränderbar sein muss (wie einer, der in einer Schleife oder von verwendet wirdrealloc()
), wird er so eingestellt, dass erNULL
Fehler abfängt, wo er zuvor verwendet wurde es ist mit seinem realen Wert festgelegt. Auf den meisten SystemenNULL
verursacht die Dereferenzierung an der Fehlerstelle einen Segfault (obwohl es Ausnahmen gibt), während ein nicht initialisierter Zeiger Müll enthält und das Schreiben in ihn beliebigen Speicher beschädigt.NULL
, aber es kann sehr schwierig sein, einen Müllzeiger von einem gültigen zu unterscheiden. Es ist daher hilfreich sicherzustellen, dass alle Zeiger immer entweder gültig sind oderNULL
ab dem Zeitpunkt der Deklaration.Dies ist ein Nullzeiger
quelle
Das ist richtig.
Diese Funktion ist korrekt für das, was sie tut. Es weist dem Zeichenzeiger x die Adresse 0 zu. Das heißt, es zeigt den Zeiger x auf die Speicheradresse 0.
Alternative:
Meine Vermutung, was Sie wollten, ist:
quelle
char* x = 0; if (x == 0)
ist wahr. Zeiger sind nicht unbedingt ganze Zahlen.