Hallo!
Die untersten 3 Zeilen meines c-Codes sollten den von *startPointer bis
*endPointer definierten Array in localArray kopieren. Ich habe mich
schon langsam daran gewöhnt, dass der Gelbe Pfeil im Simulator teilweise
recht herumhüpft und das macht er auch hier. Nach dem er definitiv nicht
mehr ins for zurückhüpft, ist der *number-Pointer aber nicht der gleiche
wie der des gleichen Eintrages in startPointer. Ich verstehe das nicht!
Ich arbeite mit AVR-Studio 5, einem AT-MEGA32 und optimierungsstufe -Os.
Ich habe noch zwei vorher- nachher-screenshots des Simulators gepostet.
Ich hoffe ihr könnt mir helfen!
Gruss
Erdbewohner
ich wurde Sagen da fehlt ein "volatile" bei der definition der Variablen
der Struktur
struct SignAndNumber
{
volatile enum MathSign sign;
volatile double *number;
};
Danke. Mit -O0 funktioniert es. Aber mit -O1 schon nicht mehr. Ich kann
aber nicht meinen ganzen Code mit O0 kompilieren, da wird er ja ewig
langsam und riesig.
Gibt es noch andere Ratschläge?
> uint8_t localLength = (startPointer-endPointer+3)/3;
Wieso +3) / 3
So wie du das aufrufst
solve(&problemArray[0], &problemArray[0]);
ist der Ausdruck
startPointer-endPointer
gleich 0. Noch 3 dazu, macht 3, durch 3 macht 1
Die Schleife wird also 1 mal durchlaufen.
das +3 weil die länge eines mehr als der Start-pointer minus der
endpointer ist. (siehe mein Test-beispiel mit einem 1-elemente-array:
sonst wäre die länge 0).
das /3, weil ein SignAndNumber-struct 3 Bytes gross ist und ich in
localLength die Anzahl Elemente in localArray haben möchte.
Raphael F. schrieb:> das +3 weil die länge eines mehr als der Start-pointer minus der> endpointer ist. (siehe mein Test-beispiel mit einem 1-elemente-array:> sonst wäre die länge 0).> das /3, weil ein SignAndNumber-struct 3 Bytes gross ist
No.
Das rechnet der COmpiler bei Pointerarithmetik von alleine ein.
Ausserdem ist die Anzahl immer noch Ende - Start.
Bei
*(startPointer+i)
hast du ja die Strukturgröße auch nicht berücksichtigen müssen.
> und ich in> localLength die Anzahl Elemente in localArray haben möchte.
Die vernünftigste Methode ist es nicht, da einen Endpointer mitzugeben.
Vernünftig wäre es, wenn der Aufrufer gleich mitteilt was Sache ist und
die Anzahl mit übergibt.
Also: Das, was kbuchegg vorgeschlagen hat habe ich geändert. Es ändert
aber nichts. Das mit der länge anstatt endPointer ist auch eine gute
Idee. Es sollte aber nichts mit unserem Problem zu tun haben. Ich werde
das später mal ändern.
der komplette Code (es soll mal ein "Taschenrechner" werden, bei dem man
die Rechnung so eingeben kann, wie sie auf dem Papier steht) ist
angehängt.
>Je nachdem wies weitergeht, könnte der Compiler das komplette Array>komplett wegoptimiert haben.
Ja, das war auch so, als ich den restlichen Code auskommentiert habe.
Ich habe aber noch einige Versuche gemacht, wo er den Inhalt von
localArray irgendwie verrechnet und dann an ein Port ausgibt. Dann kann
der Compiler ihn ja nicht mehr wegoptimieren. Es hat trotzdem nicht
funktioniert. :-(
UI. Das habt ihr aber kompliziert aufgebaut. (Und vor allem viel zu viel
Code geschrieben ohne zwischendruch wieder mal ausreichend zu testen)
WIrd ne weile dauern bis ich das aufgedröselt habe.
Auf jeden Fall habt ihr den 3-er Fehler noch ein paar mal gemacht.
Und eine Arraylänge errechnet sich mittels Ende - Anfang. Nicht anders
rum.
uint8_t localLength = ( endPointer - startPointer ) + 1;
Ich weiss, es gibt sicher noch viele Fehler zu beheben. Aber leider ist
alles das nicht die Lösung für das Problem.
Ein uneleganter, aber simpler Ausweg wäre, eine kleine
inline-Assembler-Routine zu schreiben. :-/
Ich gehe jetzt aber ins Bett.
Und danke für die Hilfe!
Ja da sind ein paar Fehler drinnen. Die Klammernauswertung wird zu
Fehlern führen und SetBack geht so gar nicht. Aber das findet ihr
selber.
Mit Pointers stehst du noch ein wenig auf Kriegsfuss. Solange du da
nicht sicherer bist, solltest eventuell die Arrayschreibweise benutzen.
1
for(uint8_ti=0;i<localLength;i++)
2
{
3
localArray[i]=startPointer[i];
4
}
und den Dualismus zwischen
(*a).n <==> a->n
hast du auch noch nicht durchschaut, aber das ist jetzt erst mal nicht
so wichtig.
OK. Was passiert, wenn du im Debugger weiterstepst? Kriegt localArray[0]
irgendwann mal seine Werte?
// und i korrigieren für den nächsten Schleifendurchlauf
54
i--;
55
break;
56
}
57
}
58
59
return*(localArray[0].number);
60
}
61
62
//
63
// Kopiert Array Elemente um (schiebt das Array zusammen)
64
//
65
// Kopiert size Elemente beginnend bei source nach destination um
66
voidsetArrayBack(structSignAndNumber*destination,
67
structSignAndNumber*source,
68
uint8_tsize)
69
{
70
for(uint8_ti=0;i<size;++i)
71
{
72
destination[i]=source[i];
73
}
74
}
Mach dir klar, warum dieser Code genau so und nicht anders ist. Mach dir
Zeichnungen wleche Werte miteinander verrechnet werden müssen, welche
Werte wohin kopiert werden müssen.
Wenn das dann mit 1 '+' funktioniert, dann probier Ausdrücke mit
mehreren Additionen ( 2.0 + 5.0 + 8.0 )
Das muss funktionieren! Vorher ist es sinnlos wenn ihr den Code
erweitert. Ihr verliert euch sonst in der Komplexität!
Wenn Additionen klappen dann kümmert euch um Subtraktionen. Das wird
ganz ähnlich aussehen wie Additionen.
Klappen dann Subtraktionen, dann macht ihr Multiplikationen.
Wieder: testen, testen, testen.
Danach würde ich mal die Klammern in Angriff nehmen. Die Idee die ihr da
habt, kann man so machen. Dass man im Vorfeld erst mal Klammern
auswerten und den Klammerausdruck durch den Wert ersetzen lässt. Das
wird euch eine Weile beschäftigen.
Und erst dann kommen die Gimmicks wie Wurzel oder Sinus.
Das was ihr da aufbaut ist ziemlich komplex aufgebaut. Wenn ihr euch
einen zu großen Brocken auf einmal abbeisst, werdet ihr daran ersticken.
Daher: Kleine Bissen und den dann gut kauen (== testen)!
Macht euch in den Code Notizen in Form von Kommentaren rein, aus denen
ersichtlich ist, wie die Arrays manipuliert werden müssen! Speziell wenn
ihr dann bei der Klammerauswertung seid, wird das essentiell. So wie ich
das sehe solltet ihr Pointer noch vermeiden. Arbeitet lieber mit
Indizes, das könnt ihr noch besser durchschauen. In einer Zeichnung kann
man dann leicht abzählen, welcher Index sich wie ergibt.
Das wird dann bei komplexeren Ausdrücken übersichtlicher und einfacher
in der Verwendung als wie wenn du da erst Einzelstrukturobjekte
zusammenpfriemelst.
Aber je mehr ich mir das überlege, desto weniger bin ich von dem Konzept
überzeugt. Die Idee mit dem Ersetzen ist gar nicht so gut. Ihr zrstört
euch die Zahlen im number Array. Bei Zahlen ist das noch wurscht aber
spätestens wenn dann Variablen ins Spiel kommen wird das haarig.
Die klassische Stackmaschine wäre besser gewesen. Ist auch einfacher zu
implementieren.
Oktavian Gniot schrieb:> ich wurde Sagen da fehlt ein "volatile" bei der definition der Variablen> der Struktur
Und warum würdest du das sagen?
Weißt du überhaupt, was volatile bedeutet? Oder wolltest du nur auch mal
was sagen?
Ok......
Vielen Dank für all die Tipps und Ratschläge! Ich werde sie nun
versuchen umzusetzen und werde den Code dann nochmals hier posten.
>Aber je mehr ich mir das überlege, desto weniger bin ich von dem Konzept>überzeugt. Die Idee mit dem Ersetzen ist gar nicht so gut. Ihr zrstört>euch die Zahlen im number Array. Bei Zahlen ist das noch wurscht aber>spätestens wenn dann Variablen ins Spiel kommen wird das haarig.
Meinst du mit Variablen ein CAS-System? Falls ja: Das habe ich erstmal
nicht vor zu implementieren. Ich bin schon sehr froh, wenn das jetzige
läuft.
>Die klassische Stackmaschine wäre besser gewesen. Ist auch einfacher zu>implementieren.
Kannst du das etwas genauer beschreiben?
Raphael F. schrieb:>>euch die Zahlen im number Array. Bei Zahlen ist das noch wurscht aber>>spätestens wenn dann Variablen ins Spiel kommen wird das haarig.> Meinst du mit Variablen ein CAS-System? Falls ja: Das habe ich erstmal> nicht vor zu implementieren.
Dann frag ich mich, wozu du den double Pointer in die Struktur mit
aufgenommen hast. Nimm doch den double direkt mit in die Struktur, das
vereinfacht das alles wieder ein bischen. Wenn es nicht sein muss,
benutzt man keine Pointer, nur weil es so lustig ist.
und die solve Funktion natürlich entsprechend anpassen. Da werden dann
einige 'Sonderzeichen' wegfallen :-)
>>Die klassische Stackmaschine wäre besser gewesen. Ist auch einfacher zu>>implementieren.> Kannst du das etwas genauer beschreiben?http://www.arstdesign.com/articles/expression_evaluation.html
>Dann frag ich mich, wozu du den double Pointer in die Struktur mit>aufgenommen hast. Nimm doch den double direkt mit in die Struktur, das>vereinfacht das alles wieder ein bischen. Wenn es nicht sein muss,>benutzt man keine Pointer, nur weil es so lustig ist.
Weil bei allen Zeichen der Wert garnicht relevant ist. Dort ist der
Pointer dann einfach 0x0000 oder sowas und wird auch nie beachtet. Das
verbraucht weniger Speicher als wenn ich jedesmal ein 0x0000000000000000
(8 Byte für ein Double) habe.
Ich hab
Martin schrieb:> Oktavian Gniot schrieb:>> ich wurde Sagen da fehlt ein "volatile" bei der definition der Variablen>> der Struktur>> Und warum würdest du das sagen?>> Weißt du überhaupt, was volatile bedeutet? Oder wolltest du nur auch mal> was sagen?
nein, Ich habe nur mal so eben irgendwo gehört das sei das Zauberwort
das alle Programieierungsproblemme automatisch löst.
tavin schrieb:> Ich hab>> Martin schrieb:>> Oktavian Gniot schrieb:>>> ich wurde Sagen da fehlt ein "volatile" bei der definition der Variablen>>> der Struktur>>>> Und warum würdest du das sagen?>>>> Weißt du überhaupt, was volatile bedeutet? Oder wolltest du nur auch mal>> was sagen?>> nein, Ich habe nur mal so eben irgendwo gehört das sei das Zauberwort> das alle Programieierungsproblemme automatisch löst.
Leider falsch.
Karl Heinz Buchegger schrieb:>> nein, Ich habe nur mal so eben irgendwo gehört das sei das Zauberwort>> das alle Programieierungsproblemme automatisch löst.>> Leider falsch.
Mein Weltbild Bricht zusammen...... :(
Raphael F. schrieb:>>Dann frag ich mich, wozu du den double Pointer in die Struktur mit>>aufgenommen hast. Nimm doch den double direkt mit in die Struktur, das>>vereinfacht das alles wieder ein bischen. Wenn es nicht sein muss,>>benutzt man keine Pointer, nur weil es so lustig ist.>> Weil bei allen Zeichen der Wert garnicht relevant ist. Dort ist der> Pointer dann einfach 0x0000 oder sowas und wird auch nie beachtet. Das> verbraucht weniger Speicher als wenn ich jedesmal ein 0x0000000000000000> (8 Byte für ein Double) habe.
Was sagt dir
'Premature optimization is the root of all evil'
Schaun wir mal, wie gross die Einsparung wirklich ist.
Ausdruck 2 + 3
alte Struktur
2 Zahlenknoten, macht 2 * 3 Bytes 6
1 Operatorknoten 3 Bytes 3
2 Einträge im double Array 2 * 8 16
------
25
neue Struktur
3 Knoten a 9 Bytes 27 Bytes
OK. du brauchst 2 Bytes weniger. Dafür hast du eine zusätzliche
Fehlerquelle (das Zahlenarray kann zu klein dimensioniert sein) und der
Code wird komplizierter.
(PS: so wie es aussieht, benutzt du den gcc. Bei dem sind auf dem AVR
double nur 4 Bytes gross. Die Rechnung steht dann 6 + 3 + 8 = 17 gegen
3 * 5 = 15, und die neue Struktur gewinnt.
>Weil bei allen Zeichen der Wert garnicht relevant ist. Dort ist der>Pointer dann einfach 0x0000 oder sowas und wird auch nie beachtet. Das>verbraucht weniger Speicher als wenn ich jedesmal ein 0x0000000000000000>(8 Byte für ein Double) habe.
Aber ich habe gerade bemerkt, dass mir das einige Probleme bereitet
(z.B. wenn ich bei der Auflösung von Klammern die letzte Klammer in eine
Zahl "verwandle"). Ich werde die doubles doch direkt in den Struct
nehmen. Wenn ich speicherprobleme kriege, muss ich das halt noch
irgendwie verbessern...
Darf ich fragen, was du machst.
Bist du Schüler, Student? Auf welchem Level arbeitest du?
Der Grund: So wie du das angehst, ist das sehr aufwändig. Du musst
nämlich zuerst einmal aus der textuellen Eingabe dein Array aufbauen,
wobei sich natürlich das Problem ergibt, wie du das dimensionierst. Die
Sache mit der Ersetzung sieht zwar auf den ersten Blick logisch aus, ist
aber in der Praxis ziemlich aufwändig, wie du schon gemerkt hast. Durch
das ständige Array hin und her schaufeln wird es dann auch noch ziemlich
langsam. Kurz und gut: eigentlich hat das überhaupt keinen Vorteil,
ausser das es mehr oder weniger 1:1 der Vorgehensweise in der
Satzableitung entspricht, wie man sie in der Theorie der formalen
Sprachen lernt, wenn man versucht einen Eingabetext anhand der Grammatik
als Satz dieser Grammatik durch bottom up Ersetzungen zu beweisen.
Du könntest
* einen Parser bauen, der direkt auf dem Eingabetext aufsetzt.
* der eine Stackmaschine wie im besagten Link benutzt
* oder aber zb einen Recursive Descent Parser bzw. Evaluator
implementiert.
Solange du einen arithmetischen Ausdruck nicht umformen musst (zb um ihn
zu vereinfachen oder zb ihn zu differenzieren), sondern ihn nur
Evaluieren musst, bringt das Überführen in eine spezielle Struktur mit
Knoten nichts. Das eigentliche Problem wird dadurch nicht einfacher, du
hast nur die Daten in eine etwas strengere Form gebracht.
Ich weiss aber nicht, ob ich dir insbesondere letzteres empfehlen soll,
das geht schon ein bischen in Compilerbau hinein (dieselben Techniken)
und ist in der Theorie schon ein wenig anspruchsvoll auch wenn die
Implementierung danach relativ einfach ist (wenn man die Theorie intus
hat)
Wow!
Das mit der Stackmaschine ist (klingt auf jeden fall) ja voll Simpel! Es
wäre auf jeden Fall fiel schneller! Dass ich bei meiner jetzigen
Variante noch einen zweiten text-parser brauche weiss ich.
Ich bin übrigens in der 11. Klasse (und gehe in der Schweiz zur Schule,
dort haben wir noch 13 Jahre).
Also, ich habe jetzt meinen Code umgeschrieben: So weit wie möglich auf
Pointer verzichtet usw...
Das Problem mit dem Kopieren der struct-Arrays bleibt aber. Ich habe
einen Screenshot des Disassembly gemacht. Die Stelle
1
000001B6 LD R0,Z+ Load indirect and postincrement
2
000001B7 ST X+,R0 Store indirect and postincrement
geht er 5 mal durch (so gross ist das zu kopierende struct). Am Anfang
ist der Z-pointer auch auf der richtigen stelle, aber der X-pointer ist
auf 0x0839 anstatt 0x083D (die Adresse von localArray)
Die Zeilen
1
for(doublei=0;i<result;i=i+0.12345)
2
{
3
PORTB=(uint8_t)i*12345;
4
}
sind nur dazu da, dass der compiler result nicht ignorieren kann (sonst
würde er es vllt. wegoptimieren)
Ich habe den Compiler (wenn auch nur eher provisorisch) überlistet:
1
structSignAndNumberlocalArray[length];
2
uint8_tlocalLength=length;
3
asmvolatile("push r18"::);
4
asmvolatile("ldi r18, 4"::);
5
asmvolatile("add r16, r18");
6
asmvolatile("adc r17, __zero_reg__"::);
7
asmvolatile("pop r18");
8
for(uint8_ti=0;i<localLength;i++)
9
{
10
localArray[i]=expression[i];
11
}
Da die Adresse von localArray vorher in r16/r17 ist, habe ich einfach
per inline-Assembler r16 und r17 um 4 erhöht, sodass sie jetzt auf
0x083D zeigen. Sobald der Compiler ein anderes Register nimmt muss ich
es halt anpassen... (eben: nur provisorisch)
Ich habe noch eine andere Theorie: Die Watch ist falsch.
Denn wenn ich es mit "Korrektur" (siehe letztem Post) laufen lasse,
springt er innerhalb von solve(), nicht so, wie er sollte. Jedoch ohne
Korrektur springt er genau so, wie er sollte (was heisst, dass die werte
in localArray eigentlich stimmen sollten). Ich werde es nun einfach mal
ohne Korrektur weitertesten und schauen, ob es stimmt...
Ja: die watch ist falsch (wird hoffentlich irgendwann mit einem update
beseitigt). Ich lasse den Code nun ohne irgendeine Änderung bezüglich
der Array-Kopier-Funktion laufen, und er funktioniert schon sehr gut
(Klammern kombiniert mit +-*/ läuft perfekt :-))) )!
Ich glaube, ich werde ihn vorerst nicht auf die Stackmaschine
umschreiben, da er auch so (mit 16MHz) genügend schnell sein sollte.
Vielen Dank für die ganze Hilfe, die Tipps und Ratschläge, kbuchegg!!
Ich habe nocheinmal den Code in der aktuellen Version gepostet.