Forum: Compiler & IDEs Nullpointer-Frage


von lex (Gast)


Lesenswert?

Hallo,

folgendes hab ich im Netz heute gelesen:
1
Laut Sprachdefinition wird ein integraler konstanter Ausdruck mit dem Wert 
2
0 zu einem Null-Zeiger, wenn er einem Zeiger zugewiesen oder auf 
3
Gleichheit mit einem Zeiger verglichen wird (Äquivalenzvergleich). Die 
4
Umgebung (in aller Regel wohl die Übersetzungsumgebung) muß in einem 
5
solchen Fall feststellen, dass ein Null-Zeiger benötigt wird und einen 
6
Wert für den entsprechenden Typ eintragen. Deshalb sind die folgenden 
7
Code-Fragmente einwandfrei:
8
9
      char *z = 0;
10
      if (z != 0)
Allerdings hab ich schon öfter folgende Konstrukte in Quellcodes 
gesehen:
1
#define NULL_PTR  ((void *)0)
Bedeutet das, NULL_PTR ist hier garkein Nullpointer im C-Sinne?
Sondern ein Zeiger auf die Adresse 0x00?

Weiter: wenn konsequent diese Definition verwendet wird sollte ja nichts 
passieren.
Kommen allerdings Bibliotheksfunktionen ins Spiel die einen "echten" 
Nullpointer zurückgeben wirds aber kritisch, oder?

Grüße,
lex

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

lex schrieb:

> Bedeutet das, NULL_PTR ist hier garkein Nullpointer im C-Sinne?
> Sondern ein Zeiger auf die Adresse 0x00?

Ja, das bedeutet es.

> Kommen allerdings Bibliotheksfunktionen ins Spiel die einen "echten"
> Nullpointer zurückgeben wirds aber kritisch, oder?

Sagen wir mal so: in allen praktisch relevanten Fällen auf heutigen
Maschinen nicht, da der Nullzeiger wirklich auch der Zahl 0
entspricht.  Ein standardgerechtes Programm sollte aber nicht
versuchen, einen Nullzeiger selbst zu definieren, sondern entweder
wirklich auf 0 vergleichen (dann muss sich der Compiler oder die
Bibliothek ggf. um eine Umwandlung des Bitmusters kümmern, falls eine
exotische Zielmaschine sowas benötigt), oder die Konstante NULL
benutzen, die bspw. von <stdlib.h> oder <stdio.h> zur Verfügung
gestellt werden muss.

von Sven P. (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> lex schrieb:
>
>> Bedeutet das, NULL_PTR ist hier garkein Nullpointer im C-Sinne?
>> Sondern ein Zeiger auf die Adresse 0x00?
>
> Ja, das bedeutet es.
Ne, tuts nicht...
'Adresse' ist im C-Umfeld ein ziemlich gefährliches Wort. Man sollte 
unbedingt trennen zwischen den Adressen innerhalb von C und den 
Adressen, die auf der zugrundeliegenden Maschine benutzt werden. Das ist 
exakt das, was Jörg mit den Bitmustern meint.

Aber in diesem Fall ist sogar ausdrücklich spezifiziert, dass '(void*)0' 
effektiv den Null-Zeiger liefert (ISO/IEC9899:1999 TC2, §6.3.2.3 Abs. 
3).

von Charlie (Gast)


Lesenswert?

Jörg Wunsch schrieb:
>> Bedeutet das, NULL_PTR ist hier garkein Nullpointer im C-Sinne?

Nein.

>> Sondern ein Zeiger auf die Adresse 0x00?

Nein.

> Ja, das bedeutet es.

Nein!

http://c-faq.com/null/index.html beantwortet vermutlich alle Fragen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Charlie schrieb:
>> Ja, das bedeutet es.
>
> Nein!

Hatte Sven doch schon ausreichend klargestellt.

von Detlev T. (detlevt)


Lesenswert?

Da bin ich beim AVR 'mal auf die Nase gefallen. Ich hatte Zeiger auf 
Werte im EEPROM diese und - vorsichtig wie ich bin - vor Benutzung auf 
NULL getestet. Nur fängt der Adressbereich für EEPROM halt bei 0 an...

von Sven P. (Gast)


Lesenswert?

Detlev T. schrieb:
> Da bin ich beim AVR 'mal auf die Nase gefallen. Ich hatte Zeiger auf
> Werte im EEPROM diese und - vorsichtig wie ich bin - vor Benutzung auf
> NULL getestet. Nur fängt der Adressbereich für EEPROM halt bei 0 an...

Das ist egal, C kennt keinen Adressbereich. Genau aus diesem Grund ist 
'(void*)0' (also NULL) nicht auch identisch mit Adresse 0.

von Bronco (Gast)


Lesenswert?

Sven P. schrieb:
>> Da bin ich beim AVR 'mal auf die Nase gefallen. Ich hatte Zeiger auf
>> Werte im EEPROM diese und - vorsichtig wie ich bin - vor Benutzung auf
>> NULL getestet. Nur fängt der Adressbereich für EEPROM halt bei 0 an...
>
> Das ist egal, C kennt keinen Adressbereich. Genau aus diesem Grund ist
> '(void*)0' (also NULL) nicht auch identisch mit Adresse 0.

Was? Es wird doch der gleiche Assembler/Maschinen-Code erzeugt, wie soll 
dann die Zielhardware nachher den Unterschied erkennen?

Beispiel:
1
char* myPtr = (char*) 0;
2
if ( myPtr == ((char*)0) )
erzeugt doch den gleichen Assembler/Maschinen-Code wie
1
char* myPtr = (void*) 0;
2
if ( myPtr == ((void*)0) )
"myPtr" speichert doch nur einen Wert (den man als Speicheradresse 
interpretieren kann), aber nicht eine Qualifizierungsinformation, ob 
dieser Wert als char* oder void* übergeben wurde.

von Markus F. (mfro)


Lesenswert?

> Das ist egal, C kennt keinen Adressbereich. Genau aus diesem Grund ist
> '(void*)0' (also NULL) nicht auch identisch mit Adresse 0.

???

Sondern?
Wie willst Du einen void-Zeiger auf die Adresse 0L von NULL 
unterscheiden?

von Karl H. (kbuchegg)


Lesenswert?

Sven P. schrieb:

> Das ist egal, C kennt keinen Adressbereich. Genau aus diesem Grund ist
> '(void*)0' (also NULL) nicht auch identisch mit Adresse 0.

Konzeptionell.

Als Bitmuster allerdings auf einem AVR letzten Endes schon.

Und genau da liegt auch das Problem mit dieser ganze 
Null-Pointer-Konstant Idee. Als Idee nicht schlecht, aber in der Praxis 
bringts nix, denn irgendeinen Wert wird man immer als die 
Null-Pointer-Konstante vereinbaren, der mit der für die spezielle CPU 
typischen Pointergröße prinzipiell darstellbar ist. Und damit hat man 
auch immer das Problem, dass dieser Wert in der Praxis regulär vorkommen 
könnte. Die Idee, dass der Wert der Null-Pointer-Konstante dergestalt 
ist, dass er nicht als regulärer Pointerwert auf dieser speziellen 
Hardware nicht auch vorkommen kann, ist zwar nett gedacht, in der Praxis 
aber nicht ohne zusätzliche Hilfsmittel (wie zb 1 Byte mehr als die 
normale Adressierung auf dieser Maschine verlangt) durchführbar. Das der 
Adresswert 0 für die Null-Pointer-Konstante gut funktioniert, ist mehr 
der Tatsache geschuldet, dass in der überwiegenden Mehrzahl der Fälle 
die Speicheradresse 0 vom Runtime-System sowieso nicht benutzbar ist 
(weil da zb das Betriebssystem irgendwelche Werte liegen hat und der 
Memory Allokierer diese Adresse niemals liefern wird) als das es der 
Tatsache geschuldet ist, dass die Idee hinter dieser 
Null-Pointer-Konstante so genial wäre. Hier ist man IMHO weit über das 
Ziel hinausgeschossen, ohne tatsächlich etwas zu erreichen.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Null-Pointer-Konstant Idee. Als Idee nicht schlecht

Muss mich korrigieren.
Das ist noch nicht mal als Idee besonders gut.
Wobei Stroustrup die Idee in C++ IMHO noch weiter vermurkst hat.
In C ist wenigstens ein
#define NULL ((void*)0)
zugelassen. Wohlgemerkt: zugelassen. D.h. das ist nichts womit ich als 
Programmierer rechnen kann.
In C++ ist es noch nicht mal das. Was dann zu so seltsamen Blüten 
treibt, wie diese hier
1
void foo( char* str )
2
{
3
  ...
4
}
5
6
void foo( double val )
7
{
8
  ...
9
}
10
11
int main()
12
{
13
  foo( NULL );
14
}
Preisfrage: welche Funktion wird aufgerufen?
Antwort: die Version, die das double Argument nimmt. Und dabei hab doch 
extra wunderschön NULL benutzt um anzudeuten, dass ich hier einen 
Pointer-Kontext habe. Entschärfen lässt sich das ganze nur, indem ich 
selbst die vermeintliche Null-Pointer-Konstante explizit nochmals in 
einen Pointer caste
1
int main()
2
{
3
  foo( (char*)NULL );
4
}
Sinnig ist das nicht gerade.
Und das bei einer Sprache, die eigentlich die schlimmsten C Fallstricke 
'ausmerzen' wollte. Denn auch in C kann mich dieses Problem beißen: bei 
variadischen Funktionen. Aber die sind, abgesehen von printf, eher 
selten.

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> In C++ ist es noch nicht mal das. Was dann zu so seltsamen Blüten
> treibt, wie diese hier

gibt es aus dem Grund nicht in C++ das nullptr?

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Karl Heinz Buchegger schrieb:
>> In C++ ist es noch nicht mal das. Was dann zu so seltsamen Blüten
>> treibt, wie diese hier
>
> gibt es aus dem Grund nicht in C++ das nullptr?

Ja. In C++09.
Also seit rund 4 Jahren :-)

Nicht schlecht für ein Problem, welches in 30 Jahren für unzählige 
schlaflose Nächte verantwortlich war.

Alles was Stroustrup 1979 hätte tun müssen, wäre zu definierten, dass in 
C++ NULL immer und überall als
#define ((void*)0)
zu definieren ist und das Problem wäre keines mehr gewesen.

von Matthias L. (Gast)


Lesenswert?

>Preisfrage: welche Funktion wird aufgerufen?

Lässt sowas der Compiler zu? Zwei identische Funktionen bzw. 
Funktionsnamen?

Kommt da nicht sowas wie "redefinition of function..."

??

von Peter II (Gast)


Lesenswert?

Matthias Lipinsky schrieb:
> Kommt da nicht sowas wie "redefinition of function..."

nein nicht bei C++ das sind normale überladungen mit anderer signatur.

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:
>>Preisfrage: welche Funktion wird aufgerufen?
>
> Lässt sowas der Compiler zu? Zwei identische Funktionen bzw.
> Funktionsnamen?

C++

C++ hat einige Altlasten von C geerbt. Und in vielen Fällen tatsächliche 
Verbesserung gebracht.
Aber hier hat BS IMHO danebengehauen.
Ich teile seine Abneigung für Makros in C++. Aber die Verwendung von 
NULL dadurch einzudämmen (weil es ein Makro ist, igitt), dass man

  if( ptr == 0 )

möglichst ohne Cast eintippbar macht, war der falsche Weg. Denn 
ironischerweise musste er in die Sprachdefinition hier eine Ausnahme für 
Pointeroperationen in denen das Integer-Literal 0 vorkommt, einführen, 
damit das überhaupt syntaktisch korrekt wird :-)

von Matthias L. (Gast)


Lesenswert?

>nein nicht bei C++

Ah ok.


>das sind normale überladungen mit anderer signatur.

Es gilt dann wohl immer die letzte Definition?

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:
>>nein nicht bei C++
>
> Ah ok.
>
>
>>das sind normale überladungen mit anderer signatur.
>
> Es gilt dann wohl immer die letzte Definition?

No.
Es gilt die Funktion, deren Signatur am besten zu den Datentypen der 
Argumente an der Aufrufstelle passt (kurz zusammengefasst. Das ganze 
Thema ist bei Klassenhierarchien etwas komplexer - Stichwort: 
König-Lookup - aber im Endeffekt läuft es darauf hinaus, dass aus einer 
Menge von Kandidaten diejenige Funktion ausgewählt wird, bei der am 
wenigsten Konversionen der Argumentdatentypen des Aufrufes notwendig 
sind)

von Sven P. (Gast)


Lesenswert?

Bronco schrieb:
> Sven P. schrieb:
>>> Da bin ich beim AVR 'mal auf die Nase gefallen. Ich hatte Zeiger auf
>>> Werte im EEPROM diese und - vorsichtig wie ich bin - vor Benutzung auf
>>> NULL getestet. Nur fängt der Adressbereich für EEPROM halt bei 0 an...
>>
>> Das ist egal, C kennt keinen Adressbereich. Genau aus diesem Grund ist
>> '(void*)0' (also NULL) nicht auch identisch mit Adresse 0.
>
> Was? Es wird doch der gleiche Assembler/Maschinen-Code erzeugt,
Nein, eben nicht.

Ein Zeiger auf NULL, also etwa '(void*)0' ist immer verschieden von 
allen anderen gültigen Zeigern. Auch auf Maschinen und Prozessoren, bei 
denen der Speicher ab '0' gezählt wird.

> Beispiel:
>
1
> char* myPtr = (char*) 0;
2
> if ( myPtr == ((char*)0) )
3
>
> erzeugt doch den gleichen Assembler/Maschinen-Code wie
>
1
> char* myPtr = (void*) 0;
2
> if ( myPtr == ((void*)0) )
3
>
Richtig und falsch. Es erzeugt hier denselben Maschinencode, weil ein 
Zeiger auf 'void' dieselbe Darstellung wie ein Zeuger auf 'char' hat 
(ISO/IEC9899:1999TC2 §6.2.5p26). Das Beispiel ist allerdings ungünstig 
gewählt.

Ich wähle mal ein günstigeres:
1
double *dp = (double *) 0;
2
if (dp == (void *) 0)
3
4
/* und */
5
char *cp = (char *) 0;
6
if (cp == (void *) 0)
Also zwei verschiedene Zeigertypen und das magische NULL.

Daraus wird nicht zwangsläufig derselbe Maschinencode. Aus mehreren 
Gründen. Es ist einerseits in C nicht erlaubt, verschiedene Zeigertypen 
miteinander zu vergleichen. Lediglich Zeiger auf kompatible Typen (...) 
miteinander sowie Vergleiche mit dem void-Zeiger sind zulässig.
Außerdem müssen verschiedene Zeigertypen noch nichtmal gleich groß im 
Speicher abgelegt werden. Die könnte ja beispielsweise in total 
verschiedene Speicher zeigen.

Schließlich könnte genau das von dir beschriebene Problem eintreten: Die 
Speicherstelle '0' ist nutzbar.

> "myPtr" speichert doch nur einen Wert (den man als Speicheradresse
> interpretieren kann),
Richtig. Aber den interpretierst nicht du, sondern der Compiler. Wenn er 
möchte, könnte er die Zuordnung zwischen Wert des Zeigers und 
Speicherstelle im RAM auch rückwärts festlegen. Oder einfach zufällig, 
wenn er sich merkt, was wohin landete.

von Stefan E. (sternst)


Lesenswert?

Matthias Lipinsky schrieb:
>>das sind normale überladungen mit anderer signatur.
>
> Es gilt dann wohl immer die letzte Definition?

Nein, die die "passt".
1
void print ( const char * str );
2
void print ( int num );
Mit print("Hallo") wird die erste Funktion aufgerufen, mit print(42) die 
zweite.

von Sven P. (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> In C ist wenigstens ein
> #define NULL ((void*)0)
> zugelassen. Wohlgemerkt: zugelassen. D.h. das ist nichts womit ich als
> Programmierer rechnen kann.
Auf was kann man sich denn hier nicht verlassen?

von Sven P. (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Preisfrage: welche Funktion wird aufgerufen?

Garkeine, der Aufruf mit '0' als Argument ist nicht eindeutig und muss 
zum Compiler-Fehler führen...

von Karl H. (kbuchegg)


Lesenswert?

Sven P. schrieb:
> Karl Heinz Buchegger schrieb:
>> Preisfrage: welche Funktion wird aufgerufen?
>
> Garkeine, der Aufruf mit '0' als Argument ist nicht eindeutig und muss
> zum Compiler-Fehler führen...


OK.
Hab ich nicht bedacht.
Gut. Mach aus dem double einen int und gleiche Frage.

von Karl H. (kbuchegg)


Lesenswert?

Sven P. schrieb:
> Karl Heinz Buchegger schrieb:
>> In C ist wenigstens ein
>> #define NULL ((void*)0)
>> zugelassen. Wohlgemerkt: zugelassen. D.h. das ist nichts womit ich als
>> Programmierer rechnen kann.
> Auf was kann man sich denn hier nicht verlassen?

variadische Funktionen, die eine ungecastete NULL nicht als Pointer 
übernehmen sondern als int.

Ist sizeof(int) != sizeof(void*), dann hast du Ärger.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Aber die Verwendung von
> NULL dadurch einzudämmen (weil es ein Makro ist, igitt), dass man
>
>   if( ptr == 0 )
>
> möglichst ohne Cast eintippbar macht, war der falsche Weg.

Ja, er hätte es ja in der Hand gehabt, NULL als reserviertes Wort
einzuführen.  Dann muss man das nicht als Makro auflösen, sondern
der Compiler bildet es direkt in seinen Syntaxbaum ab.

von Markus F. (mfro)


Lesenswert?

Sven P. schrieb:

> Ein Zeiger auf NULL, also etwa '(void*)0' ist immer verschieden von
> allen anderen gültigen Zeigern. Auch auf Maschinen und Prozessoren, bei
> denen der Speicher ab '0' gezählt wird.
>

Das läuft jetzt irgendwie auf die m.E. sehr philosophische Diskussion 
hinaus, ob dasselbe etwas anderes ist, wenn man es nur anders nennt.

In meiner Welt ist NULL - wie jeder andere Zeigertyp, der den Wert 0L 
hat - ein Zeiger auf die Adresse 0, der sich - solange die Maschine es 
zulässt (und viele tun das) - dereferenzieren - mit dem sich also 
"arbeiten" - läßt.

Daß man Zeiger mit diesem Wert (der selten genug tatsächlich benötigt 
wird) dazu benutzen kann, um bestimmte (Sonder-/Fehler-) Situationen zu 
kennzeichnen, steht dem keinesfalls entgegen, finde ich.Genausogut hätte 
man auch MAXINT verwenden können, aber die Erfinder fanden 0L aus 
irgendwelchen Gründen halt praktischer.

von Karl H. (kbuchegg)


Lesenswert?

Markus F. schrieb:
> Genausogut hätte
> man auch MAXINT verwenden können, aber die Erfinder fanden 0L aus
> irgendwelchen Gründen halt praktischer.

Der Grund ist sehr einfach.
Weil man Pointer dann in einem boolschen Kontext benutzen kann, ohne 
irgendwelche Sonderfälle, was wiederrum sehr praktisch beim Abfragen auf 
gültig/ungültig ist.

  if( ptr )


Das ist schon ok.


>> Ein Zeiger auf NULL, also etwa '(void*)0' ist immer verschieden von
>> allen anderen gültigen Zeigern. Auch auf Maschinen und Prozessoren, bei
>> denen der Speicher ab '0' gezählt wird.
>>
>
> Das läuft jetzt irgendwie auf die m.E. sehr philosophische Diskussion
> hinaus, ob dasselbe etwas anderes ist, wenn man es nur anders nennt.

Das seh ich auch so.
Denn in der Praxis ist diese Vorgabe nicht oder nur mit enormen 
Resourcenverbrauch durchführbar. Das ist wie ein spezieller Wert für 
int, der von allen mit int darstellbaren Werten verschieden sein soll. 
In der Praxis nicht machbar.

von Karl H. (kbuchegg)


Lesenswert?

Jörg Wunsch schrieb:
> Karl Heinz Buchegger schrieb:
>> Aber die Verwendung von
>> NULL dadurch einzudämmen (weil es ein Makro ist, igitt), dass man
>>
>>   if( ptr == 0 )
>>
>> möglichst ohne Cast eintippbar macht, war der falsche Weg.
>
> Ja, er hätte es ja in der Hand gehabt, NULL als reserviertes Wort
> einzuführen.  Dann muss man das nicht als Makro auflösen, sondern
> der Compiler bildet es direkt in seinen Syntaxbaum ab.

Und genau so ist es dann ja letztenedes auch gekommen :-)
Nur 30 Jahre und Milliarden von Lines of Code zu spät.

von Sven P. (Gast)


Lesenswert?

Markus F. schrieb:
> In meiner Welt ist NULL - wie jeder andere Zeigertyp, der den Wert 0L
> hat - ein Zeiger auf die Adresse 0, der sich - solange die Maschine es
> zulässt (und viele tun das) - dereferenzieren - mit dem sich also
> "arbeiten" - läßt.
Naja, das ist ja schon auf deinem PC nicht mehr der Fall. Da steckt noch 
die MMU dazwischen.

Die Implementierung des NULL ist natürlich am simpelsten, wenn man den 
auch tatsächlich auf Speicherstelle '0' setzt. Das vereinfacht ja qausi 
schon das Assembly, weil man nur auf '0' testen muss. Aber es ist halt 
nichts, auf das man sich verlassen kann.

Solche Diskussionen haben ganz stark akademischen Charakter, da stimme 
ich aber zweifellos zu. Allerdings sind sie manchmal notwendig, um 
subtile Fehler zu begreifen.

von jack (Gast)


Lesenswert?

Und wie würde ich bei meinem Atmel zB auf die Adresse 0 zugreifen?

Char *ptr = 0;
DoSomething(*ptr);

So?

von Karl H. (kbuchegg)


Lesenswert?

jack schrieb:
> Und wie würde ich bei meinem Atmel zB auf die Adresse 0 zugreifen?
>
> Char *ptr = 0;
> DoSomething(*ptr);
>
> So?

Ja.
Bei den AVR Compilern hat die Null-Pointer-Konstante auch tatsächlich 
alle Bits auf 0.

von jack (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ja.
Danke.

Mit der Antwort wär ich glücklich gewesen, aber...
> Bei den AVR Compilern hat die Null-Pointer-Konstante auch tatsächlich
> alle Bits auf 0.
...verwirrt mich jetzt.

Hat das was mit meiner Frage zu tun?
Nachdem was ich hier gelesen habe ist:
void *ptr = 0;     ein NULL-Pointer
char *ptr = 0;     ein Zeiger auf die bei AVRs gültige Adresse 0x00

Dennoch sind doch im zweiten Fall auch alle Bits auf 0, richtig?
Nur durch die Deklaration mit void* wird Ersteres vom Compiler als 
NULL-Pointer interpretiert. Die interne Darstellung müsste ja gleich 
sein bzw. keine Rolle spielen.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

jack schrieb:
> ein Zeiger auf die bei AVRs gültige Adresse 0x00
Im Prinzip ja, allerdings liegt dort das Register R0, es sit also 
vermutlich keine gute Idee überhaupt dorthin direkt zu schreiben auch 
wenn es möglich wäre, ich denke nicht das der Compiler erkennt das du 
ihm gerade eines seiner Register überbretzelst.

Und genau das ist doch der Punkt: Ich arbeite doch (hoffentlich) mit vom 
Compiler verwalteten Variablen, wenn nicht (wie beim EEPROM der oben 
angesprochen wurde) kann ich mich auch nicht auf die "Null-Pointer" 
Konvention verlassen oder muss mir was anderes ausdenken.

von Bronco (Gast)


Lesenswert?

jack schrieb:
> void *ptr = 0;     ein NULL-Pointer
> char *ptr = 0;     ein Zeiger auf die bei AVRs gültige Adresse 0x00

Beim AVR GCC kann man das NULL-Pointer-Konzept nicht so einfach 
realisieren, da der Compiler nicht zwischen gültiger Adresse 0 und 
NULL-Pointer unterscheidet.
Der Compiler müßte dazu zu jedem Pointer zusätzliche Informationen 
speichern (was es z.B. beim 8051 gibt, aber aus ganz anderen Gründen).

Natürlich kannst Du für Deine Anwendung definieren, daß die Zahl 0 als 
Erkennung von ungültigem Inhalt verwendet wird, wenn Du Dir sicher bist, 
daß Du niemals auf die Adresse 0 zugreifen mußt.
Wenn Du z.B. Objekte im RAM behandelst, wirst Du niemals Adresse 0 
verwenden, da dort die Register liegen.

von DirkB (Gast)


Lesenswert?

Bei den allermeisten Prozessoren liegt an der realen Adresse Null 
irgendein prozessorspezifischer Kram.
Sei es Register, Zeropage, ROM, Resetadressen, ...
Also Sachen, die man in "normalen" Programmen gar nicht braucht.

Und bei den anderen Programmen kann über den NULL-Pointer auch darauf 
zugreifen (wenn die Rechte dafür vorhanden sind)

von Karl H. (kbuchegg)


Lesenswert?

jack schrieb:
> Karl Heinz Buchegger schrieb:
>> Ja.
> Danke.
>
> Mit der Antwort wär ich glücklich gewesen, aber...
>> Bei den AVR Compilern hat die Null-Pointer-Konstante auch tatsächlich
>> alle Bits auf 0.
> ...verwirrt mich jetzt.
>
> Hat das was mit meiner Frage zu tun?
> Nachdem was ich hier gelesen habe ist:
> void *ptr = 0;     ein NULL-Pointer
> char *ptr = 0;     ein Zeiger auf die bei AVRs gültige Adresse 0x00
>
> Dennoch sind doch im zweiten Fall auch alle Bits auf 0, richtig?

genau.

> Nur durch die Deklaration mit void* wird Ersteres vom Compiler als
> NULL-Pointer interpretiert.

:-)
Um die Verwirrung aufzulösen.
Der C-Standard sagt nichts dazu, welchen konkreten Typ der Pointer haben 
muss. Ob der ein void* oder ein char* ist, spielt überhaupt keine Rolle. 
Es ist die reine Zuweisung von 0 an einen Pointer, die den Compiler dazu 
veranlasst, die 0 (!) als Null-Pointer-Konstante anzusehen und 
gegebenfalls diese 0 durch ein anderes Bitmuster auszutauschen. Die 0, 
und ihre Verwendung in einem Pointer-Kontext ist der springende Punkt!

Aber auf dem AVR hat die Null-Pointer-Konstante ebenfalls den Zahlenwert 
0. So wie auf den meisten Systemen. Ich kenne eigentlich kein System, 
bei dem das anders wäre.

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wobei Stroustrup die Idee in C++ IMHO noch weiter vermurkst hat.

Das dürfte damit zusammenhängen, dass C++ keine cast-freie Konvertierung 
von void* nach sonstwas* zulässt. Das würde nämlich allzu leicht das 
ganze Typenkonzept von Klassen aushebeln, insbesondere bei multiple 
inheritance (da wird ggf. die Adresse umgerechnet).

Richtiger wäre allerdings ein nullptr gewesen.

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Aber auf dem AVR hat die Null-Pointer-Konstante ebenfalls den Zahlenwert
> 0. So wie auf den meisten Systemen. Ich kenne eigentlich kein System,
> bei dem das anders wäre.

Heute nicht mehr. Gab es aber. Bei den Transputern war der Adressbereich 
mit Vorzeichen realisiert, beim 16-Bitter T212 also -0x8000...+0x7FFF. 
Die 0 folglich mitten drin.

von Sven P. (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Es ist die reine Zuweisung von 0 an einen Pointer, die den Compiler dazu
> veranlasst, die 0 (!) als Null-Pointer-Konstante anzusehen und
> gegebenfalls diese 0 durch ein anderes Bitmuster auszutauschen.
JA JA JA RICHTIG! :-)

Hach was war das alles so einfach, als man sich über sowas noch keine 
Gedanken gemacht hat...

von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:
> Karl Heinz Buchegger schrieb:
>> Aber auf dem AVR hat die Null-Pointer-Konstante ebenfalls den Zahlenwert
>> 0. So wie auf den meisten Systemen. Ich kenne eigentlich kein System,
>> bei dem das anders wäre.
>
> Heute nicht mehr. Gab es aber. Bei den Transputern war der Adressbereich
> mit Vorzeichen realisiert, beim 16-Bitter T212 also -0x8000...+0x7FFF.
> Die 0 folglich mitten drin.


Sachen gibts :-)

Im Ernst.
Im C-Standard sind einige Anachronsimen enthalten, die früher mal 
relevant waren, aber schon seit einiger Zeit komplett irrelevant sind
* Zeichensatz
  Die Zugeständnisse an EBCDIC (denn genau das waren sie letzten Endes)
  sind längst überholt. EBCID hat heute m.W. keine Bedeutung mehr.
  Auch IBM ist bei seinen Mainframes (bauen die überhaupt noch welche?)
  längst auf ASCII gewechselt.
* 2-er Komplement Arithmetik
  Ok, da mag es heute noch Ausnahmen geben, könnte sein. Ich denke
  allerdings nicht. Ich denke, es wäre kein Beinbruch, wenn man
  auch signed Arithmetik mit 2-er Kompl. Arithmetik im Hinterkopf
  aus seinem undefined Status holt. 1-er Komplement und sonstige
  Variationen sind längst tot, hat sich alles nicht wirklich
  bewährt.
* Floating Point
  Die ganze Welt verwendet mitlerweile die IEEE Funktionalität.
  OK, ältere Vaxen mit ihrem eigenen Floating Point Format haben
  dann das nachsehen. Würde mich allerdings wundern, wenn da noch
  groß Aufwand in die Neuentwicklung eines C-Compilers gesteckt wird.
* und eben die Sache mit der 0-Pointer-Konstanten
* was hab ich noch vergessen?

Denn die Sache ist doch die. Berücksichtigt man beim Programmieren all 
diese Dinge, dann hat man einen riesen Aufwand. Es würde mich sehr 
wundern, wenn man mehr als ein paar Prozentpunkte der real existierenden 
Programme nehmen könnte und auf eine Maschine mit einer derartigen 
Besonderheit laufen könnte, ohne dass es zu Problemen kommt. D.h. de 
facto hat sich die Computer-Industrie längst in all diesen Bereichen auf 
Standards eingeschossen. Die Zeit des ausprobierens ist längst vorbei. 
Die Flexibilität, die im C-Standard notwendig war um das alles unter 
einen Hut zu bringen, braucht man heutzutage gar nicht mehr. Aber ich 
weiß auch, dass die ISO-Gremien da sehr konservativ sind. Eher kann man 
die 10 Gebote ändern, als das etwas aus dem Standard rausfliegt, was da 
bereits drinn steht.

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz Buchegger schrieb:
>   Auch IBM ist bei seinen Mainframes (bauen die überhaupt noch welche?)

Und ob die noch welche bauen: http://www-03.ibm.com/systems/z

Sind zwar rein physisch nicht grösser als deren Highend-RISCs und auch 
nicht schneller, aber in Sachen Verfügbarkeit ziemlich Spitze.

>   Die ganze Welt verwendet mitlerweile die IEEE Funktionalität.

Sogar besagte Mainframes beherrschen schon ein Weilchen neben IBMs altem 
Hex-Fliesskommaformat auch das IEEE-Format.

von Markus F. (mfro)


Lesenswert?

Karl Heinz Buchegger schrieb:
>> Heute nicht mehr. Gab es aber. Bei den Transputern war der Adressbereich
>> mit Vorzeichen realisiert, beim 16-Bitter T212 also -0x8000...+0x7FFF.
>> Die 0 folglich mitten drin.


Da gab's noch mehr: http://c-faq.com/null/machexamp.html . Überwiegend 
obsolet, trotzdem interessant zu wissen, finde ich.

von (prx) A. K. (prx)


Lesenswert?

A. K. schrieb:
> Sind zwar rein physisch nicht grösser als deren Highend-RISCs und auch
> nicht schneller, aber in Sachen Verfügbarkeit ziemlich Spitze.

Aufbau: http://www.redbooks.ibm.com/redpieces/abstracts/sg248049.html

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:

> * was hab ich noch vergessen?

Funktionen ohne Prototyp, register, auto, Trigraphen, Duff's device, ...
1
send (to, from, count)
2
register short *to, *from;
3
register count;
4
{
5
        register n = (count + 7) / 8;
6
        switch(count % 8) {
7
        case 0: do {    *to = *from++;
8
        case 7:         *to = *from++;
9
        case 6:         *to = *from++;
10
        case 5:         *to = *from++;
11
        case 4:         *to = *from++;
12
        case 3:         *to = *from++;
13
        case 2:         *to = *from++;
14
        case 1:         *to = *from++;
15
                } while(--n > 0);
16
        }
17
}

von Bronco (Gast)


Lesenswert?

Johann L. schrieb:
> Funktionen ohne Prototyp, register, auto, Trigraphen, Duff's device, ...

"I can't get enough of the wonderful Duff" (Die Simpsons)

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Karl Heinz Buchegger schrieb:
> * Zeichensatz
>   Die Zugeständnisse an EBCDIC (denn genau das waren sie letzten Endes)
>   sind längst überholt. EBCID hat heute m.W. keine Bedeutung mehr.
>   Auch IBM ist bei seinen Mainframes (bauen die überhaupt noch welche?)
>   längst auf ASCII gewechselt.

Ich bekomme recht viele Daten in EBSCDIC, natürlich in irren Varianten
um deutsche Umlaute unterzubringen ... Das gibt's echt noch ;)

von Matthias L. (Gast)


Lesenswert?

>Duff's device

Das ist doch auch so eine C-Eigenheit. Oder welche Sprache lässt es 
sonst noch zu, die CASE-Anweisung und die WHILE-Schleife gegenseitg so 
zu verschachteln...

von Simon H. (simi)


Lesenswert?

Ich war letztens mal an einem Vortrag über C. Der Dozent schloss mit der 
Bemerkung: C ist halt noch eine Sprache für Männer. :-)

von Rolf Magnus (Gast)


Lesenswert?

So schaut's aus. Was will ich mit so rosaroten Plüschsprachen? ;-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.
Lade...