Hallo zusammen,
was ist die beste Praxis wenn ich prüfen möchte, ob ein Speicherbereich
noch mit gültigen Werten initialisiert oder bereits wieder freigegeben
ist? Folgendes Beispiel:
1
#include<stdio.h>
2
#include<stdlib.h>
3
4
structpoint_t{
5
intx;
6
inty;
7
};
8
9
structpoint_t*createPoint(intnewX,intnewY){
10
structpoint_t*newPoint=NULL;
11
12
newPoint=malloc(sizeof(structpoint_t));
13
newPoint->x=newX;
14
newPoint->y=newY;
15
16
returnnewPoint;
17
}
18
19
intmain(void){
20
21
structpoint_t*new=NULL;
22
23
new=createPoint(4,2);
24
25
printf("%d / %d\n",new->x,new->y);
26
free(new);
27
printf("%d / %d\n",new->x,new->y);
28
29
returnEXIT_SUCCESS;
30
}
Das zweite printf gibt ja auch noch einen Wert aus, auch wenn dieser
hier Blödsinn ergibt. Theoretisch könnte er aber gültig sein. Mein
Ansatz wäre jetzt, eine Funktion "deletePoint" zu schreiben, dort ein
free zu machen und den Pointer auf NULL zu setzen. Das kapselt zwar
schön, ändert aber nichts am Ergebnis..
Gruß
Dennis
> [..] und den Pointer auf NULL zu setzen. Das kapselt zwar schön,> ändert aber nichts am Ergebnis..
Doch, das gibt dann nämlich eine Nullpointerdreference die Dir um die
Ohren fliegt (und zwar zurecht!).
Gültigkeit von Pointer prüft man ∗grundsätzlich∗ per Prüfung auf NULL.
Wer einen Pointer nutzt, ohne ihn geprüft zu haben, wird gesteinigt -
und der erkannte Fehler behoben.
Dennis S. schrieb:> was ist die beste Praxis
Testen, testen, testen. Programm-Quelltext nicht mit unötigen Checks
verunstalten.
Zum Testen Tools verwenden, die solche und ähnliche Fehler fangen.
Valgrind, -fstack-protector, MALLOC_CHECK_ usw.
g457 schrieb:> Doch, das gibt dann nämlich eine Nullpointerdreference die Dir um die> Ohren fliegt (und zwar zurecht!).
auf eine µC kann es auch null pointer geben die gültig sind. Adressen
fangen nun mal bei 0 an.
Hallo,
mit C hast du keine Möglichkeiten verlässlich zu prüfen ob irgend ein
Zeiger gültig ist oder nicht.
Kapseln ist die einzig gute Art damit umzugehen.
Das sollte man eh immer machen und nicht wild mit Zeigern hantieren.
Grüße,
> auf eine µC kann es auch null pointer geben die gültig sind. Adressen> fangen nun mal bei 0 an.
Das ist erstens in dieser Allgemeinheit falsch und zweitens hier
vollkommen fehl am Platz - hier gehts um PC-Programmierung.
Hmm... ich stehe gerade auf dem Schlauch. Habe folgendes zum Testen
ergänzt:
1
voiddeletePoint(structpoint_t*del){
2
free(del);
3
del=NULL;
4
}
In main:
1
...
2
deletePoint(new);
3
printf("%p",new);
Ich bekomme aber eine Ausgabe die ich nicht erwartet habe:
1
4 / 2
2
00372E88
Ich hätte jetzt eigentlich mit einem 00000000 gerechnet!
Gruß
Dennis
P.S.: Das Prüfen würde ich im "richtigen" Code natürlich machen, aber
wenn es grundlegend schon nicht klappt... Valgrind arbeite ich mich auch
gerade ein.
g457 schrieb:> Das ist erstens in dieser Allgemeinheit falsch und zweitens hier> vollkommen fehl am Platz - hier gehts um PC-Programmierung.
auch dort kann es eine Adresse 0 geben - im Linux Kernel hat das sogar
schon zu Problemen geführt.
Peter II schrieb:> auch dort kann es eine Adresse 0 geben - im Linux Kernel hat das sogar> schon zu Problemen geführt.
Mal ernsthaft.. sieht die Frage aus, als ob ich den Kernel umschreibe...
> auch dort kann es eine Adresse 0 geben - im Linux Kernel hat das sogar> schon zu Problemen geführt.
Ist das Speicher der über die C-Runtime mit der malloc-Familie angelegt
worden ist so wie hier? Dann ist die implementierung fehlerhaft. Und es
ist hier immernoch off-topic weils hier offensichtlich nicht um
Kernelcode geht.
Dennis S. schrieb:> was ist die beste Praxis wenn ich prüfen möchte, ob ein Speicherbereich> noch mit gültigen Werten initialisiert oder bereits wieder freigegeben> ist?
Generell, unabhängig von dem synthetischen "createPoint/freePoint"
Beispiel:
Wenn du irgendwo auf einen bereits freigegebenen Speicherbereich
zugreifst, ist das ein Fehler.
Fehler will man idealerweise finden und beheben, nicht verschleiern.
Ein Vorgehen mit "bei Free pointer auf NULL setzen, bei jedem
Pointer-Zugriff prüfen" macht genau das: den Fehler verschleiern.
Bei komplexeren Programmen: Woher weißt du, welche Kopien von deinem
Pointer noch existieren?
Wie soll dein "freePoint" wissen, welche anderen Pointer noch auf 0 zu
setzen sind? (Jaja, in C++ mit boost usw. wär das alles ganz einfach...)
Ein Check mit valgrind & co hingegen kann dir ganz klar sagen:
Speicher wurde in xxx.c, Zeile 12345 alloziert, freigegeben in yyy.c,
Zeile 5432, und anschliessend wurde böserweise in zzz.c, Zeile 333, auf
den freigegebenen Speicher zugegriffen.
> Ein Vorgehen mit "bei Free pointer auf NULL setzen, bei jedem> Pointer-Zugriff prüfen" macht genau das: den Fehler verschleiern.
Nö, im Gegenteil: Es haut Dir eine Nullpointerdereference (vergessene
Prüfung) oder eine lesbare Warnung auf $stderr nebst ∗erkanntem∗ Fehler
(Error condition/Failure/.. return code, lesbare Exception in C++) um
die Ohren [0]. Und es ∗verhindert∗ zuverlässig, dass Du versehentlich
nicht (mehr) gültigen Speicher benutzt.
> Bei komplexeren Programmen: Woher weißt du, welche Kopien von deinem> Pointer noch existieren?
Das ist ein anderes Problem.
> Ein Check mit valgrind & co hingegen kann dir ganz klar sagen:> Speicher wurde in xxx.c, Zeile 12345 alloziert, freigegeben in yyy.c,> Zeile 5432, und anschliessend wurde böserweise in zzz.c, Zeile 333, auf> den freigegebenen Speicher zugegriffen.
Nope, das funktioniert nur sehr eingeschränkt. Klar nutzt man diese
Tools ∗zusätzlich∗, aber keinesfalls ausschließlich. Die üblichen
Verdächtigen laufen nämlich nur zur Compilezeit (oder davor) und können
dabei nicht aller erkennen, oder verursachen einen massiven
Performanceverlust zur Laufzeit und können dann nur tatsächlich
abgelaufene Codepfade mit tatsächlich vorgekommenen Daten prüfen.
In letzterem Fall lässt man die Dinger dann aufgrund der unerträglich
schlechten Performance nur in 'Debugversionen' mit einem sehr begrenzten
Testdatensatz laufen und merkt dann in der 'Releaseversion' nicht, dass
man gerade ungültigen Speicher bearbeitet - klassischer Schuss in eigene
Knie.
Fehler wie ungültige Zeiger muss man grundsätzlich zur Laufzeit erkennen
können, denn nur da hat man die tatsächlichen Daten zur Verfügung und
nicht irgendeinen schlecht gewählten Unittestbeispieldatensatz der zu
fälligerweise funktioniert.
Nix für ungut.
[0] den erkannten Fehler im Code stillschweigend zu ignorieren anstatt
ihn zu propagieren ist selbstredend keine Option und bleibt hier deshalb
unerwähnt.
g457 schrieb:> Es haut Dir eine Nullpointerdereference (vergessene> Prüfung) oder eine lesbare Warnung auf $stderr nebst ∗erkanntem∗ Fehler> (Error condition/Failure/.. return code, lesbare Exception in C++) um> die Ohren
Vorausgesetzt, daß das die Zielhardware und Laufzeitumgebung auch
unterstützt. Was auf einem µP mit MMU leicht möglich ist, wird auf einem
µC schon deutlich schwieriger ... bis unmöglich, weil a) 0 eine völlig
legale Speicheradresse sein kann und b) es keine Hardwareunterstützung
zur Erkennung von Zugriffen auf "ungültige" Speicheradressen geben muss.
Aber gut, wir sind hier bei "PC-Programmierung", da gibt es eine MMU,
und entsprechende Mechanismen, solange nicht gerade im x86-Real-Mode
programmiert wird (was hoffentlich niemand mehr macht).
g457 schrieb:>> Ein Vorgehen mit "bei Free pointer auf NULL setzen, bei jedem>> Pointer-Zugriff prüfen" macht genau das: den Fehler verschleiern.>> Nö, im Gegenteil: Es haut Dir eine Nullpointerdereference (vergessene> Prüfung) oder eine lesbare Warnung auf $stderr nebst ∗erkanntem∗ Fehler> (Error condition/Failure/.. return code, lesbare Exception in C++) um> die Ohren [0].
Ja. In diesem Wischi Waschi Beispiel.
Nur sind die Dinge in der Praxis komplexer.
Hast du 2 Pointer auf denselben Speicher, was bei komplizierteren
Datenstrukturen schon mal vorkommt, dann haut dir eben keine Runtime
eine Null-Pointer Dereference für den 2.ten Pointer um die Ohren.
> Das ist ein anderes Problem.
Das ist exakt das Problem.
Im Trivialbeispiel da oben ist die Sache einfach und überschaubar. Aber
die vorgeschlagege Lösungsmethode verallgemeinert nun mal nicht für
komplexere Fälle. Bei Trivialbeispielen funktioniert sie gut. Allerdings
braucht man sie dort eher selten (mit ein wenig Erfahrung), eben weil
man noch alles gut überblicken kann. Und in den Fällen, in denen man
Hilfe benötigen würde, versagt sie aus den genannten Gründen.
> Fehler wie ungültige Zeiger muss man grundsätzlich zur Laufzeit erkennen können,
Kannst du aber nicht, weil es in C (Standard-C) keine Möglichkeit gibt
prüfen zu lassen, ob ein Pointer in einen gültig allokierten Speicher
zeigt. In einer Funktion, die einen Pointer als Argument kriegt, bist du
auf Deutsch gesagt, mit der Prüfung aufgeschmissen. Auf NULL kann man
noch prüfen, aber ein Pointer der nicht NULL ist, den musst du als
gültig annehmen - ob du willst oder nicht.
Peter II schrieb:> auf eine µC kann es auch null pointer geben die gültig sind. Adressen> fangen nun mal bei 0 an.
Und du hast scheinbar immer noch nicht begriffen, dass Zeiter keine
Adressen sind.
Nase schrieb:> Peter II schrieb:>> auf eine µC kann es auch null pointer geben die gültig sind. Adressen>> fangen nun mal bei 0 an.> Und du hast scheinbar immer noch nicht begriffen, dass Zeiter keine> Adressen sind.
Abgesehen davon ist das C ziemlich egal. Der NULL Pointer hat in C den
Wert 0. Egal ob da damit dann die tatsächliche Speicheradresse 0x00 oder
irgendeine andere nicht zugreifbar wird. Für einen C Programmierer ist
der Wert dieses 'Ich zeige nirgendwohin' Pointers immer 0. Wenn das auf
einer bestimmten Zielhardware anders sein soll (weil zb an der Adresse
0xFFFF kein Speicher liegt und sich daher 0xFFFF als Null-Pointer
anbieten würde), dann ist es die Sache des Compilers, eine derartige
Umsetzung zu machen.
Und ja. Das bedeutet, dass man eine Adresse aus dem potentiellen
Adressraum der Maschine opfert, um den Wert 'ungültig' darstellen zu
können, was normalerweise keine große Sache ist.
Nase (Gast) schrieb:
Peter II schrieb:
>> auf eine µC kann es auch null pointer geben die gültig sind. Adressen>> fangen nun mal bei 0 an.> Und du hast scheinbar immer noch nicht begriffen, dass Zeiter keine> Adressen sind.
Natürlich sind Zeiger "Adressen". Aus Assemblersicht ist gibt es nur
Adressen und Inhalte und sonst nichts. Und je nach Gebrauch steht der
Pointer Synonym für eine Adresse oder deren Inhalt. Deswegen gibt es
auch einen Adress & und einen Inhaltsoperator *.
Das Problem von euch Käseköpfen ist, ihr macht aus allem was C und
besonders C++ angeht eine Mystifizierung und an Pointern ist nichts
Mystisches.
Nase schrieb:> Karl Heinz schrieb:>> Der NULL Pointer hat in C den>> Wert 0.> Eben.
Eben nicht.
Der NULL Pointer hat den Wert NULL.
Auf welche 'Adresse' oder was auch immer das zeigt ist egal.
Stefan
Ach was schrieb:> Natürlich sind Zeiger "Adressen"
Ja und nein. Ok, ich formuliere es besser: Zeiger sind keine
Maschinenadressen.
Stefan schrieb:> Nase schrieb:>> Karl Heinz schrieb:>>> Der NULL Pointer hat in C den>>> Wert 0.>> Eben.>> Eben nicht.
Eben doch.
Es ist richtig: Auf welche (Maschinen-)Adresse der Null-Zeiger zeigt,
ist egal. Das interessiert in C aber nicht, da man nicht mit
(Maschinen-)Adressen arbeitet, sondern mit Zeigern.
Und für Standard-C ist eindeutig definiert, dass die Ganzzahl-Konstante
'0' zum Null-Zeiger wird[1]. Die einzige Freiheit der C-Umgebung ist es,
das Makro 'NULL' als '0' oder '0L' oder auch mit Cast als '(void *)0' zu
definieren[2].
[1] ISO/IEC 9899:1999 7.17§3, 6.3.2.3§3
[2] C99-Rationale 7.17§30
Εrnst B✶ schrieb:> Nase schrieb:>> Karl Heinz schrieb:>>> Der NULL Pointer hat in C den>>> Wert 0.>> Eben.>> http://c-faq.com/null/machexamp.html
Auch das ist in Ordnung.
Es ist dann aber Aufgabe des Compilers, dir all diese verschiedenen
Darstellungen des Null-Zeigers abzunehmen. Das heißt auch, dass er die
richtige Darstellung (z.B. 07777:0) einsetzt, wenn du in C den
Null-Zeiger mit '0' erzeugst. Oder dass er die richtige Instruktion
benutzt, wenn du gegen Null-Zeiger testest.
Damit hat der Null-Zeiger in C halt effektiv wieder den Wert '0'.
Möglicherweise eine andere Darstellung im Maschinenadressraum. Aber
Zeiger sind ja keine (Maschinen-)Adressen...
Εrnst B✶ (ernst) schrieb:
Nase schrieb:
>> Karl Heinz schrieb:>>> Der NULL Pointer hat in C den>>> Wert 0.>> Eben.> http://c-faq.com/null/machexamp.html
Wo immer es eine Regel gibt, gibt es auch Ausnahmen. Die Ausnahmen
BESTÄTIGEN aber jeweils immer die Regel und sind kein Gegenbeweis.
Es ist ja keine Ausnahme. Es ist eine der Möglichkeiten, die der
C-Standard anbietet. Vorallem aber ist es eine Sache, die mich als
C-Programmierer normalerweise nicht interessiert.
Nase (Gast) schrieb:
> Es ist ja keine Ausnahme. Es ist eine der Möglichkeiten, die der> C-Standard anbietet.
Doch, es ist eine Ausnahme. Auch Ausnahmen sind irgendwo in einem
Regelwerk beschrieben. Unsere Gesetzestexte sind voll von Ausnahmen.
"Gesetz A gilt immer, jedoch dann nicht, wenn B, C, D eintreten und E
sowie F gemeinsam vorhanden sind" usw.
In C ist das nicht anders.
Wen interessiert heute noch was ein "The old HP 3000 series" oder eine
The "CDC Cyber 180" mal irgendwann an "null pointers of 0xB00000000000"
implementiert hat, wenn beispielsweise 99% aller derzeitigen C/C++
Compilate dem Symbol NULL eine '0' zuschreiben?
> Vorallem aber ist es eine Sache, die mich als> C-Programmierer normalerweise nicht interessiert.
Diese ganzen kleinkarierten Diskussionen um die Auslegung von
Begrifflichkeiten in C/C++, die Poiner mystifizieren und mit Buzzwörtern
angeben, finde ich genauso anstrengend wie lästig. Die machen auch die
in C/C++ geschirbene Software nicht besser. Gerade an so Programmen wie
FreeCAD, das mir bei der kleinsten Fehlbedienung eine Exeption schmeißt
merke ich, wie weit dieser ganze geredete Klein-Klein-Formalismus dann
wirklich beim praktischen Programmieren auf der Strecke bleibt bzw.
nichts bewirkt. Da wird mir eine Datei beim Konvertieren erzeugt, die
gar keinen Inhalt hat. Da wird mir eine Fehlermeldung um die Ohren
gehauen, aber kein Hinweis, was zu tun ist, um diese krude Software
vernünftig zu bedienen. Grrr.
Nase (Gast) schrieb:
> Natürlich, wenn man die Grundlage nie begriffen hat, dann wird ganz> schnell alles zur Ausnahme...
Wer soll was nicht begriffen haben? Das worum es hier geht ist banal.
Scheinbar nicht banal genug, sonst würden nicht ständig wieder Leute
Zeiger und Adressen pauschal zusammenschmeißen und sich anschließend
wundern.
Wenn man sich den Unterschied - und ja, das mag in den meisten Fällen
nur eine Nuance sein - mal klar gemacht hat, erübrigt sich die gesamte
Diskussion.
Aber es scheint ja einfacher zu sein, die Ausnahme zur Regel zu erklären
und dann für jede Abweichung wieder Ausnahmen zu basteln. Ist mir nun zu
blöde.
Ach was schrieb:> Wen interessiert heute noch was ein "The old HP 3000 series" oder eine> The "CDC Cyber 180" mal irgendwann an "null pointers of 0xB00000000000"> implementiert hat, wenn beispielsweise 99% aller derzeitigen C/C++> Compilate dem Symbol NULL eine '0' zuschreiben?>
Darum gehts doch gar nicht.
Auch auf derartigen Systemen hätte man in C
1
void*pPtr=(void*)0;
geschrieben und es wäre der Job des Compilers, diese Zuweisung durch die
Zuweisung vo n0xB0000000000 zu ersetzen.
Als C Programmierer benutzt du IMMER den Zahlenwert 0 um den Null
Pointer auszudrücken. Da ist also nichts mit Ausnahme. Ganz im
Gegenteil. Das ist eines der wenigen Dinge, die tatsächlich auf allen C
Systemen gleich sind. Der Null-Pointer hat im C Programm den Wert 0.
Unabhängig davon, wie er auf dem fraglichen System tatsächlich
implementiert wird. Und ja, wenn man mit dem Debugger sich den Hex-Wert
einer derartigen Variable mit einem Null-Pointer ansehen würde, dann
würde man da ein anderes Bitmuster drinn finden. Trotzdem vergleicht ein
derartiger Null-Pointer bei
1
if(pPtr==0)
mit TRUE.
Zu deinen Ausführungen über FreeCad: Zweifellos gibt es auch schlechte
Software. Ich würde sogar soweit gehen und sagen, dass ein großer
Prozentsatz in diese Kategorie fällt. Aber das hat mit der
Programmiersprache recht wenig zu tun. Schon eher damit, dass die
Programmierer die Regeln ihrer Sprache nicht beherrschen.
Karl Heinz (kbuchegg) (Moderator) schrieb:
Ach was schrieb:
>> Wen interessiert heute noch was ein "The old HP 3000 series" oder eine>> The "CDC Cyber 180" mal irgendwann an "null pointers of 0xB00000000000">> implementiert hat, wenn beispielsweise 99% aller derzeitigen C/C++>> Compilate dem Symbol NULL eine '0' zuschreiben?>>> Darum gehts doch gar nicht.> Auch auf derartigen Systemen hätte man in C> void * pPtr = (void*)0;> geschrieben und es wäre der Job des Compilers, diese Zuweisung durch die> Zuweisung vo n0xB0000000000 zu ersetzen.
Das ist genau die Bestätigung meiner Aussage. Nur noch mal zur
Erinnerung, er ging darum das in C-Programmen der NULL Pointer den Wert
0 hat. Darauf hatte einer als Einwand ein Posting gebracht und ich
bekräftigte nur, DEINE Aussgage Karl Heinz, nämlich das hier (jedenfalls
war es so gemeint)
> void * pPtr = (void*)0;
Das der Compiler oder das BS hier den Wert dann möglicherweise (intern)
ersetzt durch ein n0xB0000000000 oder sonstwas wollte und würde ich
nicht in Abrede stellen.
Aber du hast recht, ich bin wohl davon ausgegangen, dass sich das
"Umbiegen" des Wertes auch schon in einem anderen Makro in einem der
C-Code Header, z.B. durch ein
/* Define NULL pointer value */
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
nur mit anderem Wert als 0 zeigt. (vielleict ist es auch so)
Mein Fehler!
> ... Und ja, wenn man mit dem Debugger sich den Hex-Wert> einer derartigen Variable mit einem Null-Pointer ansehen würde, dann> würde man da ein anderes Bitmuster drinn finden. Trotzdem vergleicht ein> derartiger Null-Pointer bei
Ich hab noch mal nachgeschaut. In der Win32-Speicherarchitektur vom
alten Win95 sind die untersten 4 kByte 0x00000FFF besonders geschützt,
gerade wegen u.a. "kein Zugriff - Null-Zeiger" (sowie 16-bit Windows und
MS-DOS). Bei der Implementation des Win32 in NT 3.5 war es schon der
Adressraum 0x0 bis 0x0000FFFF, also 64 kByte für das Erkennen
nichtinitialisierter Zeiger, also Null-Zeiger.
(Buch von Jeffrey Richter, Kapitel Virtuelle Adressräume)
> Zu deinen Ausführungen über FreeCad: Zweifellos gibt es auch schlechte> Software. Ich würde sogar soweit gehen und sagen, dass ein großer> Prozentsatz in diese Kategorie fällt. Aber das hat mit der> Programmiersprache recht wenig zu tun. Schon eher damit, dass die> Programmierer die Regeln ihrer Sprache nicht beherrschen.
FreeCAD ist nicht grundsätzlich schlecht. Es ist in gewisser Weise sogar
konkurrenzlos in der Vielfalt seiner Funktionen und
Konvertierungsmöglichkeiten. Man kommt kaum beim Thema 3D CAD ohne Geld
auszugeben an dem Teil vorbei (ebenso wie Blender). Leider hat das
Programm sowohl in der Bedienweise als auch im Abfangen von Fehlern
schlimme Macken. Ich hab das an dieser Stelle hier nur mal angeführt,
weil es mir auf den "Zeiger" geht, wie hier oft bis ins Klein-Klein
hinein auf Nebenkriegsschauplätzen sich ausargumentiert wird (über
Formalien, Definitionen usw.), während so bekannte Softwaren wie FreeCAD
(was übrigens recht beliebt ist) mit erheblichen Softwarefehlern oder
sagen wir Überraschungen dem Anwender gegenüber aufwartet. Wie oft mir
das Teil schon abgeschmiert ist kann ich gar nicht sagen.
Naja, ist halt so.
Ach was schrieb:> Aber du hast recht, ich bin wohl davon ausgegangen, dass sich das> "Umbiegen" des Wertes auch schon in einem anderen Makro in einem der> C-Code Header, z.B. durch ein>> /* Define NULL pointer value */> #ifndef NULL> #ifdef __cplusplus> #define NULL 0> #else> #define NULL ((void *)0)> #endif> #endif>> nur mit anderem Wert als 0 zeigt. (vielleict ist es auch so)
Dann würde nur noch ein direkter Vergleich mit "NULL" funktionieren,
nicht aber so etwas:
>> Das ist ein anderes Problem.>> Das ist exakt das Problem.
Nein eben nicht, siehe unten :-)
> Auf NULL kann man noch prüfen, aber ein Pointer der nicht NULL ist, den> musst du als gültig annehmen - ob du willst oder nicht.
Ebenst, genau das ist das Konzept. Jeder Zeiger, der nicht gültig ist,
hat NULL zu sein. Wenn man eine Ressource (hier: Speicher) freigibt, hat
man alle Nutzer (hier Halter von Zeigern) mittelbar oder unmittelbar
darüber zu informieren. Sowas nennet man dann Architektur.
Wer seine Architektur dermaßen versemmelt, dass er keinen Überblick mehr
hat, wer wann wo welche Ressourcen wie nutzt, der hat dann genau obiges
(nämlich das andere) Problem: ein selbstverschuldetes Chaos. Da hilft
dann natürlich(!) kein Prüfen mehr, da kann man nur noch beten. Aber das
ist ein anderes Problem :-)
Dennis S. schrieb:> was ist die beste Praxis wenn ich prüfen möchte, ob ein Speicherbereich> noch mit gültigen Werten initialisiert oder bereits wieder freigegeben> ist?
Das freigegebener Speicher anderweitig verwendet wird (und eventuell für
etwas exakt gleiches), kann man das nicht zuverlässig prüfen.
Es gibt spezielle Speicherverwaltungen zur Fehlersuche, die mit
zusätzlichen Bytes und Speicher nie freigeben und Markierungen (meist
vor dem eigentlichen Spaeicherblock) überprüfen, wie gross er ist und ob
er freigegeben ist.
Da aber in C Pointer auch mitten in Speicher zeigen können, und das
völlig legal ist, kann so eine Überprüfung bei jedem Zugriff nur mit
immensem Aufwand erfolgen, und damit langsamem Programmlauf.
Daher ist man am Besten bedient wenn man sich selbst einschränkt in der
Verwendung. Denn selbst NULL-setzen von freigegebenem Speicher ist ja
nicht zuverlässig
Was ist denn das für ein blödsinn?
MaWin schrieb:> nur mit> immensem Aufwand erfolgen, und damit langsamem Programmlauf.
Was ist an einem
1
if(pointer==NULL)
2
{
3
returnpointer_not_valid_error_code_or_what_ever;
4
}
immenser aufwand? Wo verlangsamt das das programm bitte so erheblich?
MaWin schrieb:> int *ptr,*ptr2;> ptr=malloc(10);> ptr2=ptr;> free(ptr);> ptr=NULL;> // und nun> *ptr2=3333; // da hilft auch kein if(ptr2!=NULL)
Wenn du so eine scheiße machst, bist du selbst schuld!
Du erschafst da ein Problem, das gar keins ist, sondern nur die
schludrigkeit des Programmieres zeigt! Es gibt halt regeln, in jeder
Sprache, und an die hat man sich zu halten.
Wenn man sich nicht dran hält, musst man sich nicht wundern, wenns in
die hose geht! Dein beispielcode ist von der Qualität ala: Ich will
zeigen wie schlecht und unsicher etwas ist, also mache ich absichtlich
fehler um das zu beweisen! Sorry, aber solch absichtlichen fehler sind
einfach nur dumm! Da ist so als wenn du den schnittschutz an einer
handkreissäge/winkelschleifer demontierst und dich dann beschwerst, das
du gerade deine hand verloren hast...
Und dein Code zeigt genau folgendes Problem:
g457 schrieb:> Wer seine Architektur dermaßen versemmelt, dass er keinen Überblick mehr> hat, wer wann wo welche Ressourcen wie nutzt,...MaWin schrieb:> Denn selbst NULL-setzen von freigegebenem Speicher ist ja> nicht zuverlässig
Natürlich kann man JEDEM System schwächen nachweisen, in dem man es
absichtlich falsch benutzt! auch Python oder Java oder Ada kranken an
sowas, wenn man es mit absicht falsch benutzt!
Sorry MaWin, aber dein Beispiel Code zeigt gar nichts, außer ein
hausgemachtes Problem, was aber kein Problem der Sprache ist!
g457 schrieb:> Wer seine Architektur dermaßen versemmelt, dass er keinen Überblick mehr> hat, wer wann wo welche Ressourcen wie nutzt, der hat dann genau obiges> (nämlich das andere) Problem: ein selbstverschuldetes Chaos. Da hilft> dann natürlich(!) kein Prüfen mehr, da kann man nur noch beten. Aber das> ist ein anderes Problem :-)
Dann sind wir ja einer Meinung.
Allerdings: In einem fehlerfreien Programm passiert sowas natürlich
nicht, dass man auf die Pointerkopie vergisst, mit der man die Knoten
einer Liste anhand eines weiteren Sortierkriteriums in einer anderen
Reihenfolge in eine Index-Datenstruktur per Pointer zum Original
einträgt. Aber wann hat man schon ein fehlerfreies Programm.
> Da hilft dann natürlich(!) kein Prüfen mehr
Doch das hilft.
Hast du eine Datenstruktur in der du 6 verschiedene Strukturen jeweils
in eigenen Listen halten musst UND es von den jeweiligen Strukturen
Querverdindungen in andere dieser Listen in Form von Verpointerungen
gibt (und das auch so sein muss), dann bist du heilfroh, wenn du bei der
Entwicklung der Bearbeitungsfunktionen eine Check-Funktion hast, die dir
alle Knoten und alle Pointer in allen Knoten abklappert und überprüft,
ob dieser Pointer noch gültig sind, oder ob du in den
Manipulationsfunktionen einen übersehen hast.
Die Manipulationsfunktionen einer Winged Edge Datastructure schüttelt
man nicht (und vor allen Dingen nicht fehlerfrei) aus dem Ärmel.
Kaj schrieb:> Was ist denn das für ein blödsinn?>> MaWin schrieb:>> nur mit>> immensem Aufwand erfolgen, und damit langsamem Programmlauf.> Was ist an einem>
1
>if(pointer==NULL)
2
>{
3
>returnpointer_not_valid_error_code_or_what_ever;
4
>}
5
>
> immenser aufwand? Wo verlangsamt das das programm bitte so erheblich?
Das ist nicht das, wovon MaWin gesprochen hat.
>> Da hilft dann natürlich(!) kein Prüfen mehr>> Doch das hilft.> Hast du eine Datenstruktur in der du 6 verschiedene Strukturen jeweils> in eigenen Listen halten musst UND [..]
Das ist nicht die Art prüfen, die ich hier meinte - denn die ist - und
da sind wir uns offenkundig einig - schlicht nicht möglich (man kann an
einem Zeiger sehen, dass er nicht gültig ist (er ist genullt), aber ohne
die Runtime zu manipulieren oder höheres Wissen zu haben (umgebende
Datenstrukturen) kann man nicht ∗prüfen∗, ob ein Zeiger, der nicht 0
ist, gültig ist (-> Archtiktur und Wissen um die Abhängigkeit der Daten
regelt).
Die von Dir angesprochene Art zu prüfen (Datenstrukturen auf Konsistenz
prüfen) hat damit erst mal nichts zu tun (die fällt unter Architektur
und höheres Wissen), ist aber selbstredend verpflichtend [0].
Also alles im grünen Bereich ;-)
Nix für ungut.
[0] wenn man wartbaren, fehlerarmen, stabilen und schnellen Code
schreiben will
Kaj schrieb:> Wenn du so eine scheiße machst, bist du selbst schuld!
Wenn du glaubst, so etwas käme in der Realität nicht vor, in einem 10
Jahre von 3 verschiedenen Teams gewarteten Programm, das Erweiterungen
erhielt die bei Grundsteinlegung noch nicht vorherzuahnen waren,
dann bist du noch ein kleines Kind daß dem Sandkasten nicht entwachsen
ist, aber schon genau weiss, wer schuld hat (du nicht).
Dein Problem lässt sich NUR durch konsequentes Programmieren lösen.
Im Nachhinein läuft da nichts.
Du kannst zwar jede Freigabe suchen und mit einem Zeiger=NULL ergänzen,
aber wenn Dein Programm, außer "if (Pointer)" noch was zustande bringen
soll, ist diese Abfrage, im Nachhinein ein echter Bremsklotz.
Da man Fehltritte dieser Art nicht ignorieren kann, kommst Du um einen
Leiharbeiter (Sisyphos) nicht herum.
Mit vielen Einschränkungen: lint oder seine Kumpels könnten helfen.
g457 schrieb:> kann man nicht ∗prüfen∗, ob ein Zeiger, der nicht 0> ist, gültig ist
Es nützt ja nicht mal was, wenn man sicher ist, dass man auf eigene
Daten zeigt - eher im Gegenteil. Wenn nicht gibt es eine Exception oder
das Programm wird von Windows beendet, soweit Ok. Ist das nicht der
Fall, so heisst das ja noch lange nicht, dass der Pointer auf das
"richtige" (bzw gewollte) zeigt. Als Nebeneffekt eine eigene Variable zu
ändern ist aber noch viiiel schlimmer als ein Absturz, da kommt man erst
nach längerer Forschung auf den Grund für seltsame Effekte.
Georg
Karl Heinz schrieb:> g457 schrieb:>>> Wer seine Architektur dermaßen versemmelt, dass er keinen Überblick mehr>> hat, wer wann wo welche Ressourcen wie nutzt, der hat dann genau obiges>> (nämlich das andere) Problem: ein selbstverschuldetes Chaos. Da hilft>> dann natürlich(!) kein Prüfen mehr, da kann man nur noch beten. Aber das>> ist ein anderes Problem :-)>> Dann sind wir ja einer Meinung.
Naja, es gibt aber auch Anwendungen, deren Datenfluß so komplex ist,
daß man es nicht mehr überblicken kann.
Ein Beispiel, das hier wohl jeder kennt, ist GCC.
kopfkratz
Also die Standardmethode ist ja schon genannt worden.
Wenn man nun wirklich effektiv testen will ob da was sinnvolles
drinsteht muß man halt schauen welchen OPCode man da hat und ob das 1.
mit der Architektur übereinstimmt und 2. keine NOP Schiene ist o.ä. :-P
Gut solange es kein void pointer ist könnte man mal einen Typecast
versuchen und abfangen, nur ganz ehrlich wozu das ganze ?
Wenn man Pointerfehler vermeiden will macht man entweder was bereits
gesagt wurde oder steigt auf eine Programmiersprache um wo ein
Nullpointer bzw. ungültiger Pointer eine Exception auslöst ohne das
gleich das ganze Programm einem um die Ohren fliegt.
Welche Sprachen können das ?
kopfkratzer schrieb:> Wenn man nun wirklich effektiv testen will ob da was sinnvolles> drinsteht muß man halt schauen welchen OPCode man da hat
Das bringt Dir nur bei Funktionspointern etwas; Pointer auf Daten
können auf irgendwas zeigen.
Wie willst Du bei einem Pointer auf uint8_t anhand des Wertes
herausfinden, ob der "gültig" ist? Jeder mit uint8_t darstellbare Wert
ist gültig.
kopfkratzer schrieb:> oder steigt auf eine Programmiersprache um wo ein> Nullpointer bzw. ungültiger Pointer eine Exception auslöst ohne das> gleich das ganze Programm einem um die Ohren fliegt.> Welche Sprachen können das ?
Java hat zwar keine Pointer, aber offenbar Nullpointer, denn
entsprechende Exceptions werden schon mal gerne geworfen. Ob mein
Programm nun aber bei einem Nullpointer-Zugriff mit einem Segmentation
Fault oder mit einer Nullpointer-Exception absemmelt, macht eigentlich
auch keinen Unterschied.
Rufus Τ. Firefly schrieb:> Wie willst Du bei einem Pointer auf uint8_t anhand des Wertes> herausfinden, ob der "gültig" ist? Jeder mit uint8_t darstellbare Wert> ist gültig.
Und wie ich weiter oben schon erwähnt habe, gültig reicht als Kriterium
ja auch noch nicht - es kann nur was Sinnvolles rauskommen, wenn der
Pointer auf das zeigt, was der Programmierer GEMEINT hat. Auf einen
Algorithmus der das berücksichtigt werden wir aber noch einige Zeit
warten müssen.
Georg
Rolf Magnus schrieb:> Java hat zwar keine Pointer,
Doch, natürlich. Eigentlich hat java nur Pointer auf Objekte am Heap,
und dafür keine Referenzen auf Objekte, oder z.B. Objekte am Stack / als
"automatic" Varablen oder globals...
Aber: Java hat auch einen Garbage Collector. d.H. das Problem, dass ein
Pointer auf einen bereits freigebenen Speicherbereich zeigt, existiert
nicht: Einfach weil der Speicherbereich nicht freigegeben wird, solange
ein Pointer darauf zeigt...
Wenn man C verlässt und C++ verwenden mag: Hier gibt es Tonnen von
passenden Smart-Pointern (ggfs. boost::), die das Problem lösen. Hilft
nur in C nix.
Das ist ganz trivial. Die beste Praxis dazu ist folgende:
Immer wenn man einen Speicher löscht setzt man auch den Zeiger auf Null.
Also:
free(new);
new=NULL;
Ganz einfach.
Frank schrieb:> Ganz einfach.
Einfach schon, aber völlig wirkungslos wenn man Pointer weitergegeben
hat, z.B. zillionenfach beim Aufruf von Windows-Funktionen. Besonders
hübsch: char array als lokale Variable in einer Prozedur verwenden und
dann per PostMessage weitergeben - wird die Message verarbeitet, ist
nicht nur der Speicherbreich nicht mehr alloziert, es gibt auch keinen
auf Null gesetzten Pointer mehr.
Georg
Frank schrieb:> Das ist ganz trivial. Die beste Praxis dazu ist folgende:>> Immer wenn man einen Speicher löscht setzt man auch den Zeiger auf Null.>> Also:> free(new);> new=NULL;>> Ganz einfach.
Meinen Code gelesen?