Ja, Sterne sind teuer, aber den einen hätte ich schon noch spendiert.
Dass die Hardware NULL und 0 nicht unterscheidet, ist ja o.k., aber so
eine einzelne 0 ist doch kein Pointer? Da müsste es doch eine Warnung
geben?
eagle user schrieb:> aber so> eine einzelne 0 ist doch kein Pointer?
Noe, die 0 selber nicht. Aber du sagst, dass der Pointer bitte auf die
Adresse 0 zeigen soll. Was sollte denn die 0 von jeder anderen Zahl
unterscheiden? Warum sollte der Pointer nicht darauf zeigen duerfen?
Warum soll dich der Compiler da warnen?
eagle user schrieb:> Dass die Hardware NULL und 0 nicht unterscheidet
Die Hardware kennt sowas wie NULL gar nicht.
> Ja, Sterne sind teuer, aber den einen hätte ich schon noch spendiert.> Dass die Hardware NULL und 0 nicht unterscheidet, ist ja o.k., aber so> eine einzelne 0 ist doch kein Pointer? Da müsste es doch eine Warnung> geben?
In C++ wäre foo eine Referenz und würde Dir 5 Sterne sparen für den
Preis eines Unds:
1
voidget_foo(intwhat,int&foo)
2
{
3
foo=one_wire_read_eeprom(what);
4
if(foo<42||foo>=666){// Muell im EEPROM?
5
foo=0;// ja
6
}
7
}
Der wesentliche Unterschied wäre, dass man in C++ ausdrücken kann, dass
&foo nicht 0 sein kann.
mfg Torsten
Peter II schrieb:> Torsten R. schrieb:>> Der wesentliche Unterschied wäre, dass man in C++ ausdrücken kann, dass>> &foo nicht 0 sein kann.>> wie meinst du das?
Technisch gesehen, ist eine Referenz nichts anderes, als ein Zeiger.
Allerdings mit der Semantik, nie 0 zu sein. Referenzen, müssen z.B.
immer initialisert sein und können auch nicht geändert werden.
Eine Warnung, wenn man den Pointer auf NULL bzw. 0 setzt, waere sogar
kontraproduktiv. Dies ein voellig normaler Vorgang. Wenn man mit
dynamischen Speicher arbeitet setzt man den Pointer nach der Freigabe
des Speichers auf NULL/nullptr (what ever) um zu signalisieren, das der
Pointer ungueltig ist. Ebenso geben Funktionen wie malloc (usw.) einen
NULL-Pointer zurueck, wenn kein Speicher alokiert werden konnte.
eagle user schrieb:> Dass die Hardware NULL und 0 nicht unterscheidet, ist ja o.k., aber so> eine einzelne 0 ist doch kein Pointer? Da müsste es doch eine Warnung> geben?
0 ist eine gültige Nullzeigerkonstante. NULL ist nur ein Makro, das in C
im Prinzip entweder schlicht als 0 oder als (void*)0 definiert sein
muss.
Peter II schrieb:> eagle user schrieb:>> einzelne 0 ist doch kein Pointer?>> in C schon.>> C++ kennt dafür jetzt ein nullptr;
0 geht auch in C++. Aber nullptr ist zu bevorzugen.
Kaj G. schrieb:> Noe, die 0 selber nicht. Aber du sagst, dass der Pointer bitte auf die> Adresse 0 zeigen soll.
Das stimmt nicht ganz. Man kann (nicht direkt ohne cast) keine
beliebigen Zahlen in Pointer rein schreiben, denn der C Standard
definiert auch gar nicht dass Zeiger intern eine Zahl sind die einer
Adresse entsprechen. Wenn man aber ein 0 Literal zuweist, wird ein "null
pointer " geschrieben, der aber gar nicht notwendigerweise durch 0
dargestellt wird, sondern ggf. auch über irgendein anderes Bitmuster.
Praktisch nutzen die meisten Plattformen/Compiler für Nullpointer die
Zahl 0. Es wäre aber denkbar, dass man z.B. auf Cortex-M tatsächlich
0xFFFFFFFF in den Pointer schreibt, denn Zugriffe darauf führen zum
Programm Absturz, während die Adresse 0 durchaus eine gültige sein kann
(z.B. erstes Byte im Flash); ich wüsste aber gerade keinen Compiler der
das kann...
Dr. Sommer schrieb:> Wenn man aber ein 0 Literal zuweist, wird ein "null> pointer " geschrieben, der aber gar nicht notwendigerweise durch 0> dargestellt wird, sondern ggf. auch über irgendein anderes Bitmuster.
Das kann auch ins Auge gehen. Seit (ich glaube) 6.x kann man mit gcc
nicht mehr ohne weiteres per * (unsigned long *) 0 auf Adresse 0
schreiben (bei m68k manchmal notwendig, wenn man initial pc und initial
sp schreiben will um einen Reset vorzubereiten). Die neuere Version
frisst den Code, tut aber nicht mehr, was sie früher mal tat - nämlich
auf Adresse 0 schreiben.
Sondern ruft einfach abort() auf. Trotz -ffreestanding. Auf m68k extra
blöd, weil abort einen trap #7 auslöst, auf dem man nicht unbedingt
einen trap-handler hat.
Hat mich mal ein paar Stunden gekostet, bis ich
-fno-delete-null-pointer-checks gefunden habe...
Markus F. schrieb:> Die neuere Version> frisst den Code, tut aber nicht mehr, was sie früher mal tat - nämlich> auf Adresse 0 schreiben.
Ja, das ist durch C explizit erlaubt - 0 auf einen Pointer zu schreiben
bewirkt, dass da ein Nullpointer-Wert gespeichert wird. Den zu
derefenzieren ist undefiniert, und da darf der Compiler alles tun, z.B.
abort() aufrufen. Zugriffe auf beliebige Adressen sind in C nicht
wirklich vorgesehen, der Trick mit Integer nach Pointer casten ist
eigentlich verboten, und gerade bei der Adresse 0 geht's schief... Man
könnte sich notfalls mit Inline Assembly behelfen.
Nullmeridianvermesser schrieb:> Was ist eigentlich, wenn man tatsächlich die Adresse 0 meint? Das würden> einem doch viele Bibliotheksfunktionen um die Ohren hauen?
Weil diese Bibliotheksfunktionen die Adresse 0 als ungültig ansehen, was
auf CPUs mit Paging-Einheit dadurch gehandhabt werden kann, daß die
erste Speicherseite nicht gemapped wird, also immer zu Pagefault führt
und der Pagefault-Handler die NULL-Page speziell behandelt.
Dem Compiler ist es aber egal ob die allererste Adresse oder irgendeine
danach kommende benutzt wird.
Auf typischer Forums-Hardware (AVR) kann man per *(uint8_t*0) das
Register R0 erreichen, was selten Sinn macht.
Dr. Sommer schrieb:> Zugriffe auf beliebige Adressen sind in C nicht> wirklich vorgesehen, der Trick mit Integer nach Pointer casten ist> eigentlich verboten,
wer sollte mir das verbieten? Im Gegenteil, das ist explizit erlaubt:
ANSI C99 6.3.2.3.5: an integer may be converted to any pointer type.
nur mit der NULL geht's nicht:
ANSI C99 .3.2.3.3: An integer constant expression with the value 0, or
such an expression cast to type void *, is called a null pointer
constant.55) If a null pointer constant is converted to a pointer type,
the resulting pointer, called a null pointer, is guaranteed to compare
unequal to a pointer to any object or function.
C ist eine recht liberale Sprache. Wenn Du nur mal so was bastelst,
siehst Du im Debugger, was da passiert.
Wenn Du ernsthaft Code schreibst, hätten Compiler oder Lint gewarnt,
selbst wenn Du das const vor foo vergessen hättest. Einfach, weil die
Zeile sinnlos ist und die statische Codeanalyse das erkennt.
Bei den INMOS Transputern war der Adressraum mit Vorzeichen definiert
und ging folglich von -32768 bis +32767 (T212) oder
0x80000000..0x7FFFFFFF (T414). Die Adresse 0 lag also mitten im
Adressraum, und beim 16-Bitter aufgrund problemlos verfügbarer
RAM-Kapazität auch ganz real mitten im RAM.
Bei der Implementierung von C war eine korrekte Umsetzung des Umgangs
mit 0/NULL folglich nicht trivial: Der Wert 0 kann eigentlich nicht für
die Funktion "ungültiger Pointer" verwendet werden, int*ptr=0 weist dem
Pointer nicht den Wert 0 zu, if(ptr!=0) vergleicht intern nicht mit 0
und (void*)0 ist nicht das wonach es aussieht. Und was bei if((int)ptr)
rauskommt...
Danke für die vielen tröstenden Worte! Die Suche nach dem Schuldigen
endet wie immer bei mir :(
Jörg W. schrieb:> Andere Variante: den Zeiger als unverändlich deklarieren.> void> get_foo (int what, int * const foo)
daran sollte ich mich gewöhnen...
Torsten R. schrieb:> Peter II schrieb:>> Markus F. schrieb:>>>> warum überhaupt der Zeiger?>> Wahrscheinlich, weil es ein Beispiel ist ;-)
in Wirklichkeit macht die Funktion noch etwas mehr und liefert statt
void einen Pointer auf was anderes. Ja, das sollten evt. zwei getrennte
Funktionen sein.
Markus F. schrieb:>> Zugriffe auf beliebige Adressen sind in C nicht>> wirklich vorgesehen, der Trick mit Integer nach Pointer casten ist>> eigentlich verboten,>> wer sollte mir das verbieten? Im Gegenteil, das ist explizit erlaubt:>> ANSI C99 6.3.2.3.5: an integer may be converted to any pointer type.
Wie würde man auf die Hardware zugreifen wenn das nicht erlaubt wäre?
Hmm, eine struct, die per Linker Script auf die richtige Adresse gemappt
wird, wäre sogar irgendwie elegant.
Aber der entscheidende Satz ist doch "may be converted to any pointer
type". Und das passiert bei einer nackten Konstanten nicht. Also sollte
es bei so einer Zuweisung wenigstens eine Warnung geben. Oder aus diesem
Grund:
Achim S. schrieb:> Wenn Du ernsthaft Code schreibst, hätten Compiler oder Lint gewarnt,> selbst wenn Du das const vor foo vergessen hättest. Einfach, weil die> Zeile sinnlos ist und die statische Codeanalyse das erkennt.
Hat der Compiler aber nicht. Richtig analysiert hat er es - und das
komplette if-statement wegoptimiert. Aber der gcc kennt z.B. eine
Warnung "statement with no effect", sowas hätte ich mir hier gewünscht.
Und die Leute sagen, mit dem aktuellen gcc bekommst du Lint gratis...
A. K. schrieb:> Bei den INMOS Transputern war der Adressraum mit Vorzeichen definiert> und ging folglich von -32768 bis +32767 (T212) oder> 0x80000000..0x7FFFFFFF (T414).
Also deswegen sind die ausgestorben, nix Meteorit... ;)
Dr. Sommer schrieb:> Es wäre aber denkbar, dass man z.B. auf Cortex-M tatsächlich> 0xFFFFFFFF in den Pointer schreibt, denn Zugriffe darauf führen zum> Programm Absturz, während die Adresse 0 durchaus eine gültige sein kann> (z.B. erstes Byte im Flash);
Ab dem Cortex M0+ kann man per MPU einen "Page Fault" erzeugen wenn auf
Adresse 0 zugegriffen wird. Sehr empfehlenswert!
Markus F. schrieb:> ANSI C99 6.3.2.3.5: an integer may be converted to any pointer type.
Ja, aber dereferenzieren darfst du den nur, wenn der Integer entstanden
ist durch einen Pointer to Integer cast von einem "kompatiblen" Pointer
(z.B. gleich oder char), und der Integer groß genug ist. Wie gesagt, C
kennt keine Adressen. Es könnte auch wie in Java umgesetzt sein, wo
Referenzen Objekt-Nummern enthalten. C ist halt doch kein
Super-Assembler, sondern definiert ein plattformunabhängiges Verhalten .
Ich meine es gibt Compiler (für AVR?) die in Pointern auch die Art des
Ziel Speichers ablegen (Flash/RAM). Also keine einfache Adresse mehr.
eagle user schrieb:> Ab dem Cortex M0+ kann man per MPU einen "Page Fault" erzeugen wenn auf> Adresse 0 zugegriffen wird
Ok, da steht der initiale Stack Pointer, auf den muss man normalerweise
nicht zugreifen...
eagle user schrieb:> Aber der gcc kennt z.B. eine> Warnung "statement with no effect", sowas hätte ich mir hier gewünscht.
Kleiner Tipp:
Nicht nur alleine auf den Compiler verlassen, sondern auch Analyse-Tools
wie cppcheck & Co. nutzen. :)
eagle user schrieb:> Aber der entscheidende Satz ist doch "may be converted to any pointer> type". Und das passiert bei einer nackten Konstanten nicht.> Also sollte es bei so einer Zuweisung wenigstens eine Warnung geben.
Gerade bei der 0 nicht. Die hat hier eine Sonderrolle, weil sie im
Zeigerkontext für einen Nulzeiger steht. Hättest du z.B. stattdessen foo
= 3 (oder jede beliebige andere Zahl außer ausgerechnet 0) geschrieben,
hättest du deine Warnung bekommen ("warning: assignment makes pointer
from integer without a cast").
> Oder aus diesem Grund:>> Achim S. schrieb:>> Wenn Du ernsthaft Code schreibst, hätten Compiler oder Lint gewarnt,>> selbst wenn Du das const vor foo vergessen hättest. Einfach, weil die>> Zeile sinnlos ist und die statische Codeanalyse das erkennt.>> Hat der Compiler aber nicht. Richtig analysiert hat er es - und das> komplette if-statement wegoptimiert. Aber der gcc kennt z.B. eine> Warnung "statement with no effect", sowas hätte ich mir hier gewünscht.
Einen Effekt hat das Statement ja schon, nur wird dieser Effekt nie
genutzt (es wird ein Wert reingeschrieben, der danach nie verwendet
wird). Es scheint, dass der gcc nur warnt, wenn eine Variable mal
initial mit einem Wert belegt und danach nie benutzt wird ("warning:
variable ‘foo’ set but not used"), aber nicht, wenn sie schon verwendet
wurde und ihr am Schluss dann ein neuer Wert zugewiesen wird, der dann
nicht mehr benutzt wird.
Rolf M. schrieb:> Einen Effekt hat das Statement ja schon,
nein, hat es nicht.
foo ist eine lokale variable (also i.d.R. auf dem Stack), by value
übergeben. Ein Schreiben auf foo hätte nur dann einen Sinn, wenn sie
danach nochmal verwendet würde. Das ist hier nicht der Fall.
eagle user schrieb:> Und die Leute sagen, mit dem aktuellen gcc bekommst du Lint gratis.
ja, natürlich steckt hinter gcc mehr manpower als hinter PC-Lint, so
dass gcc immer besser wird. Aber zum einen ist Lint m.E. noch immer
besser individualisierbar, zum anderen für jede Plattform verwendbar.
Achim S. schrieb:> Rolf M. schrieb:>> Einen Effekt hat das Statement ja schon,>> nein, hat es nicht.
Doch. Der Effekt ist, dass foo den Wert 0 bekommt. Was danach damit
passiert, ist dafür erstmal egal. Ein "statement with no effect" wäre
sowas wie:
1
3;
> foo ist eine lokale variable (also i.d.R. auf dem Stack), by value> übergeben. Ein Schreiben auf foo hätte nur dann einen Sinn, wenn sie> danach nochmal verwendet würde. Das ist hier nicht der Fall.
Richtig. Da wäre aber eher so eine Warnung wie das oben schon erwähnte
"variable ‘foo’ set but not used" angebracht.
Rolf M. schrieb:> Der Effekt ist, dass foo den Wert 0 bekommt
Naja, aber es hat keinen Einfluss auf den Programmverlauf.
Der Compiler darf auch ohne Optimierung das ganze if wegschmeißen, da
foo by value übergeben ist und *foo nicht volatile.
Hat eine Anweisung if(1==0){} auch einen Effekt? Muss dann ein Vergleich
stattfinden?
Achim S. schrieb:> Rolf M. schrieb:>> Der Effekt ist, dass foo den Wert 0 bekommt>> Naja, aber es hat keinen Einfluss auf den Programmverlauf.
Richtig. Das ist aber dann Sache des Optimizers. Hier muss man auch
unterscheiden zwischen dem, was im Code steht und dem, was der Compiler
dann daraus macht. Auf C-Ebene bekommt foo erstmal den Wert 0, und damit
ist ein Effekt da. So ist im Kontext dieser Warnung zumindest der
Begriff "effect" zu verstehen.
Gemeint ist nicht, ob es für den weiteren Programmablauf eine Rolle
spielt, sondern ob das Statement selbst auf C-Ebene überhaupt irgendwas
tut.
Wenn du ganz ohne den weiteren Kontext nur die Zeile
1
foo=0;
sehen würdest, würdest du da auch sagen, dass das keinen Effekt hat?
Oder würdest du sagen: "Damit gebe ich foo den Wert 0"?
> Der Compiler darf auch ohne Optimierung das ganze if wegschmeißen, da> foo by value übergeben ist und *foo nicht volatile.
Wieso "ohne Optimierung"? Wenn er das wegschmeißt, ist das ganz klar
eine Optimierung.
> Hat eine Anweisung if(1==0){} auch einen Effekt? Muss dann ein Vergleich> stattfinden?
Gute Frage. Das ist wohl Ansichtssache.
In C sind Statements ohne Auswirkungen bzw. herausoptimierte Statements
die Regel.
z.B. bei der Verwendung von Abfragen in typunabhängigen Makros, wo immer
nur eine Abfrage zur Compilezeit zutrifft.
Deshalb kann der Compiler das nicht warnen.
Torsten R. schrieb:>> Der wesentliche Unterschied wäre, dass man in C++ ausdrücken kann, dass> &foo nicht 0 sein kann.
Und wenn es aus anderen Gründen unbedingt ein Zeiger sein muss, dann
sollte man
auch z.B. not_null<> statt rohe Zeiger verwenden. Damit ist der
Aufrufer gezwungen, den Vertrag einzuhalten und der Aufgerufene braucht
es nicht zu prüfen...
Und generell siehe CPL.1:
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-cpl
MaWin schrieb:> Deshalb kann der Compiler das nicht warnen.
Kann er sehr wohl, falls es sich um eine Variable handelt, die zwar
initialisiert, aber ansonsten nie benutzt wird. Macht er auch.
Dürfte aber für das Problem des TEs nichts bringen, da er uns ja nicht
seinen kompletten Code, sondern nur ein Stück daraus gezeigt hat.
Jörg W. schrieb:> Kann er sehr wohl, falls es sich um eine Variable handelt, die zwar> initialisiert, aber ansonsten nie benutzt wird. Macht er auch.
Ja in diesem einen Spezialfall tut er es. In 1000 anderen Fällen tut er
es nicht.