Moin,
Ich stehe grade etwas auf dem Schlauch.
Wenn ich in einer Funktion ein Ergebnis habe und das in einer globalen
Variable speichern möchte, ist dann
1
main
2
{
3
intWert;
4
Wert=funktion(2,5);
5
}
6
7
intfunktion(para11,para2)
8
{intresult;
9
result=para1+para2;
10
returnresult;
11
}
das selbe wie
1
main
2
{
3
intWert;
4
funktion(2,5);
5
}
6
7
voidfunktion(para1,para2)
8
{Wert=para1+para2;
9
}
?
Bei all den pointern und zuweisungen und all dem Lesen über Strings bin
ich da grade echt verwirrt...
Das zweite funktioniert nicht, weil Wert in funktion nicht bekannt ist.
Damit Wert eine globale Variable ist (und das Programm funktioniert),
muss sie außerhalb von main stehen.
Äh logisch, Tipfehler im Beispielcode, ich sag ja, ich bin verwirrt, ich
sollte Pause machen ;)
Wenn Wert ausserhalb steht dann geht es auf beide Arten oder?
Phillip,
das zweite Beispiel wird einen Fehler beim Übersetzen bringen, da "Wert"
in der funktion eine unbekannte Variable ist. Die Deklaration von "Wert"
innerhalb von main ist auch nur innerhalb von main sichtbar.
Das ist eine lokale Variable deren Inhalt beim verlassen von main
verloren geht.
Stefan
Zunächst hast du hier weder Pointer noch globale Variablen definiert.
Globale Variablen (global--> überall) werden vor der main fkt.
deklariert.
Diese Variablen kann man dann von überall ansprechen.
Eigendlich seltener genutzt, da man sie schnell bei größeren Programmen
versehendlich doppelt anspricht. (Irgendwie ist man bei der
Namensvergebung meist doch nicht so kreativ)
Funktionen kennen nur die Variablen die
1. ihnen übergeben werden
2. Global deklariert wurden
3. in der Funktion selbst deklariert wurden
Du musst beachten, jede Variable IN einer Funktion wird nach dessen
beenden "vergessen".
Ausnahme ist noch alles was static deklariert wird.(versuch das später)
Bei Pointern hingegen wird direkt auf die Adresse der Variablen
zugegriffen.
Dazu muss der Funktion aber die Adresse übergeben werden.
//Bsp.:
//in der main:
...
//aufruf der funktion
myfunction(&Variable);
...
//die Funktion
void myfunction( *Variable)
{
*Variable = 5;
}
Danke, langsam komme ich wieder rein.
Nochmal grad die nächste (saublöde?) Frage (kann das grade nicht testen
da ich nur mitm Notepad schreibe, Compiler etc hab ich hier nicht aufm
Schlepptop):
Wie übergebe ich einer Funktion ein Array? Pointer auf die erste Stelle
oder?
In der Funktion soll eine Schleife laufen die die einzelnen
Array-Elemente verarbeitet.
Das 2. Beispielt geht auch, unter gewissen bedingungen (Compiler)
Siehe: http://ridiculousfish.com/blog/page/3/
-> February 5th, 2006
Das ganze ist aber soweit ich weiss kein Standard mehr.
Ok, eins nach dem andern, vergesst mal den zweiten Code.
Mein Problem ist das: Ich habe seit über einem Jahr nicht mehr
Programmiert, Microcrontroller bedient etc. Jetzt muß ich am Projekt
weitermachen, hatte dazu ne glaube ich recht Brauchbare Idee und habe
jetzt das Gefühl daß das auf einmal unendlich kompliziert wird.
Ich hatte vor einem Jahr auch schon einige Programme geschrieben die auf
dem Atmel wunderbar liefen, ohne ein einziges mal irgendwelche Pointer
benutzen zu müssen (auch wenn ich inzwischen so halbwegs verstehe was es
damit auf sich hat und mir ist auch klar daß ohne Pointer beim
Programmaufruf immer nur der WERT der Parametzer übergeben wird).
Ich versuche gerade ein bisschen Struktur in die ganze Sache zu
bringen...
Wie wäre es, wenn du dir schnell mal nen C-Compiler auf deinem
Schlepptop installierst? Dann könntest du schon einmal Syntaxfehler
selber suchen.
Bitte höre auch auf, Variablen am Wortanfang groß zu schreiben oder
Umlaute zu nutzen.
while(i=0;i++;durchläufe)
das sollte wohl eine for-schleife werden.
Phillip Hommel schrieb:
> Jetzt muß ich am Projekt> weitermachen, hatte dazu ne glaube ich recht Brauchbare Idee und habe> jetzt das Gefühl daß das auf einmal unendlich kompliziert wird.
Dann vergiß erstmal, Programme aus dem Ärmel zu schütteln.
Schalte den PC ab, nimm Papier und Bleistift und mache erstmal nen
Programmablaufplan.
Schon Egon Olsen hat nichts ohne Plan gemacht.
Und wenn Du grundsätzliche Fragen zu C hast, installiere AVRStudio +
WINAVR und simuliere einfach mal einige Sachen.
Peter
Sorry, der Umlaut ist natürlich nur im Beispieltext vorhanden, ging mir
da ja um die Funktion. Was ist das Problem an großgeschriebenen
Variablen? (ernsthafte Frage).
Ich werd das für heute mal beiseite legen, mir raucht grad so die
Birne...
Uiuiui ...
- Nutzung einer globalen Variable anstatt die Return-Funktionalität zu
verwenden
- die "for"-Schleife wird dir nicht ganz tun was sie soll, Tipp:
durchlaeufe=sizeof(array)/sizeof(int)
Fazit: Hier sollte jemand einen C-Grundkurs belegen.
> Was ist das Problem an großgeschriebenen Variablen? (ernsthafte Frage).
Es ist kein Problem. Es erhöht einfach die Lesbarkeit des Quelltextes,
da sich viele Leute an diese Vereinbarung halten.
Stichworte: Coding Style, Coding Rule, ...
Ansonsten siehe Peter und bartsch.
> > Was ist das Problem an großgeschriebenen Variablen? (ernsthafte Frage).>> Es ist kein Problem. Es erhöht einfach die Lesbarkeit des Quelltextes,> da sich viele Leute an diese Vereinbarung halten.
Das ist eine reine Konvention.
Es gibt zwei unterschiedliche Varianten, aus mehreren Wörtern bestehende
Symbolnamen zusammenzusetzen:
mach_was_sinnvolles
MachWasSinnvolles
Beide Varianten sind weit verbreitet, und beide Varianten werden von den
Anhängern der jeweils anderen Variante aufs übelste verteufelt und für
völlig unlesbar gehalten.
Hier soll es jeder so machen, wie er es für sich richtig hält. Nur
konsistent muss das ganze dann sein, also auf gar keinen Fall ein
Mischbetrieb aus beiden Varianten.
Bei der zweiten Variante werden oft auch den Variablentyp bezeichnende
Präfixe verwendet, so daß man ohne die Deklaration zu sehen schon
erkennen kann, worum es sich handelt:
iCount
pBuffer
(das vorangestellte i steht für int, das vorangestellte p für Pointer).
Das ganze gipfelt in der sogenannten "ungarischen Notation", die von
Charles Simonyi in der Firma Microsoft entwickelt wurde.
Das ist natürlich auch äußerst umstritten, die pro- und
contra-Ungarisch-Diskussionen füllen locker eine kleine
Stadtteilbibliothek.
Auch hier bin ich der Ansicht, daß das jeder so einsetzen soll, wie er
es für richtig hält, solange er dabei auch langfristig konsistent
bleibt.
Es gibt aber auch Konventionen, an die man sich unbedingt halten
sollte:
Komplett in Großbuchstaben werden nur Macros geschrieben, also Dinge,
die per #define definiert werden.
Bezeichner fangen nie mit einem Unterstrich an, das ist für
Compilerdinge reserviert.
[quote]
- Nutzung einer globalen Variable anstatt die Return-Funktionalität zu
verwenden
[/quote]
Wenn ich genau einmal genau einen Wert zurückgeben möchte mache ich das
natürlich per return. Es geht ja darum daß die Funktion eine Schleife
beinhaltet die je durchlauf zb einen anderen Pin ausliesst und dessen
Zustand in eine andere Stelle eines arrays abspeichern soll.
Daß ich in C alles andere als fit bin bestreite ich absolut nicht und
ich danke auch allen für konstruktive Kritik und Hilfestellungen.
Vielleicht erkläre ich nochmal kurz was ich eigentlich machen möchte:
Der Atmel soll folgendes tun:
Es sollen nach einander PORTA, PORTB, PORTC, (D nicht, der wird anders
verwendet) sowie der ADC und pins, an denen Gray-Encoder hängen
ausgewertet werden. welche Pins wie belegt sind wir in einem setup-file
festgelegt.
Darin stehen dann arrays wie
1
intporta_input_pins[]{zb0,1,2,3,4}
2
intportb_input_pins[]{0,4,5,6}
3
intportc_input_pins[]{0,1}
4
intadc_input_pins[]{5,6,7}//es wird an anderer stelle
5
//dafür gesorgt daß sich das mit den pins von porta nicht überschneidet.
6
dannnoch
7
intporta_input_values[sizeof(porta_input_pins)]
8
intportb_input_values[sizeof(portb_input_pins)]
jetzt ist die Idee, in main eben die zugewiesenen pins von port a
auszulesen, und ihren Zustand in die entsrechenden Stellen von
porta_input_values zu speichern. Dazu kommen noch ein paar
Vergleichs-Funktionen, die bei Ungleichheit gegenüber dem letzten
Durchgang die Pin Nummer(bzw am Ende einen dem Pin zugewiesenen Namen
als String) und den Zustand per UART an den PC schickt.
Das selbe dann eben auch noch mit dem ADC der die Position angehängter
Potis ausliest und Zahlenwerte die per Drehencoder hoch oder
runtergezählt werden.
Dazu sollen dann in der Haputschleife die einzelnen Funktionen in etwa
so aufgerufen werden (nur schematisch, evtl Synthaxfehler ignorieren!)
//also soviele Durchläufe wie das Array elemente hat
14
//bei 0 wird direkt abgebrochen
Der Code soll dabei fertig stehen, sodaß man nur (später per GUI) im
Setup-File eine "Pinbelegung" erstellen braucht und im Code selbst
nichts rumschreiben muss(soll).
So, habe mal über alles geschlafen und mir heute wieder AVR-Studio
installiert.
Soweit lief das auch alles in der Simulation so wie ich mir das
vorgestellt hatte.
Was mich jetzt aber stutzig macht, ist ich einige der vorigen Posts so
verstanden habe, als wäre die Benutzung von globalen Variablen eines der
absoluten No-Gos in C, und im AVR-GCC Tutorial wird es auch so
beschrieben "manchmal geht es leider nicht anders sodaß man sich diesem
Teufelszeug doch bedienen muss".
Aus anderen Sprachen mit denen ich früher gearbeitet habe (VB, Delphi)
ist das dagegen ganz normale Anwendung. Daher würde mich mal
interessieren wo denn in C dabei das große Problem liegt.
Wenn es nämlich GEHT (und so sieht es in der simulation aus), dann
verstehe ich nicht warum ich mir aus meiner bescheidenen Einsicht das
Leben unendlich kompliziert gestalten soll und mit Pointern um mich
werfen soll wenn ich doch einfach einen zentralen Speicherbereich
festlegen kann in dem alles gespeichert wird was ich will und auf den
ich jederzeit zugreifen kann.
Vielleicht könnte mir das jemand erklären?
Phillip Hommel schrieb:
> Was mich jetzt aber stutzig macht, ist ich einige der vorigen Posts so> verstanden habe, als wäre die Benutzung von globalen Variablen eines der> absoluten No-Gos in C, und im AVR-GCC Tutorial wird es auch so> beschrieben "manchmal geht es leider nicht anders sodaß man sich diesem> Teufelszeug doch bedienen muss".
Alles was "global" ist kannst du bedenkenlos auch global machen (und bei
Interrupts z.B. hat man meist garkeine Andere möglichkeit als globale
Variablen zu nutzen), nur Funktionsrückgaben über globale Variablen
"macht man nicht" da das häufig zu Problemen führen kann (z.B. bei
Rekursionen) und die Wiederverwendbarkeit des Codes stark einschränkt.
Ich würde sagen, dass es eine Art Glaubensfrage ist. Es ist keineswegs
verboten, globale Variablen zu verwenden (spart auch Platz auf dem
Stack). Oft ist es aber sinnvoll lokale Variablen einzusetzen, da der
Compiler diese direkt auf die Register verteilen kann und keinen
RAM-Speicher benötigt.
Globale Variablen bergen bei größeren Projekten das Risiko, dass man sie
nicht immer lokal (im Quellcode) sehen kann. Oft passiert es dann, dass
man eine lokale Variable mit gleichem Namen verwendet.
Es ist halt nicht verboten, globale Variablen zu verwenden (sonst gäbe
es sie nicht). Man sollte aber überlegen, ob sie Sinn machen.
Ok dankeschön. Hab grade auch noch nen recht interessanten Artikel
entdeckt wo von Seiteneffekten gesprochen wird, klingt schon
eileuchtend, ast dann aber wieder eher eine Sorgfaltsfrage.
Ich denke wenn mal globale Variablen vernünftig kennzeichnet (zb mit
prefix "glob" oder wie auch immer) sollte das gehen.
Also kriege ich für mein o.g. Beispiel dann doch nicht von allen Seiten
auf den Deckel? Das hatte ich nämlich vorher so verstanden.
Daß man natürlich eher Parameter übergibt und einfache Funktionen per
return "antworten" lässt ist mir auch klar, ich sehe nur ehrlich gesagt
auch echt keine andere Möglichkeit, meine Loop-Pinabfrage anders
abzuspeichern oder sehe ich das jetzt wieder falsch?
Hallo Phillip, komm mit erhobenen Händen raus!
Stelle dich, hier spricht die C-Polizei!
Naja, soweit wird es nicht kommen. Versuch dein Problem zu lößen, wenn
es geht, ist gut.
Mach deine Erfahrungen, ist bisher der beste Lehrmeister.
Irgendwann wirst du konsistenten Programmierstil zu schätzen wissen.
Genauso wie du lernst, abzuschätzen was eine globale Variable (vll. auch
static) Wert oder nicht Wert ist.
Läubi .. schrieb:
> da das häufig zu Problemen führen kann (z.B. bei> Rekursionen)
Rekursive Funktionen setzt man auf kleinen Mikrocontrollern sowieso
nicht ein (in MISRA-C z.B. sind sie verboten).
Was ich als pauschale Aussage auch nicht unterschreiben würde...
MISRA ist nicht die Bibel, und Rekursion kann sinnvoll sein,
wenn man weiß was man tut.
MISRA verbietet ja alles, was in C Spaß macht (fast alles,
Zeiger glaube ich sind erlaubt, oder?).
Wenn ich mit bsearch in einem sortierten Feld suche, nehme ich
Rekursion. Da kann MISRA sauer werden oder nicht.
Klaus Wachtler schrieb:
> Was ich als pauschale Aussage auch nicht unterschreiben würde...> MISRA ist nicht die Bibel, und Rekursion kann sinnvoll sein,> wenn man weiß was man tut.> MISRA verbietet ja alles, was in C Spaß macht (fast alles,> Zeiger glaube ich sind erlaubt, oder?).
Ja - mit Einschränkungen ;-)
> Wenn ich mit bsearch in einem sortierten Feld suche, nehme ich> Rekursion.
Auf einem Mikrocontroller? Was sortiersuchst Du denn so? :-)
Bisher nichts, aber wenn ich z.B. eine Art Kommandointerpreter
bräuchte mit einer Kiste voll Schlüsselwörtern, würde ich
dafür lieber binäre Suche nehmen als linear zu suchen.
Das generelle Problem mit Rekursion, worauf du vielleicht
hinauswillst, ist daß man ggf. nicht sicher vorhersagen kann, wie
tief die Verzweigung ist und dementsprechend man nicht sicher
sagen kann, ob der Stack ausreicht - und folglich sicher
irgendwann nicht reichen wird.
Bei einem Fall wie binärer Suche gibt es aber eine sichere
Grenze (log2(Feldlänge)); deshalb habe ich es als Beispiel
dafür angeführt, weswegen ich ein generelles Verbot von
Rekursion nicht als zielführend erachte.
Wenn ich Rekursion generell verbiete ("kann ja schief gehen,
wenn man es falsch einsetzt"), muß ich mit der gleichen
Begründung die komplette Sprache C, Bier und vieles andere
verbieten.
Irgendwie ist mir das zu religiös. Zumindest brauche ich dann
kein C mehr.
Die Größenberechnung von porta_input_values ist noch flasch. sizeof
liefert nicht die Anzahl der Elemente, sondern die Größe in Bytes. (Im
vorliegenden Beispiel ist das kein Problem da das resultierende Array
größer wird als nötig.)
Korrrekt wäre:
> Die Größenberechnung von porta_input_values ist noch flasch. sizeof> liefert nicht die Anzahl der Elemente, sondern die Größe in Bytes. (Im> vorliegenden Beispiel ist das kein Problem da das resultierende Array> größer wird als nötig.)>> Korrrekt wäre:>
Abgesehen davon finde ich die ganze angedachte Systematik ziemlich
umständlich und eigentlich unnötig aufwändig.
Anstelle des Arrays porta_input_pins tut es auch ein einzelner unsigned
char. Angeforderte Port-Pins werden dort durch ein 1 Bit repräsentiert.
Sprich dieser uchar dient gleichzeitig als Mask, mit der das PIN
Register geundet wird um nur noch die ineterssierenden Bits übrig zu
lassen. Der Pin Inhalt wird auf einmal eingelesen, mit der Maske geundet
mit einer Previous Kopie des vorhergehenden Pin-Zustands ge-xodert und
es bleiben die Bits übrig, die sich geändert haben.
Das ganze braucht keine 20 Takte und ist linear runterprogrammiert. Im
Vergleich zu so einer Schleifenlösung x-fach schneller.
Lediglich das auseinanderpfriemeln einer Benutzereingabe bzw. die
Anzeige der überachten Pins wird (leicht) komplexer. Aber nicht wirklich
gravierend komplexer.
Mark Brandis schrieb:
> Läubi .. schrieb:>> da das häufig zu Problemen führen kann (z.B. bei>> Rekursionen)>> Rekursive Funktionen setzt man auf kleinen Mikrocontrollern sowieso> nicht ein (in MISRA-C z.B. sind sie verboten).
Ich wollte auch eher sagen warum man im Allgemeinen Rückgaben nicht
über globale Variablen machen sollte.
Ansonsten tritzt das Problem natürlich auch auf wenn an zwei Stellen die
gleiche Funktion aufgerufen wird.
Globale Variablen haben einfach den Nachteil, dass sie dir einen guten
Teil an Flexibilität aus einer Funktion herausnehmen.
Wartung und Weiterentwicklung, sowohl eines kompletten Programms als
auch einer spezifischen Funktion, werden mit globalen Variablen
erschwert. Je kleiner man den Sichtbarkeitsbereich von Variablen
gestalten kann, desto leichter ist zu überblicken, was mit diesen
Variablen wann, wo und warum passiert.
Im Hinblick auf Weiterverwendbarkeit von Funktion sind globale Variablen
eigentlich immer kontraproduktiv.
Klar ist allerdings auch, dass man dieses Dogma der Nichtbenutzung von
globalben Variablen auf einem µC wie einem (kleineren) AVR nicht
vernünftig durchziehen kann. IRQ-Kommunikation ist eine Sache, die
Überschaubarkeit des SRAM Verbrauchs ist eine andere Sache. Hier haben
globale Variablen den Vorteil, dass man schon zur Compilezeit schon
abschätzen kann, welchen Speicherverbrauch das Programm wirklich haben
wird.
Ich versuch die Sache so zu halten:
Schleifenvariablen bleiben funktionslokal. Benötigt eine Funktion nur
wenige lokale Variablen, dann mach ich das auch. Extensive
Datenstrukturen werden global angelegt.
Nochmal zwischendruch vielen vielen Dank für die zahlreichen Tips und
hilfen. Ich bin in meiner Denkweise glaube ich noch nicht so wirklich
auf C getrimmt, wenn ich auch dem PC programmiere sind mir ein paar
Bytes oder Taktzyklen total egal, auf nCs sieht das ganze scho necht
anders aus.
Den Vorschlag von Karl Heinz finde ich total super, hab da auch schon
ein bisschen rumprobiert.
Mein Problem liegt auch gar nicht in Code selbst, in dem habe ich ja
eigentlich auch nur total simple Funktionen.
Mein Problem hängt eher in dem Setup-File, daß ich einbinden möchte, in
dem die Pinvergabe gesetzt wird.
Die soll übersichtlich und auch für nicht-Programmierer einfach
bearbeitbar sein. Ich würde also eine Reihe #defines benutzen:
1
#define ENABLED = 1;
2
#define DISABLED = 0;
3
4
#define PA_IN_PIN_0 ENABLED;
5
#define PA_IN_PIN_1 DISABLED;
6
...
7
#define PA_OUT_PIN_0 DISABLED;
8
#define PA_OUT_PIN_1 ENABLED;
9
//Das sollte eigentlich jeder verstehen.
Mein Problem fängt jetzt an, wenn ich die je 8 "ENABLED" und "DISABLED"
Pins, zu der angesprochenen Maske zusammenfassen will.
Mit
Klappt das nicht, ich bekomme an der Stelle in der Init-Funktion (wo
auch der USART initialisiert wird etc), in der ich das DDRA dann mit der
Maske setzen will die Fehlermeldung "...expecting ')' before ';' token"
1
DDRA=PA_DDR_MASKE;
Wenn ich an der stelle DDRA = 0x01; oder sowas setze geht es, also nehme
ich an daß das Makro so nicht funktioniert. Kann ich das überhaupt vom
Präprozessor machen lassen und wie?
Na, ob das leichter zu verstehen ist?
Das Problem für den 'Programmier-Anwender' ist ja nicht welcher Pin
'enabled' ist oder 'nicht enabled' ist. Wenn du einen Code von jemand
anderem bekommst, lautet die Fragestellung praktisch immer:
"Meine Hardware ist an diesem Pin angeschlossen. Wie teile ich das dem
Programm mit."
OOOOOH man, klar, das Semikolon war das Problem.
Ja das sind keine Globalen Variablen mehr, das hatte ich ja inzwischen
verstanden und versuche es nun nach einem Alternativ-vorschlag zu
machen.
Der "Anwender" in meinem Fall bekommt eigentlich eine GUI, im Extremfall
soll das bestehende setup-file zwischen
"////changes only below this line///"
und
"////do not change anything below this line////"
von hand in sofern geändert werden, daß in diese Zeilen eingetragen
wird, an welchen Pins ein Schalter hängt, an welchen eine LED, an
welchen ein Poti, ein Drehencoder oder PWM und 7Segment displays.
ENABLED bedeutet dabei einfach, "dieser Pin ist für die IN-Funktion
(Schalter) aktiviert".
Das lange Marko braucht nicht vom Anwender verstanden werden, er ändert
einfach nur
PA_IN_PIN_x
...
PB_IN_PIN_x
...
PC_IN_PIN_x
...
ADC_IN_PIN_x (ich weiß, hier muß ich noch prüfen daß nicht der gleiche
pin für den IO und für den ADC Betrieb ENABLED wird, die GUI lässt sowas
gar nicht zu).
...
GRAY_IN_PIN (immer 2)
...
PA_OUT_PIN_x
...
usw
Der rest soll eben im Code varbaut werden, aber natürlich auch da so
übersichtlich und reproduzierbar/flexibel wie möglich.