Hallo zusammen,
ich arbeite mich gerade in die Programmierung von 8-Bit-µCs unter C ein.
Das Konzept mit den Pointern habe ich verstanden, nur eins nicht: Wozu
benötigte ich auf der Ebene der 8-Biter eigentlich Pointer ?
Man kann doch auch vieles mit globalen Variablen machen und so z.B.
ebenfalls Variablen-Werte in Funktionen verändern.
Ich sehe z.Zt. keine Vorteile durch den Einsatz von Pointern.
Kann mir da jemand weiter helfen ?
Vielen Dank,
Zeiger_1
Ein Argument für Pointer wäre, dass globale Variablen permanent
Speicherplatz einnehmen, Pointer nicht, da der Zugriff dann erfolgt,
wenn er benötigt wird. Der Speicherplatz der Variablen, auf die
zugegriffen wird, kann ja später durch sog.Overlay wieder für ne andere
Variable herhalten...
Ralf
Nicht? Dann hast du Zeiger ganz sicher nicht verstanden.
Paar ganz einfache Stichworte:
- 're-entrant' Funktionen*
- elegante und/oder effiziente Programmierung
- Vektoren und Arrays
- dynamische Speicherzuweisung*
- ...
* vielleicht auf kleinen Maschinen nicht ganz so relevant.
Vektor- und Array-Zugriffe zerfallen grundsätzlich in Zeiger. Zeiger als
Rückgabeparameter können jede Menge Herumkopiererei sparen.
Hallo Ralf,
wenn ich aber innerhalb der Funktion mit lokalen Variablen arbeite, wird
der Speicherplatz doch auch wieder freigegeben, wenn ich die Funktion
verlasse.
Ein Hauptargument bei den Pointern ist doch, daß man Werte innerhalb
einer Funktion ändern will, die in der Funktion selber gar nicht bekannt
sind.
Das sind nach meiner Vorstellung dann doch globale Variablen, auf die
ich auch direkt, über ihren Namen, zugreifen kann.
Zeiger_1
Klar kannst Du alles global machen, aber wenn Deine Projekte eine
gewisse Grösse erreichen, kann das schnell mal unübersichtlich werden
und verleitet zu doofen Fehlern. Daher macht man für Module / Funktionen
nur die Daten sichtbar, welche für diese relevant sind.
Globale Variablen sind für vieles nur bedingt geeignet und enden
sehr schnell in sehr schlechten Programmen.
Es gibt sinnvolle Anwendungen dafür, aber noch viel mehr
Fälle, wo man sie vermeiden sollte.
Daß man etwas theoretisch mit globalen Variablen machen kann,
heißt noch lange nicht, daß man es auch sollte.
Hallo Sven,
diese Stichworte kenne ich alle, aber mehr auch nicht, vor allen Dingen
keine konkreten Beispiele.
Wenn ich z.B. 100 Meßwerte in einem Array ablege und die von
verschiedenen Funktionen aus bearbeiten will, wo bringen Pointer mir
Vorteile gegenüber einer Anlage des Arrays als globale Variable ?
Den Speicherplatz für meine Meßwerte brauche ich auf jeden Fall.
Wo kann ich denn hierbei mit Pointern eleganter/effizienter arbeiten ?
Zeiger_1
Bei jedem Zugriff auf ein Feldelement verwendest du Pointer, ob du
willst oder nicht.
Egal, ob das Feld lokal oder global ist.
Sobald du nach der Deklaration/Definition des Feldes in deinem
Quelltext den Feldnamen erwähnst, ist das ein Zeiger auf das
erste Element.
z.B. sind Zeiger nützlich, wenn du eine Funktion programmieren willst,
um 2 Werte miteinander zu vertauschen. Diese musst du der Funktion
nämlich als Zeiger übegeben:
Beispiel:
void swap (int *c1, int *c2)
{
int tmp = *c1;
*c1 = *c2;
*c2 = tmp;
}
Dann der Aufruf:
int c1=10;
int c2=20;
swap(&c1,&c2);
Zeiger_1 schrieb:
> diese Stichworte kenne ich alle, aber mehr auch nicht, vor allen Dingen> keine konkreten Beispiele.
Mit ein wenig Erfahrung merkst du das schneller selbst, als es dir
jemand erklaeren kann.
> Wenn ich z.B. 100 Meßwerte in einem Array ablege und die von> verschiedenen Funktionen aus bearbeiten will, wo bringen Pointer mir> Vorteile gegenüber einer Anlage des Arrays als globale Variable ?
Nimm etwas ganz einfaches, z.B. atoi(). Zeige mir, wie du das ohne
Uebergabe eines Pointers implementierst.
Globale Variablen: Tatsächlich macht es im Embedded-Bereich wesentlich
öfter Sinn, sie zu verwenden, als bei Software die auf einem Standard-PC
läuft. Immer fröhlich lokale Variablen auf dem Stack auf- und abbauen
ist eben auch nicht gerade förderlich für die Performanz. Außerdem weiß
ich eben bei einer globalen Variablen oder einem globalen Array genau,
wieviel Speicher draufgeht. Bei einer Funktion mit lokalen Variablen,
von der ich unter Umständen nicht genau sagen kann wie oft sie
aufgerufen wird, kann ich den Speicherverbrauch über die Laufzeit nicht
so genau abschätzen.
Dann wäre da noch MISRA C. Nicht ohne Grund schränkt dieser Standard die
Verwendung von Zeigern weitgehend ein. Dynamisch Speicher anfordern mit
malloc() ist verboten. Variable Zeiger auf Funktionen sind verboten. Das
hat schon seinen Grund, man will hier schließlich Quellen für Fehler und
Instabilität vermeiden.
Mit Zeigern kann man viel machen, aber eben auch viel Mist. Ein Prof an
meiner ehemaligen Hochschule (zugegeben ein Vollblut-Informatiker und
kein Embedded Spezialist) sagte mal in der Vorlesung: "90 Prozent aller
Fehler in C-Programmen rühren von Zugriffen auf nicht oder falsch
initialiserte Zeiger her". Wird wohl was dran sein.
Klaus Wachtler schrieb:
> Globale Variablen sind für vieles nur bedingt geeignet und enden> sehr schnell in sehr schlechten Programmen.>> Es gibt sinnvolle Anwendungen dafür, aber noch viel mehr> Fälle, wo man sie vermeiden sollte.>> Daß man etwas theoretisch mit globalen Variablen machen kann,> heißt noch lange nicht, daß man es auch sollte.
Ersetze "globale Variable" durch "Zeiger", dann kannste das Ganze gleich
nochmal posten ;-)
Bestes Beispiel ist z.B. die Stringverarbeitung.
Du hast eine Funktion die 2 String vergleicht ( strcmp ) wenn die jetzt
mit festen globalen Variablen arbeiten muesste dann koennte sie nur 2
Strings vergleichen. Da die Funktion strcmp aber 2. Pointer auf String
mitbekommt kann sie so unendlich viele verschiedene Strings miteinander
vergleichen. Sie bekommt halt nur die beiden Pointer (Addressen) wo die
beiden Strings sich befinden mit geteilt.
Am besten du holst dir mal ein Buch ueber die Grundlagen von 'C'
Gruss Helmi
Stichwort: "Call by Reference"
Wenn Du in einer Funktion mehr als eine Variable der Aufrufenden
Funktion ändern willst, geht das nicht per return sondern (in C) nur,
indem Du die Adresse der zu ändernden Variablen übergibts (wie bei der
o.a. swap funktion)
@Klaus:
Sobald du nach der Deklaration/Definition des Feldes in deinem
Quelltext den Feldnamen erwähnst, ist das ein Zeiger auf das
erste Element.
Da ist sicherlich richtig, aber als Programmierer brauche ich in C beim
Zugriff auf dieses Array absolut keinerlei Zeiger zu definieren und muß
auch nicht damit arbeiten.
Ich greife einfach auf das Array-Element zu und fertig ists. Was der
Compiler im Hintergrund daraus macht, interessiert eigentlich nicht,
hauptsache es funktioniert (ohne Pointer)
@Rogie
Das Beispiel leuchtet ein
@Peter
atoi ist in meiner stdio-lib enhalten und ich programmiere einfach:
z=atoi(s1); // wobei s1 ein String ist.
Hier benötige ich keinen Zeiger.
Und dann noch: wozu Pointer auf Funktionen ?
Zeiger_1
Aber er hat doch recht:
Man braucht keine Zeiger. Globale Variablen gehen auch, sind viel
einfacher und übersichtlicher als lokale.
Sortieren und Suchen braucht man nicht, wenn man in seinem Programm
von Anfang Ordnung hält.
Felder gehen auch ohne Zeiger, statt Strings nimmt man ein paar
char nacheinander.
Und das beste: wir müssen ihm die Zeiger gar nicht erst erklären.
Bei manchen Leuten koennte man meinen, sie fragen um Recht zu bekommen,
nicht um etwas zu lernen...
Hallo Peter,
na klar wollte ich was lernen, aber bitte mit einigen einleuchtenden
Beispielen und nicht mit Statements wie:
Zeiger sind toll, muß man einfach können, Zeiger stehen in jedem
C-Lehrbúch (na klar, wo denn auch sonst).
Solche allegemeinen Aussagen finde ich überall, aber bei der Angabe von
Beispielen, die NICHT auf PCs laufen, hapert es dann doch.
Einige ganz konkrete und gute Beispiele habe ich bekommen von Random,
Helmut und Regie (vielen Dank dafür), aber leider nicht von Dir, Peter.
Zeiger_1
achso, und ein relativ einfaches und sehr praktisches Beispiel
für Funktionszeiger:
Die Übergabe einer Vergleichsfunktion an die Funktionen
bsearch() und qsort() aus der Standardbibliothek.
Naja wenn alle hier nur ein Loblied auf Zeiger singen, muss ich wohl
daraus schließen, dass sich hier noch keiner mit Standards wie eben
MISRA beschäftigt hat.
Zeiger haben nicht nur Vorteile. Wer das nicht sehen will, ist im
Endeffekt ein genauso schlechter Programmierer wie jemand, der sie von
vornherein für Teufelswerk ;-) hält.
Klaus Wachtler schrieb:
> achso, und ein relativ einfaches und sehr praktisches Beispiel> für Funktionszeiger:> Die Übergabe einer Vergleichsfunktion an die Funktionen> bsearch() und qsort() aus der Standardbibliothek.
Es soll tatsächlich Hardware geben, für die nicht die gesamte C Standard
Library zur Verfügung steht. Kaum zu glauben, aber wahr.
Es ging um ein Beispiel, wozu Zeiger gut sind.
Wenn es auf einem konkreten System kein bsearch gibt und
ich etwas in einem Feld suchen will, schreibe ich bsearch() in
10 oder 20 Zeilen selbst.
Dann habe ich wieder ein Beispiel für Zeiger auf Funktionen.
@Klaus:
"Von ihm gibt es auch vernünftige und hilfreiche Beiträge."
Genau, und über so einen hätte ich mich gefreut, da hätte ich etwas
lernen können.
@Klaus:
Die Übergabe einer Vergleichsfunktion an die Funktionen
bsearch() und qsort() aus der Standardbibliothek
Da ist wieder das Anfangsproblem: ich habe doch geschrieben, das ich
mich mit 8-Bit-Controllern befasse und dafür einige gute Beispiele für
Zeiger-Anwendungen suche.
Wie Mark Brandis schon geschrieben hat, gibt es Compiler, die nicht
alles unterstützen, so kennt mein 8-Bit-Compiler bsearch und qsort
nicht, das ist dann doch wieder etwas für die PC-Ebene und darum ging es
mir leider nicht.
Zeiger_1
Klaus Wachtler schrieb:
> Es ging um ein Beispiel, wozu Zeiger gut sind.>> Wenn es auf einem konkreten System kein bsearch gibt und> ich etwas in einem Feld suchen will, schreibe ich bsearch() in> 10 oder 20 Zeilen selbst.>> Dann habe ich wieder ein Beispiel für Zeiger auf Funktionen.
Gut, es kommt halt auf den Einsatzzweck an. In der Motorsteuerung zum
Beispiel habe ich bis jetzt noch nicht so viele Such- und
Sortieralgorithmen gesehen. Woanders wiederum braucht man sie, klar.
>so kennt mein 8-Bit-Compiler bsearch und qsort>nicht, das ist dann doch wieder etwas für die PC-Ebene und darum ging es>mir leider nicht.
Der Compiler selber kennt diese Funktionen auch nicht. Die sind Teil der
mit dem Compiler gelieferten Bibliotheken. Aber man kann sich ja auch
selber diese Funktionen schreiben und in eine eigene Bibliothek tun. Und
schon kennt dein Compiler die auch.
Zeiger_1 schrieb:
> @Klaus:> "Von ihm gibt es auch vernünftige und hilfreiche Beiträge."> Genau, und über so einen hätte ich mich gefreut, da hätte ich etwas> lernen können.
ich fand das, was er hier geschrieben hat, nicht so unvernünftig.
>>> @Klaus:> Die Übergabe einer Vergleichsfunktion an die Funktionen> bsearch() und qsort() aus der Standardbibliothek>> Da ist wieder das Anfangsproblem: ich habe doch geschrieben, das ich> mich mit 8-Bit-Controllern befasse und dafür einige gute Beispiele für> Zeiger-Anwendungen suche.
Am Anfang hattest du geschrieben, daß du generell etwas zu Zeigern
willst.
Erst später hast du konkret nach Zeigern auf Funktionen gefragt,
wofür du hiermit ein Beispiel hast.
Es kann gut sein, daß du keinen Sinn darin siehst, oder erst später
einen Sinn darin siehst, oder so etwas wirklich nie brauchst.
Da kann aber hier niemand etwas dafür.
Ich brauche Zeiger häufig, Zeiger auf Funktionen gelegentlich,
du musst sie doch nicht nehmen.
>> Wie Mark Brandis schon geschrieben hat, gibt es Compiler, die nicht> alles unterstützen, so kennt mein 8-Bit-Compiler bsearch und qsort> nicht, das ist dann doch wieder etwas für die PC-Ebene und darum ging es> mir leider nicht.>> Zeiger_1
Vom printf im Testprogramm abgesehen auch auf AVR machbar:
1
// sucht in einem sortierten Feld (*base)[] nach einem
2
// Element, für das die Funktion (*compar)() Gleichheit mit dem
3
// Schlüssel (*key) verspricht, und liefert einen Zeiger auf
4
// das gefundene Element, oder NULL
5
// (entspricht dem Original-bsearch() nach ANSI-C)
6
void*bsearch_exakt(constvoid*key,
7
constvoid*base,
8
size_tnmemb,
9
size_tsize,
10
int(*compar)(constvoid*,constvoid*)
11
)
12
{
13
switch(nmemb)
14
{
15
case0:
16
returnNULL;
17
18
case1:
19
return((*compar)(base,key)==0?(void*)base:NULL);
20
21
default:
22
// Teilen, und suchen lassen:
23
{
24
size_tn1=nmemb/2;
25
constvoid*p1=(constchar*)base+(n1*size);
26
intv1=(*compar)(p1,key);
27
if(v1==0)
28
{
29
// Schlüssel genau getroffen
30
return(void*)p1;
31
}
32
elseif(v1<0)
33
{
34
// base[n1] kleiner als Schlüssel;
35
// also in der oberen Hälfte suchen.
36
// base[n1] braucht schon nicht mehr betrachtet
37
// zu werden.
38
returnbsearch_exakt(key,
39
(constchar*)p1+size,
40
nmemb-n1-1,
41
size,
42
compar
43
);
44
}
45
else
46
{
47
// base[n1] größer als Schlüssel;
48
// also in der unteren Hälfte suchen.
49
// base[n1] braucht schon nicht mehr betrachtet
50
// zu werden.
51
returnbsearch_exakt(key,
52
base,
53
n1,
54
size,
55
compar
56
);
57
}
58
}
59
60
}// switch
61
62
}// bsearch_exakt()
63
64
65
typedefstruct
66
{
67
intschluessel;
68
intwert;
69
}wertepaar_t;
70
71
72
// Vergleichsfunktion passend zur Sortierreihenfolge des Feldes:
+ Pointer sind prima um Strukturen an Funktionen zu übergeben
+ Für Scheduler, State-Machines oder Event-Handler sind Pointer auf
Funktionen sehr praktisch
Mark Brandis schrieb:
> Naja wenn alle hier nur ein Loblied auf Zeiger singen, muss ich wohl
Ich singe nicht ein Loblied auf die Zeiger, weil ich sie so toll fände.
Aber in C kommt man halt nicht dran vorbei, sie zu benutzen.
> daraus schließen, dass sich hier noch keiner mit Standards wie eben> MISRA beschäftigt hat.> ...
doch, aber teilweise sind die Empfehlungen (als mehr verstehe ich
es nicht) dort auch nicht so ganz praxisnah.
Wer nicht mit Strings und Zeigern umgehen kann, muss es halt
bleiben lassen. Weil die einen nicht damit klar kommen und damit
laufend Fehler bauen, muß man es den anderen ja nicht verbieten.
Die MISRA-Rezepte sind weder öffentlich (soweit ich weiß) noch
allgemein als richtig oder gültig akzeptiert.
Sicher sind sinnvolle Punkte dabei, aber vieles auch gleich
wieder ziemlich dogmatisch oder zumindest zu pauschal.
Wer die Problematik hinter MISRA ernst nimmt und die Fehler,
die man in C bauen kann, vermeiden will, muß auf etwas anderes
umsteigen (Java o.s.ä.); eine KAstration von C hilft nicht viel.
Daß man mit Zeigern viel Blödsinn machen kann, ist unbestritten.
Aber bei Fragen nach Anwendungen von Zeigern gleich damit zu
kommen, sie zu vermeiden, weil man lt. MISRA damit Fehler
machen kann, ist vielleicht doch etwas übertrieben m.E..
Zeiger_1 schrieb:
> Einige ganz konkrete und gute Beispiele habe ich bekommen von Random,> Helmut und Regie (vielen Dank dafür), aber leider nicht von Dir, Peter.
Falsch, das Beispiel mit atoi war sehr konkret, du hast es nur nicht
verstanden. Nach wie vor steht die Aufgabe fuer dich im Raum, atoi()
ohne Zeiger zu implementieren.
Ein Beispiel für die Verwendung von Zeigern auch für den
Embedded-Bereich wäre die Berechnung einer DFT/FFT, wo man, je nach
Programmierung, ein Array übergeben muss mit den Messwerten und ein
zweites für das Ergebnis.
Mit Zeigern programmiert wunderbar portierbar.
Mit einer globalen Array würd man den Code nicht nochmal benutzen können
bzw. eingeschränkt.
Ein anderer Grund warum man die Verwendung von globalen Variablen
einschränken sollte, ist der einfache, dass diese Variablen in den
fertigen Bibliotheken "überschreiben" könnten.
MfG
Stefan
Zeiger sparen jede Menge Datenschaufelei bei Strukturen, das habe ich
dir aber ganz zu Beginn bereits gesagt. Und genau das ist auf 8bittern
doch sinnvoll, oder nicht?
Sonst benutzt du für jeden Port-Zugriff übrigens auch Zeiger, auch wenn
du das nicht merkst. Dann braucht man die, um mit dem EEProm und dem
Programmspeicher zu reden (EEPROM, PROGMEM)...
Angenommen du hast eine Reihe von String (char-arrays).
Mit diesen möchtest du im Laufe des Programms etwas tun, d.h. mit einer
Funktion manipulieren oder ähnliches.
Willst du nun für jedes Array eine extra Funktion schreiben?
Falls das Array was du bearbeiten willst lokal ist, willst du dann
erstmal das ganze in einen globalen Buffer schreiben? Auch wenn der
Buffer sehr lang aber das zu bearbeitende Array sehr kurz ist?
Zeiger sind unter Anderem immer dann sinnvoll wenn du Felder/Strukturen
usw. bearbeiten/lesen willst die du aber noch nicht konkret kennst. Da
ist es imho einfacher und übersichtlicher sich einfach einen Zeiger und
die Länge übergeben zu lassen anstatt immer auf etwas globales
zuzugreifen, welches dann vorher immer entsprechend zu beschreiben bzw.
dies nachher erst auszulesen.
Wenn du deinen Code so strukturierst das du zusammengehörige
Funktionalität jeweils in einer Datei steht dann wird es schnell
verwirrend wenn du dann immer auf globale Elemente aus anderen Dateien
zugreifst. Irgendwann verlierst du den Überblick wann und wo welches
deiner globalen Elemente beschrieben wird. Da ist es einfacher und
vorallem übersichtlicher wenn du dir einfach einen Zeiger übergeben
lässt und dann mit diesem arbeitest.
Wenn man mit Pointern unvorsichtig umgeht können diese einem das Leben
schwer machen. Andersrum wird einiges auch durch bewusste nutzung von
Pointern vereinfacht.
Alles was mehr als 8 Byte zum übergeben ist musst Du mit Pointern
machen. Andere Gründe dafür:
- linked list
- rekursion
Es gibt viele Gründe wo Pointer nicht nur Sinnvoll sind, sondern auch
nötig.
>Zeiger haben nicht nur Vorteile. Wer das nicht sehen will, ist im>Endeffekt ein genauso schlechter Programmierer wie jemand, der sie von>vornherein für Teufelswerk ;-) hält.
Ich würde sagen Zeiger haben nur dann nicht nur Vorteile, wenn man nicht
richtig mit ihnen umgeht. Wenn man fit in der C-Programmierung und mit
Zeigern ist sehe ich keine Nachteile darin.
Zeiger sind einfach ein schönes Werkzeug der Sprache C. Natürlich lassen
sich auch alle Probleme (theoretisch) ohne Zeiger lösen. Allerdings
kommt ja auch niemand auf die Idee alle Programme in asm zu schreiben,
nur weil es eben gehen würde.
Genauso könnte der Threadersteller auch fragen warum es eine
for-Schleife in C gibt. Mit einer while-Schleife lässt sich ja auch jede
for-Schleife ersetzen. Das selbe mit switch case -> if usw.
Übrigens spätestens wenn du einigermaßen modulare Funktionen schreiben
möchtest, die vielleicht auch noch in einem anderen Projekt verwendet
werden sollen wirst du meistens Zeiger brauchen.
>Mark Brandis schrieb:>>Klaus Wachtler schrieb:>> achso, und ein relativ einfaches und sehr praktisches Beispiel>> für Funktionszeiger:>> Die Übergabe einer Vergleichsfunktion an die Funktionen>> bsearch() und qsort() aus der Standardbibliothek.>Es soll tatsächlich Hardware geben, für die nicht die gesamte C Standard>Library zur Verfügung steht. Kaum zu glauben, aber wahr.
Es soll auch tatsächlich Hardware geben, für die keine Integer-Typen,
while-Schleifen und/oder Strukturen gebraucht werden, also wofür gibts
die ganzen Dinge in C?
Sorry, aber die Begründung ist doch total am Ziel vorbeigeschossen.
Globale Variablen um Daten in Funktionen zu schieben und wieder
rauszuholen?!
Das ist 1. total unübersichtlich,
2. ziemlich ineffizient (ich muss ja immer erst die Daten in diese eine
globale Variable kopieren)
3. extrem schlechter Stil
und im großen und ganzen einfach Quatsch!
Funktionen sind ja extra dafür da, dass man sie mehrfach verwenden kann.
Wenn ich nun eine Funktion habe, die mir einen String auf der seriellen
ausgibt, will ich doch meinen String nicht immer erst an eine bestimmte
Speicherstelle kopieren. Und erst recht nicht ohne Zeiger...
ich darf ja dann nichtmal
while(*src)
*(dst++) = *(src++);
machen, sondern muss
buchstabe1_ziel = buchstabe1_quelle;
buchstabe2_ziel = buchstabe2_quelle;
buchstabe3_ziel = buchstabe3_quelle;
machen.
Also alles in allem ist C ohne Zeiger so wie assembler wenn man nur
"and", "not und "jz" hat... Es geht, aber schön ist das nicht.
So, Senf dazugegeben, glücklich, tschüß
Sven
joep schrieb:
> Zeiger sind einfach ein schönes Werkzeug der Sprache C.
Eigentlich sind Zeiger sogar grundlegender Bestandteil unserer
Rechnerarchitekturen (ja, es gibt Ausnahmen). C stellt sie nur zur
Verfuegung. In den "guten alten Zeiten (TM)" hat das auch Basic ganz
selbstverstaendlich getan. Sprachen, die keine Zeiger kennen, sind also
eher der Spezialfall, weil sie einen wesentlichen Bestandteil der
zugrundeliegenden Architektur verstecken. Hat man verstanden, wie ein
Rechner funktioniert, sind Zeiger auch gar nichts so besonderes. Muehe
haben damit eher die Leute, die erst lernen wie man programmiert und
dann, wie ein Rechner funktioniert.
Nur mal so, weil es manchmal so klingt, als seien Zeiger irgendwie eine
schlecht ueberlegte Idee der C-Erfinder gewesen...
Arrays sind auch böse weil nur eine andere Schreibweise für Zeiger, ein
falsch berechneter Index führt zu gleichen Fehlern.
MISRA oder ähnliches ist ja nicht extra für 8-Bitter, im Gegenteil ich
denke das wird eher auf grösseren µCs angewendet die z.B. auch eine MMU
haben (wobei man die Regeln für defensive Programmierung sicher sinnvoll
sind). Ich habe selber lange Zeit mit einem spez. protected Mode OS
gearbeitet und alle Module liefen in eigenen Code- und Memorysegmenten.
Wenn man da irgendwo ein Byte zuviel gelesen oder geschrieben hat stand
alles (dafür exakt an dem fehlerhaften Code und wartete auf den
Debugger). Da lernt man den Unterschied zwischen Programmen die
funktionieren und solchen die robust sind.
Aber für 8-Bitter nimmt man ja gerade C (wenn nicht Assembler) weil die
Resourcen limitiert sind. Exzessive Zeigerverschachtelungen und dyn.
Speicherorgien machen deshalb sowieso keine Freude, dafür sind z.B. die
AVR RISC Befehlssätze garnicht ausgelegt.
Tja, dann muss es wohl sehr viele Leute geben, die nicht richtig
programmieren können und es trotzdem tun - bei den vielen kritischen
Sicherheitslücken, die durch Buffer Overflows zustande kommen... :-)
Schön wäre es, wenn man in C Exceptions werfen könnte, anstatt dass sich
bei einem fehlerhaften Zeiger- oder Array-Zugriff das Programm gleich
komplett verabschiedet oder durch undefiniertes Verhalten glänzt.
Ja, nur ist halt C nunmal C.
Ich nehme ja auch lieber C++, wo nur irgend möglich - leider nicht
auf den kleinen Mistdingern mit ein paar Byte RAM.
Aber das ist, was ich oben meinte: wenn man C für zu gefährlich hält,
muß man in den sauren Apfel beißen und mehr Geld für einen "echten"
Rechner ausgeben, der genug RAM und ROM hat für C++ oder Java oder
sonstwas. Darf ein MC nur 2 EUR kosten, muß man C oder Assembler
nehmen. Damit lebt man billiger, aber hat den Spagat zwischen
effizient und fehlerträchtig vor sich. Das wiederum geht nur gut,
wenn man in C (dto. ASM) weiß was man tut, d.h. die Leute sind
mindestens um das teurer, was man sich am Rechner spart.
Mark Brandis schrieb:
> Tja, dann muss es wohl sehr viele Leute geben, die nicht richtig> programmieren können und es trotzdem tun - bei den vielen kritischen> Sicherheitslücken, die durch Buffer Overflows zustande kommen... :-)>> Schön wäre es, wenn man in C Exceptions werfen könnte, anstatt dass sich> bei einem fehlerhaften Zeiger- oder Array-Zugriff das Programm gleich> komplett verabschiedet oder durch undefiniertes Verhalten glänzt.
Bringt dir nichts.
Exceptions sind kein Allheilmittel. Am Anfang steht immer das
Bewusstsein, dass an einer bestimmten Programmstelle etwas passieren
kann. Gleich danach kommt die Überlegung, wie ich diesen fehlerhaften
Zustand detektieren kann. Und dann die Entscheidung: werfe ich eine
Exception oder liefere ich einen Fehlercode zurück.
-> Wenn du den Fehler nicht frühzeitig erkennst, hilft dir auch eine
Exception nichts, weil keiner da ist, der sie wirft.
Klaus Wachtler schrieb:
> Aber das ist, was ich oben meinte: wenn man C für zu gefährlich hält,> muß man in den sauren Apfel beißen und mehr Geld für einen "echten"> Rechner ausgeben,
falsch.
Wenn man C für zu gefährlich hält, muss man lernen wie man die Gefahr
meistern kann und plötzlich verliert diese 'Gefahr' völlig ihren
Schrecken.
Klar, ein 'Pointer im Gehölz' kann einem den Tag versauen. Aber das kann
eine simple Addition auch, wenn sie überläuft. Wer schützt mich
eigentlich davor, in einer for-Schleife die Abbruchbedingung falsch zu
formulieren und eine Endlosschleife zu bauen?
Das Ganze ist ein bischen wie: Wir verbieten Hämmer und erlauben nur
noch Schraubenzieher. Dort ist die Gefahr geringer, dass man sich auf
den Daumen haut.
Zeiger_1 schrieb:
> alles unterstützen, so kennt mein 8-Bit-Compiler bsearch und qsort> nicht, das ist dann doch wieder etwas für die PC-Ebene und darum ging es> mir leider nicht.
Warum soll das was nur für die PC Ebene sein.
Versuch mal einen kleinen Zeit-Scheduler zu schreiben. Du wirst
draufkommen, dass eine vernünftiger Ansatz darin besteht, die
zukünftigen Events nach ihrer zeitlichen Reihenfolge zu ordnen. Und
schon musst du sortieren und hast einen vernünftigen Einsatz für qsort.
@ Karl heinz Buchegger:
Das sehe ich genauso, es setzt aber Programmierer voraus, mit denen
das klappt.
Bei der MISRA-Schiene (auf die mich mit meiner Litanei eigentlich
noch bezog) wird versucht, C so zu kastrieren, daß es hoffentlich
ungefährlich wird.
Das halte ich eben für frommes Wunschdenken und eine gefährliche
Illusion, auch wenn etliche der Empfehlungen natürlich Sinn machen.
Irgendwie erinnert mich das an die tollen "sicheren" Funktionen
in Visual C++ wie strcat_s() etc., die genauso gefährlich sind
wie ihre Originale, wenn man sie nicht verstanden hat.
Klaus Wachtler schrieb:
> Erst später hast du konkret nach Zeigern auf Funktionen gefragt,> wofür du hiermit ein Beispiel hast.> Es kann gut sein, daß du keinen Sinn darin siehst, oder erst später> einen Sinn darin siehst, oder so etwas wirklich nie brauchst.
Versuch mal ein halbwegs universell verwendbares Menüsystem zu
schreiben, welches auch noch gut konfigurierbar sein soll und nicht
allzuviel Platz wegnehmen darf.
Ohne Funktionszeiger bist du dort mehr oder weniger aufgeschmissen.
Nur mal so als Beispiel für den TO.
Aber zur Ehrenrettung des TO möchte ich noch sagen:
Ich kann ihn schon verstehen. Die üblichen Einführungsbeispiele zu
Zeigern
1
intmain()
2
{
3
inti=0;
4
int*pi=&i;
5
6
*pi=5;
7
printf("%d\n",i);
8
}
sind wirklich witzlos. Kein Mensch verwendet Zeiger in dieser Form. Das
Beispiel soll einen Aspekt demonstrieren, nämlich dass jede Variable
eine Adresse im Speicher hat und Zugriffe auch über diese Adresse
geführt werden können. Mehr nicht.
Das wahre Potential von Zeigern erschliesst sich erst, wenn sie im
Konzert der restlichen Sprachwerkzeuge mitspielen. In diesem Sinne sind
die meisten Dinge in einer Programmiersprache für sich alleine gesehen
trivial und eigentlich ziemlich uninteressant. Die wahre Mächtigkeit
kommt erst dadurch zustande, dass man diese Dinge auf vielfältige Weise
miteinander kombinieren kann.
So auch bei Zeigern. Auf den ersten Blick erscheinen sie harmlos und
simpel. Aber ihre Power merkt man erst, wenn man sie in einem Programm
zum Einsatz bringt.
Bsp:
Was ist der grundlegende Unterschied zwischen diesen beiden
Funktions-Variationen
1
voidlcd_string(char*pString)
2
{
3
while(*pString!='\0')
4
lcd_char(*pString++);
5
}
1
voidlcd_string(charString[])
2
{
3
uint8_ti;
4
5
for(i=0;i<strlen(String);++i)
6
lcd_char(String[i]);
7
}
(mal abgesehen davon, dass die Verwendung von strlen in der
Abbruchbedingung sagen wir mal ... suboptimal ... ist)
Richtig: Bei der ersten Version, die konsequent auf Zeigerbenutzung
ausgelegt ist, ist die Stringlänge nicht limitiert.
Bei der zweiten Version sollte man beten, dass der String besser nicht
länger als 255 Zeichen ist.
Sicherer, im Sinne von 'kommt auch mit ungültigen Strings zurecht', ist
keine der beiden Versionen. Ein klares Beispiel, in dem man sich durch
den Verzicht auf Zeiger keinen Vorteil sondern einen Nachteil
eingehandelt hat.