Problem NULL Pointer in case Konstruktionen
-
Hallo Liebes Forum,
Ich soll eine Software compilieren. Leider bekomme ich die folgende
pointers are not permitted as case values
Ich habe das Problem auf den folgenden Quellcode reduziert:
#include <stdio.h> int main(int argc, char **argv) { int*test=NULL; switch(test) { case NULL:printf("is empty\n");break; default:printf("is not empty\n"); }; };
Dafür erhalte ich die Fehlermeldung vom gcc
test.c:5: error: switch quantity not an integer test.c:6: error: pointers are not permitted as case values
Weiß jemand von euch, warum das nicht geht? Vielen Dank im vorraus.
-
switch((ptrdiff_t)test) { case 0: printf("is empty\n"); break; ... ... ...
-
Werner12 schrieb:
Hallo Liebes Forum,
Weiß jemand von euch, warum das nicht geht? Vielen Dank im v******.
klar, siehe Fehlermeldung: pointers are not permitted as case values
wieso machst du nicht einfach
if(test == NULL) printf("is empty\n"); else printf("is not empty\n");
???
-
Weil das eigentliche Programm riesige switch-Blöcke enthält.
-
Werner12 schrieb:
Weil das eigentliche Programm riesige switch-Blöcke enthält.
und? ehrlich gesagt, ich verstehe deine Erklärung nicht.
-
den Block kannst du doch so behalten
du musst doch nur die Zeile mit der Bedingung ändernBsp.:
int i; /* ... */ switch(i) { case 1: /* 100 lines of code */ break; case 2: /* 250 lines of code */ break default: /* 150 lines of code */ break; }
das wären 508 Zeilen code, aber tatsächlich musst du nur 8 Zeilen verändern:
if(i == 1) { /* 100 lines of code */ } else if(i == 2) { /* 250 lines of code */ } else { /* 150 lines of code */ }
die änderung sollte wohl nicht soo lange dauern
-
Oder besser:
die riesigen Switch-Blöcke in eigene Funktionen auslagern. (Rest wie bereits erklärt)
-
was haben deine switchblöcke, welche den wert einer integer auswerten mit dem intergerzeiger zu tun...?
int *i=??????; printf("Pointer is %s\n", (i==NULL) ? "empty" : "valid"); switch(*i){ case 1: //.... break; case 2: //.... break; case 3: //.... break; default: //.... break; }
-
#include <stdio.h> int main(int argc, char **argv) { int*test=NULL; switch((int)test) { case (int)NULL:printf("is empty\n");break; default:printf("is not empty\n"); }; };
-
fricky schrieb:
switch((ptrdiff_t)test) { case 0: printf("is empty\n"); break; ... ... ...
Das ist keine gute Idee. Denn das funktioniert auch nur auf Systemen, wo der Nullzeiger tatsächlich dem Wert 0 entspricht.
-
groovemaster schrieb:
Denn das funktioniert auch nur auf Systemen, wo der Nullzeiger tatsächlich dem Wert 0 entspricht.
ISO/IEC 9899:1999 schrieb:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
-
fricky schrieb:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
Du hast nicht verstanden, was der Satz aussagt. Schau dir am besten mal an, was eine integer constant expression ist. Im Beispiel wird maximal eine integer constant expression mit dem Wert 0 in ptrdiff_t umgewandelt, aber nicht in einen Zeiger.
-
groovemaster schrieb:
Du hast nicht verstanden, was der Satz aussagt. Schau dir am besten mal an, was eine integer constant expression ist.
eine 0 z.b.
der satz sagt aus, dass 0 und )void*)0 das selbe sind, also eine nullpointer-konstante.if (irgendein_pointer == 0) { // passt immer, wenn man testen will, ob's ein nullpointer ist }
-
Also wirklich *-freak/frick ich mach das immer folgendermaßen:
> if(!ptr)
alles andere ist blasphemie
-
void* p = 0; // Nullpointer (initialisiert mit einem konstanten Ausdruck) intptr_t i = (intptr_t)p; assert( i == 0 ); // nicht zwingend erfüllt i = 0; p = (void*)i; // könnte in trap resultieren assert( p == 0 ); // nicht zwingend erfüllt
Glücklicherweise ist dies die einzige Stelle, bei der die Semantik davon abhängt, ob der initialisierende Ausdruck ein konstanter Ausdruck ist oder nicht.
kleine Anmerkung: die Konvertierung void* -> intptr_t -> void* ist in C nur mit void* reversibel (liefert den ursprünglichen Zeigerwert) - in C++ gilt dies für alle Zeigertypen
-
Bei deinem letzten assert sollte es nicht heißen
assert( p == NULL ); // nicht zwingend erfüllt
???
Zu deiner Anmerkung? Wieso in C nur mit void* reversibel? Wieso gilt das nicht zwingend bspweise mit int* ?
-
supertux schrieb:
Bei deinem letzten assert sollte es nicht heißen
assert( p == NULL ); // nicht zwingend erfüllt
Beides ist möglich, da auch ein einfaches 0 eine Nullpointerkonstante ist.
Zu deiner Anmerkung? Wieso in C nur mit void* reversibel? Wieso gilt das nicht zwingend bspweise mit int* ?
Weil der C-Standard diesbezüglich keine Aussage zu anderen Zeigertypen macht.
-
fricky schrieb:
eine 0 z.b.
der satz sagt aus, dass 0 und )void*)0 das selbe sind, also eine nullpointer-konstante.if (irgendein_pointer == 0) { // passt immer, wenn man testen will, ob's ein nullpointer ist }
Richtig, nur war das ja nicht das Thema. camper hat es schon geschrieben, es ging darum
(ptrdiff_t) 0 == (ptrdiff_t)(void*) 0
was nicht zwangsläufig wahr sein muss. Allerdings ist dies auf vielen Systemen trotzdem wahr, da der Nullzeiger eben dem Wert 0 entspricht, also alle Bits 0. Es geht nur darum, dass es für das eigentliche Problem bessere Lösungen gibt und das Brechen der Plattformunabhängigkeit an dieser Stelle absolut unnötig ist.
-
camper schrieb:
Zu deiner Anmerkung? Wieso in C nur mit void* reversibel? Wieso gilt das nicht zwingend bspweise mit int* ?
Weil der C-Standard diesbezüglich keine Aussage zu anderen Zeigertypen macht.
alles klar.
-
groovemaster schrieb:
es ging darum
(ptrdiff_t) 0 == (ptrdiff_t)(void*) 0
was nicht zwangsläufig wahr sein muss.
wenn, wie wir nun alle wissen, 0 == (void)0* wahr ist, dann kann der selbe cast auf beiden seiten, ob nun nach ptrdiff_t, char oder double, auch nichts dran ändrn. 0 bleibt 0, das ist nun mal so.
-
fricky schrieb:
groovemaster schrieb:
es ging darum
(ptrdiff_t) 0 == (ptrdiff_t)(void*) 0
was nicht zwangsläufig wahr sein muss.
wenn, wie wir nun alle wissen, 0 == (void)0* wahr ist, dann kann der selbe cast auf beiden seiten, ob nun nach ptrdiff_t, char oder double, auch nichts dran ändrn. 0 bleibt 0, das ist nun mal so.
Ah... das bedeutet erst einmal nur, dass du nicht gut genug hingeschaut hast.
in0 == (void*)0;
wird der linke Operand des Gleichheitsoperators implizit in den Typ void* konvertiert, was, wie bereits erwähnt wurde, in einem Nullpointer des Typs void* resultiert. Diese implizite Konvertierung ist notwendig, weil == nur Werte des gleichen Typs vergleichen kann. Wir vergleichen also das Ergebnis der gleichen Operation auf beiden Seiten, jede 0 wird in einen Nullpointer des Typs void* konvertiert und das Ergebnis verglichen, was natürlich das Ergebnis true liefert. Die beiden Seiten unterscheiden sich nur im Grund für diese Konvertierung. Auf der rechten Seite ist diese durch den Cast-Operator bedingt, links ist es der Gleichheitsoperator, der zunächst beide Seiten (nach bestimmten Regeln) in einen gemeinsamen Typen konvertieren muss. In
NULL == (void*)0
hängt es nun davon ab, wie NULL auf einer konkreten Plattform implementiert ist. Ist das bereits ein Ausdruck vom Typ void* entfällt natürlich die implizite Konvertierung auf der linken Seite (weil sie durch das Makro bereits explizit erfolgt ist), sonst gilt das oben gesagte.
(ptrdiff_t) 0 == (ptrdiff_t)(void*) 0
ist etwas völlig Anderes. Auf der linken Seite wird die integrale Null in den Typ ptrdiff_t konvertiert. ptrdiff_t ist ein integraler Typ und Konvertierungen zwischen integralen Typen sind werterhaltend. Folglich hat die linke Seite den Wert 0. Auf der rechten Seite haben wir zunächst eine explizite Konvertierung nach void*, was in einem Nullpointer dieses Typs resultiert. Dieser Nullpointer wird dann explizit in den integralen Typ ptrdiff_t konvertiert, was nicht zwingend in dem Wert 0 resultiert. Der Vergleichsoperator wiederum bewirkt hier keine zusätzlichen impliziten Konvertierungen, denn beide Operanden haben bereits den gleichen Typ.