MoinMoin
ich versuche gerade mich ein wenig mit der Abbildung dreidimensionaler
Körper auf dem 2dimensionalen Bildschirm zu befassen.
Hierzu habe ich mir mehrere structs erstellt.
1
typedefstruct
2
{
3
floatX;
4
floatY;
5
floatZ;
6
}sKoordinate;
7
8
typedefstruct
9
{
10
sKoordinatePunkt1;
11
sKoordinatePunkt2;
12
sKoordinatePunkt3;
13
}sDreieck;
14
15
sDreieckDreieck1;
16
sDreieckTriSchieber;
dann setze ich die Koordinaten von Dreieck1 und TriSchieber noch auf
sinnvolle Werte, und dann will ich das Dreieck1 um die Werte von
TriSchieber verschieben, indem ich:
1
Dreieck+=TriSchieber;
setze.
Dafür bekomme ich aber folgende Fehlermeldung:
\main.c|92|error: invalid operands to binary + (have 'sDreieck' and
'sDreieck')|
Wieso kann ich denn die Structs nicht einfach addieren? Die sind doch
beide vom gleichen Typ? Muss ich wirklich jedes Element einzeln
addieren, oder gibt es da elegantere Lösungen?
Auch Googel hilft mir nicht so recht weiter, da ich nicht so recht weiß,
was genau ich denn nun eigentlich fragen muss.... Die Fehlermeldung half
nicht weiter, "Structs addieren" "Structs addieren in C" gab alles nur
die Tendenz, dass ich die Elemente einzeln addieren muss...
Mit Dank im voraus und freundlichen Grüßen,
Chaos
P.S. Ich weiß, ein Dreieck ist noch kein dreidimensionaler Körper, aber
ich wollts für die grundsätzliche "Verschiebungsübung" erstmal
kleinhalten. Die Darstellung eines Kubus klappt schon, indem ich die
Z-Koordinate jeweils mal sin45° nehme, und auf den jeweiligen X und
Y-Wert addiere. Planare Projektion, oder so ähnlich, ich überflog da
neulich mal den Wikiartikel, da wurden einige Arten der Projektion
genannt, und da will ich mich nun langsam rantasten.
Das ist eigentlich eine klassische Anwendung einer objektorientierten
Sprache wie C++.
Da geht das dann.
Du überlädst den "+" Operator, indem Du dann die Elemente einzeln
addierst.
P.P.S.
Ich arbeite mit CodeBlocks13.12 mit dem GCC wenn ich mich nicht irre.
Und das ganze aufm PC/Schlepptop.
Soo schnell hät ich mit keiner Antwort gerechnet.
Ja dass sich das ganze sehr OO anfühlt, ist mir auch schon
aufgefallen....
J. T. schrieb:> Wieso kann ich denn die Structs nicht einfach addieren? Die sind doch> beide vom gleichen Typ? Muss ich wirklich jedes Element einzeln> addieren, oder gibt es da elegantere Lösungen?
C ist eine auf Minimalismus ausgelegte Sprache. Die kann solche Gimmicks
nicht. Auch Funktionsüberladungen sind den C-Usern ja schon zu
abgefahren.
Verwende einfach eine Sprache, die das Definieren eigener Operatoren
erlaubt, wie z.B. C++:
C++ erlaubt noch eine Menge weitere nützliche Dinge, um Programme kürzer
und einfacher zu machen. Aber gleich kommen wieder die Leute, die
erklären dass C++ viel zu viel kann, dich dazu zwingt das böse OOP zu
nutzen (falsch), nicht für jede noch so obskure Plattform verfügbar ist,
usw.
Programmierer schrieb:> int main () {> sDreieck d1, d2, d3;> d3 = d1 + d2;> }
und die Uunterscheidung welchen der "Plusoperatoren" er nutzen soll,
trifft er dann anhand des Typs?
Sprich wenn ich nebenbei noch 2 int habe, i und j meinetwegen, dann
kommt er da nicht durcheinander, wenn ich sowas wie:
d3 = d1 + d2;
i = j + 4;
schreibe?
Kennst du zufällig Allegro? Das nutze ich für die grafische Ausgabe.
Programmierer schrieb:> Aber gleich kommen wieder die Leute, die> erklären dass C++ viel zu viel kann, dich dazu zwingt das böse OOP zu> nutzen (falsch)
Also wenn ich mit OOP so einfach sowas machen kann, kann es sooo böse
nicht sein ;-)
J. T. schrieb:> und die Uunterscheidung welchen der "Plusoperatoren" er nutzen soll,> trifft er dann anhand des Typs?
Ja.
J. T. schrieb:> Sprich wenn ich nebenbei noch 2 int habe, i und j meinetwegen, dann> kommt er da nicht durcheinander, wenn ich sowas wie
Im Zweifelsfall gibts nen "ambiguous overload" error oder so.
J. T. schrieb:> Kennst du zufällig Allegro? Das nutze ich für die grafische Ausgabe.
nur vom Hören.
J. T. schrieb:> Also wenn ich mit OOP so einfach sowas machen kann, kann es sooo böse> nicht sein ;-)
Tja. der code ist jetzt noch nicht sehr objektorientiert, nutzt nur die
Features der Sprache, die für OOP gedacht sind.
> und dann will ich das Dreieck1 um die Werte von TriSchieber verschieben
Abgesehen davon macht allerdings auch die ganze Operation keinen Sinn.
Denn was soll das sein, ein Dreieck um ein anderes Dreieck zu
verschieben? Ein Dreieck, allgmeiner irgendeine Form, wird um einen
Vektor verschoben. Eine Verschiebeoperation ist dann wiederrum weiter
eine Spezialisierung einer Matrixoperation. D.h. die ganz allgemeine
Form sieht so aus:
Du beschreibst Objekte indem du ihre Eckpunktskoordinaten als Vektoren
darstellst.
Du manipulierst Objekte (verschieben, drehen, skalieren) indem du eine
entsprechende Matrix Operation auf jeden Eckpunkt anwendest.
Die grundlegende Operation dazu ist also die Multiplikation eines
Vektors mit einer Matrix welche als Ergebnis den so manipulierten Vektor
hat. Ein Objekt, gegeben durch seine Eckpunktsvektoren wird dann
mithilfe der Matrix als ganzes manipuliert, indem man jeden Eckpunt mit
der Matrix multipliziert.
Aber es schadet auch nichts, erst mal Verschiebungen als eigene
Operation zu definieren, die einen Vektor und ein Objekt nimmt, und den
Verschiebevektor auf das Objekt anwendet. Dasselbe dann für Rotation.
Irgendwann merkt man dann schon, warum es sich lohnt, wenn man beide
Manipulationen mit ein und derselben Operation ausdrücken kann. Von da
weg, ist es dann auch bis zur perspektivischen Abbildung nicht mehr
weit.
Karl H. schrieb:> Abgesehen davon macht allerdings auch die ganze Operation keinen Sinn.> Denn was soll das sein, ein Dreieck um ein anderes Dreieck zu> verschieben? Ein Dreieck, allgmeiner irgendeine Form, wird um einen> Vektor verschoben. Eine Verschiebeoperation ist dann wiederrum weiter> eine Spezialisierung einer Matrixoperation. D.h. die ganz allgemeine
Ja genau so mach ich das auch. Das "Verschiebedreick" ist kein Dreieck,
sondern nur ein Platzhalter, da steht an jedem Punkt einfach nur der
Wert, um den Verschoben wird. Und während ich das hier schreibe, merke
ich, dass das tatsächlich unnötig ist. Ich brauche ja lediglich eine
einzelne Koordinate (ein Vektor ist es dann wohl, bzw Skalar?) um die
ich Verschiebe.
Mit dem Dreieck hatte ich mir das gedacht, das ich einfach jeden Punkt
des Verschiebedreiecks auf den selben Wert setze, und dann die beiden
"Dreiecke" einfach addiere. Aber da ich Structs nicht direkt addieren
kann, langt dann wohl der einzelne Verschiebewert.
Karl H. schrieb:> Du beschreibst Objekte indem du ihre Eckpunktskoordinaten als Vektoren> darstellst.
Genau das hab ich ja vor, deshalb hatte ich den Verschiebewert/Vektor
quasi als Dreieck aufgefasst, damit die Structs gleich sind. Ich dachte
halt, wenn man die Structs gleich hält, könnte man sie addieren, das
hätte das alles vereinfacht.
J. T. schrieb:> Und während ich das hier schreibe, merke ich, dass das tatsächlich> unnötig ist. Ich brauche ja lediglich eine einzelne Koordinate> (ein Vektor ist es dann wohl, bzw Skalar?) um die ich Verschiebe.
ein Vektor
> Ich dachte> halt, wenn man die Structs gleich hält, könnte man sie addieren, das> hätte das alles vereinfacht.
Äh. Nicht wirklich.
Momentan ist deine Basisoperation die Addition 2-er Vektoren. Um ein
Dreieck um einen Vektor zu verschieben, verschiebst du jeden einzelnen
der 3 Punkte des Dreiecks um denselben Vektor.
Programmierer schrieb:> Aber gleich kommen wieder die Leute, die erklären dass C++ viel zu> viel kann, dich dazu zwingt das böse OOP zu nutzen (falsch), nicht für> jede noch so obskure Plattform verfügbar ist, usw.
Dabei nutzt dein Beispiel nicht einmal OOP, sondern lediglich das
Überladen von Operatoren.
Doch das ist aber gerade einer der Voreile von C++ gegenüber anderen
OOP-Sprachen: Man kann OOP gewinnbringend dort nutzen, wo sie sinnvoll
erscheint, für alle anderen Fälle wird auch prozedurale Programmierung
unterstützt.
J. T. schrieb:> Genau das hab ich ja vor, deshalb hatte ich den Verschiebewert/Vektor> quasi als Dreieck aufgefasst, damit die Structs gleich sind.
Beim Überladen von Operatoren dürfen die beiden Operanden auch
unterschiedlichen Typs sein. Es stellt sich allenfalls die Frage, ob "+"
das richtige Symbol für das Verschieben eines geometrischen Objekts
entlang eines Vektors ist, oder ob man diese Funktion bzw. Methode nicht
besser "translate" o.ä. nennt.
Karl H. schrieb:> Momentan ist deine Basisoperation die Addition 2-er Vektoren. Um ein> Dreieck um einen Vektor zu verschieben, verschiebst du jeden einzelnen> der 3 Punkte des Dreiecks um denselben Vektor.
Und genau das tue ich doch. Bzw dachte, es würde so funktionieren.
Verschiebedreieck.Punkt1(1,0,0); (x,y,z,)
Verschiebedreieck.Punkt2(1,0,0);
Verschiebedreieck.Punkt3(1,0,0);
Im Verschiebedreieck jeden Punkt auf den Verschiebvektor setzen.
Dreieck.Punkt1(0,0,0);
Dreieck.Punkt2(6,0,0);
Dreieck.Punkt3(3,3,0);
Das Dreieck erzeugen. Beide "Dreiecke" addieren, würde jeden
Vektor/Eckpunkt des Dreiecks um einen auf der X-Achse verschieben, ginge
es denn so, wie ich mir das gedacht hätte.
Aber dann schreib ich die Funktion erstmal so, das sie halt die Vektoren
einzeln addiert. Oder ich schau mir doch mal C++ an....
Schaunmermal. Ich versuch erstmal weiter.
Vielen Dank für eure Antworten, evtl meld ich nochmal hierzu.
MfG Chaos
Yalu X. schrieb:> Beim Überladen von Operatoren dürfen die beiden Operanden auch> unterschiedlichen Typs sein.
Was genau ist dieses Überladen nun eigentlich? Ist dass das definieren
von Operatoren, wie "Programmierer" das gezeigt hat? Und gehört das nun
zu C++ oder zu C?
Yalu X. schrieb:> oder ob man diese Funktion bzw. Methode nicht> besser "translate" o.ä. nennt.
Ja ich glaube, das ist ein guter Name.
Natürlich ist das sozusagen eine "Hochglanzaufgabe" für eine
objektorientierte Sprache wie C++.
Nichtsdestotrotz läßt sie sich auch mit einer nicht objektorientierten
Sprache lösen. Auch schon zu Fortran-Zeiten wurden erfolgreich Punkte in
den Raum projiziert (tatsächlich haben auch heutzutage viele
CAD-Programme einen nicht objektorientierten Fortran-Kern).
Und - ehrlich gesagt - C++ und Vektorrechnung bei Adam und Eva
angefangen gleichzeitig zu lernen, scheint mir doch ein wenig heftig.
Alles, was in C kein "basic type" (char, short, int, long, float und
double) kann man nicht mit den Standard-Operatoren addieren. Dafür
gibt's Funktionen. Der angehängte Code verschiebt das Dreieck t um den
Vektor v:
J. T. schrieb:> Und genau das tue ich doch. Bzw dachte, es würde so funktionieren.>> Verschiebedreieck.Punkt1(1,0,0); (x,y,z,)> Verschiebedreieck.Punkt2(1,0,0);> Verschiebedreieck.Punkt3(1,0,0);
Man kann natürlich auch von hinten durch die Brust ins Auge arbeiten.
Aber frag dich mal selber. So ein Verschiebedreick ist ja eigentlich
auch nur ein Dreieck. Erfüllt dein Verschiebedreieck die Anforderung an
ein Dreieck? Damit man bei einer Ansammlung von 3 Punkten von einem
Dreieck sprechen kann, müssen ja ein paar Bedingungen erfüllt sein. Zum
Beispiel die, das keine 2 Punkte gleich sind - denn dann ist es ja kein
Dreieck mehr, sondern eine Linie.
Wie machst du das, wenn du ein Objekt mit 128 Eckpunkten verschieben
willst? Wie mit einem Kreis?
J. T. schrieb:> Was genau ist dieses Überladen nun eigentlich? Ist dass das definieren> von Operatoren, wie "Programmierer" das gezeigt hat? Und gehört das nun> zu C++ oder zu C?
operatoren überladen gibts nur in C++. Man kann in C++ außerdem
Funktionen überladen, z.B.:
1
voidprint(intvalue){
2
// einen Integer ausgeben
3
}
4
5
voidprint(std::stringvalue){
6
// Einen String ausgeben
7
}
8
9
intmain(){
10
print(42);
11
print("The answer\n");
12
}
Der C++ Compiler wählt hier jeweils automatisch die richtige Funktion
aus, je nach Typ der Argumente. Das können viele statisch typisierte
Sprachen, nur C mal wieder nicht. Der enorme Nutzen davon macht sich
aber erst bei komplexeren Programmen bemerkbar; wenn man Variablen
ausgibt, deren Typ man vielleicht später noch ändert, wird immer
automatisch die richtige Ausgabetechnik verwendet. In C muss man bei
printf fix im Formatstring angeben was für einen Typ die Variable hat,
und wenn man den später ändert muss man alle printf's suchen, die die
Variable ausgeben, und abändern.
Wow da kam ja noch einiges =).
Markus F. schrieb:> Der angehängte Code verschiebt das Dreieck t um den> Vektor v:
Ich glaube, da hab ich in der Zwischenzeit etwas sehr ähnliches
ausgetäuftelt. Vermutlich wieder von hinten durchs Auge in die Brust
*gg. Aber trotzdem danke ich dir recht herzlich für den Code =)
Karl H. schrieb:> Aber frag dich mal selber. So ein Verschiebedreick ist ja eigentlich> auch nur ein Dreieck. Erfüllt dein Verschiebedreieck die Anforderung an> ein Dreieck? Damit man bei einer Ansammlung von 3 Punkten von einem> Dreieck sprechen kann, müssen ja ein paar Bedingungen erfüllt sein. Zum> Beispiel die, das keine 2 Punkte gleich sind - denn dann ist es ja kein> Dreieck mehr, sondern eine Linie.
Ja das ist mir soweit klar. Warum ich das so versucht hab ich ja
erklärt. Nachdem das nun nicht so geht, bin ich auch dazu übergegangen
nur noch einen einzelnen Verschiebevektor zu definieren. Ich hing halt
einfach an dem Gedanken fest, das man gleich Structs doch eigentlich
"einfach so" addieren können müsste. Auch an dich vielen Dank für deine
wie immer hilfreichen Worte =)
Programmierer schrieb:> operatoren überladen gibts nur in C++. Man kann in C++ außerdem> Funktionen überladen, z.B.:
Ich glaube, das hab ich im groben jetzt verstanden, auch dir danke ich
recht herzlich für die Hilfe =)
J. T. schrieb:> Ich glaube, da hab ich in der Zwischenzeit etwas sehr ähnliches> ausgetäuftelt.
Na das sieht doch schon gar nicht mal so schlecht aus.
Eines noch.
Hier zum Beispiel
jedes mal, wenn diese Funktion aufgerufen wird, wird eine komplette
Kopie der Argumente erzeugt. Das kostet.
1
Punkt.X+=Schiebevektor.X;
2
Punkt.Y+=Schiebevektor.Y;
3
Punkt.Z+=Schiebevektor.Z;
beim Punkt ist es ok, wenn du eine Kopier erzeugen lässt, denn diese
Kopier kannst du sinnvoll nutzen, weil du sie ja in der Funktion lokal
verändern willst.
Aber der Schiebevektor, der braucht keine Kopie zu sein. Wenn dir der
Aufrufer ganz einfach sein Objekt zur Verfügung stellt und du damit
arbeiten kannst, dann wäre das völlig ausreichend.
dazu reicht es aber, wenn dir der Aufrufer einen Pointer von seinem
Objekt zur Verfügung stellt.
und da du nett bist, gibst du dem Aufrufer auch die Zusicherung: mach
dir keine Sorgen, ich werde den Pointer nicht benutzen um damit dein
Obekt zu verändern. Ich werde den Pointer nur benutzen um damit die
Werte innerhalb des Objektes abzufragen, aber abgesehen davon vergreife
ich mich nicht daran
hier wird nicht nur eine Koordinate kopiert, sondern es wird auch eine
Kopie des kompletten Dreiecks angelegt. Und das bei jedem Aufruf!
Auch hier
1
BITMAP*DreieckZeichnen(sDreieckTri)
gut. in der jetzigen Phase ist das wahrscheinlich noch kein Thema. Aber
wenn das einmal mehr wird, dann machen sich diese unnötigen Kopien
bemerkbar. D.h. du solltest jetzt dein Parameter Passing Wissen auf
Vordermann bringen. In C muss man eben ziemlich viele Dinge einwandfrei
beherrschen, um vernünftig programmieren zu können.
Zumal. Irgendwann werden dir Dreiecke nicht mehr genügen. Irgendwann
bist du bei Vielecken mit zb 256 Eckpunkten, die zb einen Kreis
darstellen sollen. Soll das jedesmal, bei jedem Funktionsaufruf immer
wieder kopiert werden?
Karl H. schrieb:> gut. in der jetzigen Phase ist das wahrscheinlich noch kein Thema. Aber> wenn das einmal mehr wird, dann machen sich diese unnötigen Kopien> bemerkbar. D.h. du solltest jetzt dein Parameter Passing Wissen auf> Vordermann bringen. In C muss man eben ziemlich viele Dinge einwandfrei> beherrschen, um vernünftig programmieren zu können.
Ich seh den Punkt auf den du hinauswillst! Das ist ein guter Einwand,
das werd ich mir direkt mal zur Brust nehmen.
Karl H. schrieb:> Zumal. Irgendwann werden dir Dreiecke nicht mehr genügen. Irgendwann> bist du bei Vielecken mit zb 256 Eckpunkten, die zb einen Kreis> darstellen sollen. Soll das jedesmal, bei jedem Funktionsaufruf immer> wieder kopiert werden?
Es sollen irgendwann ja nicht mehr bloße flächenbehaftete "Körper"
dargestellt werden, sondern volumenbehaftete Körper. Diese habe ich vor,
aus Dreiecken zusammenzusetzen. Mir schwirrt da aus grauer Vorzeit noch
was von Polygonen im Kopf rum ;-). Das wird der nächste Schritt. Und
danach muss ich mir etwas ausdenken, das berechnen kann, welche Punkte
dann inerhalb einer solchen Figur sind, damit sich dann an solche Sachen
wie kollisionserkennung machen kann. Ist zumindest der langfristige
Plan. Mal sehen, auf welche Hindernisse ich da so stoße.
Es macht immer wieder Spass von dir zu lernen, du bringst die Sachen auf
den Punkt und mir fällt es meist leicht, deinen Ausführungen zu folgen.
Find ich wirklich spitze!
Karl H. schrieb:> Punkt.Z += Schiebevektor->Z;
Vor einiger Zeit hatte ich schonmal die Frage nach dem Unterschied
zwischen Bla.Sülz und Bla->Sülz gefragt. Da hattest du auch schon was
gesagt, jetzt ist mir das grad völlig klar geworden ;-)!
J. T. schrieb:> Es sollen irgendwann ja nicht mehr bloße flächenbehaftete "Körper"> dargestellt werden, sondern volumenbehaftete Körper. Diese habe ich vor,> aus Dreiecken zusammenzusetzen.
Auch das kann man machen. Hängt davon ab, wie 'komplex' dann letzten
Endes die Seitenflächen werden können.
Einfache konvexe Polygone kann man auch 'on the fly' in Dreiecke
zerlegen. Das hat wiederrum den Vorteil, dass man nicht jeden Punkt
x-mal speichern muss. Für beliebige Polygone ist das Verfahren etwas
komplizierter, ist aber immer noch relativ simpel handhabbar, solange
keine Löcher in den Polygonen vorkommen. Mit Löchern wirds dann noch
einmal einen Tick komplexer.
Aber da hast du noch einiges vor dir.
> Mir schwirrt da aus grauer Vorzeit noch> was von Polygonen im Kopf rum ;-). Das wird der nächste Schritt. Und> danach muss ich mir etwas ausdenken, das berechnen kann, welche Punkte> dann inerhalb einer solchen Figur sind, damit sich dann an solche Sachen> wie kollisionserkennung machen kann.
Och das ist an und für sich gar nicht so schwer. So ein Dreieck (oder
allgemeiner ein Polygon) hat ja 2 unterscheidbare Seiten. Mach dich dann
mal zum Thema 'Normalvektor' schlau. Der hilft dir die Seiten zu
unterscheiden. In einem konvexen Körper ist ein Punkt genau dann innen,
wenn er von allen Seitenflächen aus betrachtet auf der 'Hinterseite'
dieser Fläche liegt. Im allgemeinen Fall eines beliebigen Körpers wirds
etwas kniffliger. Allerdings macht man bei derartigen
Kollisionserkennungen sowieso einen Vortest mit einem oder mehreren
einfachen geometrischen Körper(n). Anstatt die 2 Millionen Dreiecke zu
untersuchen, die die Rosen mitsamt Blumenvase beschreiben, stellt man
das Objekt gedanklich erst mal in einen entsprechend grossen Quader.
Liegt der zu untersuchende Punkt nicht in diesem Quader, dann braucht
man auch die 2 Millionen Dreiecke der Rosen nicht weiter untersuchen.
> Plan. Mal sehen, auf welche Hindernisse ich da so stoße.
Viel Spass. Geometrie und Computergraphik ist ein spannendes und
vielfältiges Thema.
Karl H. schrieb:> Einfache konvexe Polygone kann man auch 'on the fly' in Dreiecke> zerlegen.
Das geht dann zum Beispiel so (Bild)
Einen Punkt des Polygons hält man fest. Das ist quasi der eine Punkt,
der in allen Dreiecken gemeinsam vorkommt. Die restlichen beiden Punkte
der Dreiecke erhält man, indem man jeweils 2 aufeinanderfolgende Punkte
des Polygons hernimmt. Zusammen mit dem fixen Punkt hat man dann ein
Dreieck, welches durch die Rendering Pipeline gejagt wird.
Vorteil: Um dieses Polygon zu speichern, braucht man sich nur die 6
Eckpunkte des Polygons merken (und transformieren). Aus lauter Dreiecken
zusammen gesetzt wären das 4 Dreiecke und da jedes Dreick, no na, aus 3
Punkten besteht, 12 zu merkende Punkte. (die natürlich auch
transformiert werden müssten). D.h. man hat 6
Koordinatentransformationen eingespart und sich im Gegenzug einen
relativ einfachen Algorithmus (für konvexe Polygone!) zum zeichnen
eingefangen
HILFE!!,
was ist denn jetzt passiert?
Ich wollte das bei der Translation testweise auf den Pointer ändern, da
hat er mir nen Fehler geworfen. Zeile 11...
int main()
{ <----Zeile 11.
Davor würde er ein "=", ";" usw erwarten....?!?!
Ich hab die Änderungen wieder rückgängig gemacht, aber immernoch die
Fehlermeldung.
Könntest du die beiden Dateien evtl mal überfliegen, ob dir irgendwas
offensichtliches ins Auge springt, was ich übersehen hab?
Ich danke dir.
Ich habs in meiner Verzweiflung nun in nem neuen Projekt nochmal 1:1
zusammenkopiert, hab das Apostroph dort aber eingefügt, vermutlich
dachte ich ich habs nicht mit kopiert.... Das hat wieder funktioniert,
da war die Verwunderung groß. Aber das fehlende ; erklärts natürlich.
Ich hasse das Apostroph, man übersieht ein fehlendes so leicht... Obwohl
das immer das erste ist wonach ich schaue *gg.
:-)
Wenn du seltsame unerklärliche Fehlermeldungen bekommst, dann immer von
der Stelle des Auftretens des Fehlers rückwärts suchen ob in einer(!)
der vorhergehenden Zeilen ein ; vergessen wurde. Wenn eine der
vorhergehenden Zeilen ein #include war, dann in dieses Include File
hinein und ganz unten mit der Rückwärtssuche fortfahren.
Karl H. schrieb:> :-)>> Wenn du seltsame unerklärliche Fehlermeldungen bekommst, dann immer von> der Stelle des Auftretens des Fehlers rückwärts suchen ob in einer(!)> der vorhergehenden Zeilen ein ; vergessen wurde.
Oder eine geschweifte Klammer, oder eine runde Klammer, oder eine
eckige, ...
Ich habe nie verstanden, warum der Compiler deren Anzahl beim Übersetzen
nicht einfach mitzählt. Sobald man eine unterschiedliche Anzahl von
öffnenden und schließenden Klammern feststellt, könnte man eine
entsprechende Fehlermeldung ausgeben. Aber nein, stattdessen kriegt man
dann zehntausend Fehlermeldungen auf einmal ;-)
Mark B. schrieb:> Ich habe nie verstanden, warum der Compiler deren Anzahl beim Übersetzen> nicht einfach mitzählt. Sobald man eine unterschiedliche Anzahl von> öffnenden und schließenden Klammern feststellt, könnte man eine> entsprechende Fehlermeldung ausgeben. Aber nein, stattdessen kriegt man> dann zehntausend Fehlermeldungen auf einmal ;-)
Mein Reden!!! :D
J. T. schrieb:> Mark B. schrieb:>> Ich habe nie verstanden, warum der Compiler deren Anzahl beim Übersetzen>> nicht einfach mitzählt. Sobald man eine unterschiedliche Anzahl von>> öffnenden und schließenden Klammern feststellt, könnte man eine>> entsprechende Fehlermeldung ausgeben. Aber nein, stattdessen kriegt man>> dann zehntausend Fehlermeldungen auf einmal ;-)
Das Problem ist, dass das nicht so einfach ist.
Was für dich eine einfache Sache ist, die du durch hinschauen löst, ist
für den Compiler keineswegs offensichtlich. In
1
1voidfoo()
2
2{
3
3if(i==9){
4
4}
5
5
6
6voidbar()
7
7{
8
8}
ist für dich durch hinsehen offensichtlich, dass in Zeile 6 offenbar
eine neue Funktion anfängt wodurch klar ist, dass in der vorhergehenden
Funktion eine schliessende Klammer fehlen muss.
Für den Compiler sieht die Sache aber ganz anders aus. Für ihn
präsentiert sich die Sache so, dass die schliessende Klammer in Zeile 4
zur öffnenden Klammer in Zeile 3 gehören muss. D.h. Er sieht erst mal
nur
1
voidfoo()
2
{
3
if(i==9){
4
}
5
6
voidbar
und dieses hier auftauchende void kann nicht an dieser Stelle stehen, da
es hier syntaktisch nicht zulässig ist. Das die Fortsetzung dann
suggerieren würde, dass es sich hier eigentlich um die versuchte
Deklaration einer Funktion handelt, das kriegt der Compiler gar nicht
mehr mit.
Fehlererkennung und sinnvolle Fehlermeldungen zu generieren ist
erstaunlicherweise in einem Compiler eine ziemlich schwierige Sache.
Solange man es mit einer zeilenorientierten Sprache zu tun hat, geht es
ja noch. Denn da kann man wenigstens davon ausgehen, dass man alles in
einer Zeile beisammen hat. Hat man dann erst mal ermittelt, was die
übergeordnete Bedeutung der Zeile sein könnte (zb die Definition einer
Funktion), dann folgt daraus schon sehr viel. Wenn aber
Freiformformatierung zulässig ist, wie in C, kann es schon schwierig
werden aus den nächsten n Symbolen zu erkennen, was hier wohl gemeint
sein könnte. Zudem gibt es in C einige syntaktische Probleme, die die
Sache nicht gerade vereinfachen. Gerade Dinge wie "man kann bei einer
Strukturdefinition auch gleich Variablen von diesem Typ anlegen" sind
anfällig für derartige Dinge.
ist in
1
structvect
2
{
3
intx;
4
inty;
5
}int...
nach der Strukturdefinition ein ; vergessen worden, oder hat der
Programmierer einen Tippfehler gemacht und die Variable sollte gar nicht
int heissen? Ist es so geschrieben
1
structvect
2
{
3
intx;
4
inty;
5
}a,int,b;
dann ist ein Tippfehler naheliegend. Ist es aber so geschrieben
1
structvect
2
{
3
intx;
4
inty;
5
}aintb;
dann hat er wohl einen ; vergessen und das sollte eigentlich
1
structvect
2
{
3
intx;
4
inty;
5
}a;
6
7
intb;
lauten. Unterschied: ein lausiges Komma zwischen dem a und dem int.
Das was Du sagst ist schon alles richtig, Karl Heinz. Nichtsdestotrotz:
Die Zahl der öffnenden und schließenden Klammern in einer einzelnen
Datei zu zählen, das sind eine Handvoll Zeilen Code. Zumindest als eine
Art "optionale Plausibilitätsprüfung" könnte man das einbauen.
Mark B. schrieb:> Das was Du sagst ist schon alles richtig, Karl Heinz. Nichtsdestotrotz:> Die Zahl der öffnenden und schließenden Klammern in einer einzelnen> Datei zu zählen, das sind eine Handvoll Zeilen Code. Zumindest als eine> Art "optionale Plausibilitätsprüfung" könnte man das einbauen.
Klar könnte man.
Dann steht ganz unten: Die Anzahl schliessender Klammern stimmt nicht
mit der Anzahl öffnender Klammern überein. Bringt dir das was? Nicht
wirklich.
Karl H. schrieb:> Klar könnte man.> Dann steht ganz unten: Die Anzahl schliessender Klammern stimmt nicht> mit der Anzahl öffnender Klammern überein. Bringt dir das was? Nicht> wirklich.
In vielen Fällen (okay, nicht in allen) befinden sich die Klammernpaare
in der gleichen Zeile (bei runden und eckigen Klammern). Wenn einen der
Compiler optional darauf hinweisen könnte wo dies ist, könnte man sich
einiges an Rumsuchen ersparen.
Bei geschweiften Klammern wird es zugegeben schwieriger.
Nichtsdestotrotz finde ich aussagekräftigere Fehlermeldungen gut. Ist
hier nicht auch Clang/LLVM besser als gcc? Meine da sowas gelesen zu
haben, wobei das wohl in Bezug auf Templates war. Da kriegt man ja
teilweise wirklich kryptischen Schrott (sorry aber ist so) um die Ohren
geworfen als Programmierer ;-)
Mark B. schrieb:> Nichtsdestotrotz finde ich aussagekräftigere Fehlermeldungen gut. Ist> hier nicht auch Clang/LLVM besser als gcc?
In vielen Fällen ja, so auch hier:
Clang:
1
./GrafikTest_Init.h:49:75: error: expected ';' after top level declarator
Der GCC lässt sich etwas an der Nase herumführen, weil er glaubt, es
handle sich bei der Funktionsdeklaration um eine fehlerhafte
Funktionsdefinition im K&R-Stil, wo die Argumenttypen nach der
schließenden runden Klammer deklariert werden. Dass seine Annahme falsch
ist, erkennt er erst am Ende von main.c, nachdem er breits 5 andere
Fehlermeldungen ausgegeben hat und der User schon lange nicht mehr
mitliest. Er springt dann wieder in GrafikTest_Init.c zurück und
erwischt dort immerhin die richtige Zeile:
GCC:
1
...
2
In file included from main.c:4:0:
3
GrafikTest_Init.h:49:10: error: old-style parameter declarations in prototyped function definition