Problem mit dem NULL Pointer
-
Hallo und fröhliche Weihnachten an alle
;
Wie in der Überschrift zu erkennen, habe ich ein Problem mit dem NULL Pointer.
Ich habe ein Programm geschrieben, bei dem über die Kommandozeile ein Operator und zwei Operanden eingelesen werden und diese Zahlen können dann halt mit den Grundrechenarten bearbeitet werden.meine main.ccp datei sieht dann so aus:
#include "main.h" void main(int argc, char *argv[]) { char op[10]; double arg1, arg2; double *erg; double x=0; erg=&x; if (argc!=4) printf ("Eingabefehler!\n(Operator) (Argument) (Argument)"); else { strcpy(op,argv[1]); arg1=atof(argv[2]); arg2=atof(argv[3]); if (rechnung(op,arg1,arg2,erg)==1) printf ("Eingabefehler!"); else { rechnung(op,arg1,arg2,erg); printf("%lf", *erg); } } }
Da ich ja dem Pointer "erg" einen Wert in der Funktion übergebe muss ich ihn ja, bevor ich ihn verwende, initialisieren oder? Das hab ich auch gemacht, allerdings etwas unschön mit einer weiteren Variable "x" auf deren Adresse ich den Pointer gerichtet habe.
Allerdings ist mir dann eingefallen, dass das Ganze ja auch schöner mit einem NULL Pointer geht.... double *erg; erg=NULL; ...
Doch wenn ich versuche den Pointer "erg" so zu initialisieren, dann bekomm ich ich immer einen Fehler wenn ich das Programm starte.
"Aufgabe.exe funktioniert nicht mehr. Es wird nach einer Lösung für das Problem gesucht."Also was habe ich falsch gemacht bei der Verwendund von NULL?
Achso .. falls man noch andere Programmteile braucht um die Frage zu beantworten bitte Bescheid geben .. wollte hier nicht so viel reinklatschen ^^.Wäre super, wenn mir jemand helfen könnte, danke schonmal!
gruß ES
-
das Problem im ersten Code ist, dass 'erg' nicht initialisiert ist, damit ist es gemeint, dass er irgndwo zeigt, wo du sehr wahrscheinlich keine Leserechte, geschweige Schreibrechte hast, sprich, er zeigt auf eine ungültige Stelle im Speicher.
Mit 'erg = NULL' kannst du zwar den Zeiger initialisieren, aber der zeigt auf die NULL-Addresse, die weder lesbar noch schreibwar ist. Somit wirst du in 'rechnung' wenig damit anfangen können.
Wenn ich das ganze gut verstanden habe, dann wird das Ergebnis der Operation dorthin geschrieben, worauf 'reg' zeigt. Du kannst aber so machen
double erg, *erg_ptr = &erg; ... if (rechnung(op,arg1,arg2,erg_ptr)==1) ... printf("%lf", *erg_ptr); /* oder printf("%lf", erg); */
du initialisierst 'erg_ptr' mit 'erg', d.h. 'erg_ptr' zeigt nun auf eine gültige Stelle im Speicher. Aber in diesem Code ist sogar die Deklaration des Zeiger nicht notwendig, d.h. du kannst gleich so machen:
double erg; ... if (rechnung(op,arg1,arg2,&erg)==1) /* auchte auf & vor erg */ ... printf("%lf", erg);
achte auf den &operator vor erg, der die Adresse zurckgibt, wo 'erg' gespeichert ist.
-
Ah ... okay, d.h. wenn ich in meiner "main" Datei einen Zeiger verwenden möchte brauche ich auf jeden Fall eine zusätzliche Variable (wie mein "x" oder da "erg") um den Zeiger damit zu initialisieren, abwe WANN verwende ich den NULL pointer dann richtig?
Ich dachte der wäre genau für so eine Situation, wenn man einen Zeiger initialisieren möchte, aber eig. noch nichts hat wo er drauf zeigen soll.
Oder für der NULL Point nur dazu, dass der Pointer auf kein gültiges Datenobjekt zeigt und deshalb kann auch später kein Wert übergeben werden? Doch wofür verwende ich NULL dann?
-
Den NULL-Pointer nutzt man üblicherweise um anzuzeigen dass der Pointer auf nichts sinnvolles zeigt. Natürlich kann man den Zeiger später auf was sinnvolles zeigen lassen, einen Zeiger der immer auf nichts zeigt kann man sich auch sparen.
Hier ein Beispiel, ein Stack. (OMFG das ist kein Stack sondern ..... eine Liste!!!!) (OMFG!!!!! MALLOC!!!! AAAAAAAAAAAHHHHHHHHHHHHHHHHH)
struct stack{ struct stackelement *first; }; struct stackelement{ struct stackelement *previous; void *value; }; struct stack *createStack(){ struct stack *s = malloc(sizeof(struct stack)); s->first = 0; //hier zeigt first auf NULL weil es kein erstes Element gibt return s; } void push(struct stack *s, void *v){ //legt ein Element auf den Stack struct stackelement *se = malloc(sizeof(struct stack)); se->value = v; if (!s->first){ //lies: wenn es kein erstes Element gibt s->first = se; //dann ist unser neues Element das erste Element s->previous = 0; //weitere Elemente haben wir aber nicht } else{ //es gibt ein erstes Element se->previous = s->first; //dieses erste Element ist jetzt das zweite Element s->first = se; //das neue Element ist jetzt das erste } } void *pop(struct stack *s){ //holt ein Element vom Stack if (!s->first) return 0; //es exestiert kein Element das man holen könnte void *v = s->first->value; //der Wert des ersten Elements muss zurückgegeben werden struct stackelement *temp = s->first; //wir merken uns das Element was es nun nicht mehr geben soll s->first = s->first->previous; //das nächste Element ist nun das erste free(temp); //das nicht mehr benötigte Element wird gelöscht. return v; //schließlich wird der Wert zurückgegeben }
Wie du an den Kommentaren erkennst wird 0 immer für "Objekt auf das der Zeiger zeigt existiert nicht" benutzt und mit if (zeiger) wird geprüft ob der Zeiger auf ein Objekt zeigt.
Nach Ansicht des Forums ist der obige Code böse, weil malloc drin ist und es verhindert werden kann. Wenn du es forengerecht haben willst musst du einen void *buffer[12345] nehmen und dort die Elemente reinschreiben und dir den Index merken. Außerdem steht öfter mal in dem if- und else-Zweig dasselbe womit man das entsprechend vor oder nach der Verzweigung hinschreiben kann und es fehlen noch einige Funktionen bevor man den Stack sinnvoll einsetzen kann, aber der Sinn von NULL-Pointern sollte klar geworden sein.
BTW ich habe immer 0 statt NULL geschrieben. Ist eigentlich egal. In der stdlib.h steht #define NULL (void *)0 drin.
-
also NULL ist eigentlich eine definition ist so oder so ähnlich definiert
#define NULL ((void*)0)
sagen wir mal du baust eine verkettete liste
typedef struct _list{ int data; struct _list *next; }list list *newListNode(int data){ list *ret = malloc(sizeof(list)); ret->data = data; ret->next = NULL; return data; } int main(){ list *a = newListNode(1); list *b = newListNode(2); a->next = b; //wenn du jetzt die liste durchläufst mußt natürlich wissen wo schluß ist also while(a->next!=NULL){ printf("%d",a->data); a = a->next; } }
hoffe da sind nicht all zu viele fehler drin;)
-
oh da war ich bischen zu langsam
-
oh und es waren doch ne ganze menge
typedef struct _list{ int data; struct _list *next; }list; list *newListNode(int data){ list *ret = malloc(sizeof(list)); ret->data = data; ret->next = NULL; return ret; } int main(){ list *a = newListNode(1); list *b = newListNode(2); a->next = b; //wenn du jetzt die liste durchläufst mußt natürlich wissen wo schluß ist also while(a!=NULL){ printf("%d",a->data); a = a->next; } }
-
ES schrieb:
Ich dachte der wäre genau für so eine Situation, wenn man einen Zeiger initialisieren möchte, aber eig. noch nichts hat wo er drauf zeigen soll.
Ja, das gehört zum guten Stil. Der Zeiger ist sozusagen entschärft. Um ihn benutzen zu können, musst du ihm vorher eine gültige Adresse zuweisen.
ES schrieb:
Oder für der NULL Point nur dazu, dass der Pointer auf kein gültiges Datenobjekt zeigt und deshalb kann auch später kein Wert übergeben werden?
Klar kannst du später einen Wert übergeben, wenn es kein const Zeiger ist.
ES schrieb:
Doch wofür verwende ich NULL dann?
NULL ist in einigen Fällen praktisch:
Bei verketteten Listen, um den Anfang oder das Ende der Liste zu kennzeichnen und um Elemente anhängen zu können.
Als Rückgabewert einer Funktion, um zu signalisieren das etwas schiefgelaufen ist.
Bei der Reservierung und Freigabe von Speicher ( realloc, free ).
Wenn du Zeigerarrays hast kann es sein, das nicht bekannt ist, welche Elemente des Array einen gültigen Zeiger erhalten haben. Initialisierst du alle Arrayelemente mit Null, brauchst du dich nicht um jedes einzelne Element bei der Freigabe zu kümmern, weil ein free(NULL) nicht schadet.
Etc.nwp2 schrieb:
Nach Ansicht des Forums ist der obige Code böse, weil malloc drin ist und es verhindert werden kann. Wenn du es forengerecht haben willst musst du einen void *buffer[12345] nehmen und dort die Elemente reinschreiben und dir den Index merken.
Das ist doch Käse und das weißt du auch.
-
ums klar und deutlich zu sagen NULL != 0 !!! betrifft vor allem den code von nwp2 der ja immer schön 0 verwendet hat... denn der null zeiger zeigt nicht notwendiger weise auf 0
-
noobLolo schrieb:
... denn der null zeiger zeigt nicht notwendiger weise auf 0
Sondern? 0L in C++?
-
wie schon beschrieben ist es eine definition die genau so gut so aussehen könnte
#define NULL 0x1
es muß dann nur sichergestellt werden dass malloc keine adresse 0x1 zurück gibt.
-
nwp2 schrieb:
noobLolo schrieb:
... denn der null zeiger zeigt nicht notwendiger weise auf 0
Sondern? 0L in C++?
In C++ ist das einfacher. Aber hier ist nur gefragt, wie es in C ist.
-
noopLolo schrieb:
wie schon beschrieben ist es eine definition die genau so gut so aussehen könnte
#define NULL 0x1
es muß dann nur sichergestellt werden dass malloc keine adresse 0x1 zurück gibt.
Sowas kann echt passieren? Es wäre reichlich fatal wenn if(NULL) wahr wäre.
In der Doku auf http://www.cplusplus.com/reference/clibrary/cstddef/NULL/ steht
^This macro expands to a null pointer constant.A null pointer is generally used to signify that a pointer does not point to any object.
In C++, NULL expands either to 0 or 0L.^Scheinbar kann man tatsächlich NULL als 12345 definieren. Weshalb man sowas machen sollte ist mir aber unklar.
Heißt, entweder die Pointer auf NULL setzen und mit if(pointer==NULL) testen oder gleich 0 benutzen.
-
nwp2 schrieb:
Heißt, entweder die Pointer auf NULL setzen und mit if(pointer==NULL) testen
ja so sollte man das machen wenn mans sauber haben will.
genauso wie
void *data = malloc(sizeof(char)); if(data==NULL) THROW_ERROR("hab kein speicher");
statt
void *data = malloc(sizeof(char)); if(data) printf("hab speicher");
-
nwp2 schrieb:
Heißt, entweder die Pointer auf NULL setzen und mit if(pointer==NULL) testen oder gleich 0 benutzen.
gleich 0 benutzen. wenn ich mich recht erinnere, wird bei 'nem void p = 0; die 0 in die pointer-ungültigmach-nullpointer-konstante konvertiert (also auch wenn sie (void)123 ist). ich benutze das makro 'NULL' nie, gab noch niemals zickereien deshalb. den sinn einer nullpointerkonstanten != 0 kenne ich aber auch nicht.
noobLolo schrieb:
void *data = malloc(sizeof(char)); if(data==NULL) THROW_ERROR("hab kein speicher");
was iss'n 'THROW_ERROR'? ein verstecktes longjmp? *fg*
übrigens kannste dir das 'sizeof(char)' sparen.
-
nwp2 schrieb:
Sowas kann echt passieren?
hab mal folgendes gefunden unter http://www.lysator.liu.se/c/c-faq/c-1.html
The Prime 50 series used segment 07777, offset 0 for the null pointer, at least for PL/I. Later models used segment 0, offset 0 for null pointers in C, necessitating new instructions such as TCNP (Test C Null Pointer), evidently as a sop to all the extant poorly-written C code which made incorrect assumptions. Older, word-addressed Prime machines were also notorious for requiring larger byte pointers (char *'s) than word pointers (int *'s).
The Eclipse MV series from Data General has three architecturally supported pointer formats (word, byte, and bit pointers), two of which are used by C compilers: byte pointers for char * and void *, and word pointers for everything else.
Some Honeywell-Bull mainframes use the bit pattern 06000 for (internal) null pointers.
The CDC Cyber 180 Series has 48-bit pointers consisting of a ring, segment, and offset. Most users (in ring 11) have null pointers of 0xB00000000000.
The Symbolics Lisp Machine, a tagged architecture, does not even have conventional numeric pointers; it uses the pair <NIL, 0> (basically a nonexistent <object, offset> handle) as a C null pointer.
Depending on the "memory model" in use, 80*86 processors (PC's) may use 16 bit data pointers and 32 bit function pointers, or vice versa.
The old HP 3000 series computers use a different addressing scheme for byte addresses than for word addresses; void and char pointers therefore have a different representation than an int (structure, etc.) pointer to the same address would have.
nwp2 schrieb:
Es wäre reichlich fatal wenn if(NULL) wahr wäre.
When C requires the boolean value of an expression (in the if, while, for, and do statements, and with the &&, ||, !, and ?: operators), a false value is produced when the expression compares equal to zero, and a true value otherwise. That is, whenever one writes
if(expr)
where "expr" is any expression at all, the compiler essentially acts as if it had been written as
if(expr != 0)
Substituting the trivial pointer expression "p" for "expr," we have
if(p) is equivalent to if(p != 0)
and this is a comparison context, so the compiler can tell that the (implicit) 0 is a null pointer, and use the correct value. There is no trickery involved here; compilers do work this way, and generate identical code for both statements. The internal representation of a pointer does not matter.
The boolean negation operator, !, can be described as follows:
!expr is essentially equivalent to expr?0:1
It is left as an exercise for the reader to show that
if(!p) is equivalent to if(p == 0)
"Abbreviations" such as if(p), though perfectly legal, are considered by some to be bad style.