Hallo zusammen, Wer mal keine Lust auf Sudoku oder so hat, kann sich ja mal an dem C-Rätsel probieren. Was macht die folgende Funktion? int getmessage (int c) { return (!(c&~3))["(c&3)"]&c&3; } Mit freundlichen Grüssen Gastino/Linda
Da kommt Kässekuchen raus, mmmmhhhh, könnt ihr das richen. Und dann kommt die Sonne raus an einem schönen Frühlingmorgen nach einer wollkigen Woche, die Luft ist do rein und es richt nach Blütten in den Bäumen......die Vögel zwitschern und frisches Kod ist auf dem Bürgersteig...... come on be serious!!!!!!!
> int getmessage (int c) > { > return (!(c&~3))["(c&3)"]&c&3; > } > > setze mal für c 10 ein.... was kommt raus? Für den ersten Teil switche ich mal zu binär 3 00000011 ~3 11111100 c (10) 00001010 c&~3 00001000 also 8 !(c&~3) da c&~3 ungleich 0 ist, macht der ! eine satte 0 daraus 0["(c&3)"] ergitb daher '(', 0x28, also 00101000 00101000 & c 00101000 00001010 --------- 00001000 & 3 00000011 --------- 00000000 Wenn ich mich zwischendurch nicht verhaut habe, dann kommt da also 0 raus. Was das ganze allerdings sein soll, kann ich nicht erraten.
Was diese Funktion macht? Sie zeigt uns wie man unverständlich programmiert (durch Weglassen von erklärenden Kommentaren), und daß das "C" als Sprachenname für "cryptic" steht .. :-)
Carsten Pietsch wrote: > Was diese Funktion macht? Sie zeigt uns wie man unverständlich > programmiert (durch Weglassen von erklärenden Kommentaren), und daß das > "C" als Sprachenname für "cryptic" steht .. :-) Ich tendier eher dazu, dass hier jemand absichtlich möglichst kryptisch sein wollte um irgendetwas zu demonstrieren. Wahrscheinlich ist das Bsp. extra für die Frage hergestellt worden. Irgendeinen tieferen Sinn kann ich immer noch nicht erkennen. Soviel steht aber fest: Die Funktion kann nur Werte im Bereich 0 bis 3 (inklusive) liefern.
Diese Aufgabe hat nicht viel mit Programmieren zu tun. Ist eine Aufgabe aus der letzten CE Programmieren 1 Klausur...
Gastinio/Linda wrote: > Diese Aufgabe hat nicht viel mit Programmieren zu tun. Ist eine Aufgabe > aus der letzten CE Programmieren 1 Klausur... Dann gehts also eher darum, das Ding zu analysieren. Also: Wie liege ich?
Karl-Heinz, frag nicht so unproduktiv zurück .. :-) Linda hätte gerne ihre Hausaufgabe von Dir erledigt..
Aber jetzt hab ich mal für dich ein Rätsel: In einem Ort werden Temperaturen gemessen. Obwohl die Temperaturen alle in einem vernünftigem Bereich liegen ist der errechnete Durchschnittswert ungewöhnlich hoch:
1 | #include <stdio.h> |
2 | |
3 | const int march[31] = { |
4 | 8, 5, 7, 2, -4, -14, -7, -4, -2, 0, |
5 | 0, 2, 5, 7, 2, -4, -14, -7, -4, -2, |
6 | 1, 7, 2, 2, -2, -3, -4, 6, -4, 3, 9 }; |
7 | |
8 | int main() |
9 | {
|
10 | unsigned i, count = 31; |
11 | int sum = 0; |
12 | |
13 | for( i = 0; i < count; i++ ) |
14 | {
|
15 | sum += march[ i ]; |
16 | }
|
17 | printf( "The average low temperature in March was" |
18 | " %d degrees\n", sum / count ); |
19 | return 0; |
20 | }
|
Was geht hier vor?
wohl nicht wirklich ein Fehler, aber in der printf-Zeile würde ich was ändern ...
@PeakRunner: Da kommt ziemlich Unsinn raus, das würde ich schon einen schweren Fehler nennen. Mit float wäre das nicht passiert... ;) Markus
Naja, wie man es sieht, die Rechnung an sich ist ja richtig, die Frage ist ja nur mit was man rechnet ...
PeakRunner wrote: > wohl nicht wirklich ein Fehler, > aber in der printf-Zeile würde ich was ändern ... Was würdest du ändern?
1 | printf( "The average low temperature in March was" |
2 | " %f degrees\n", (float) sum / count ); |
PeakRunner wrote:
>
1 | > printf( "The average low temperature in March was" |
2 | > " %f degrees\n", (float) sum / count ); |
3 | >
|
Wäre ne Möglichkeit. Aber nimm mal an, ich gestehe dir kein float zu. Was ist das Problem im ursprünglichen Code?
> Welcher März hat denn 31 Tage???
derjenige, der genau so viele Nächte hat, so ziemlich jeder.
Zum Rätsel: Man muss halt die Zeichen - äähh Vor-Zeichen der Zeit resp.
Zahl im Auge behalten, sonst gehen einem die Augen oder Zahlenbereiche
über...
Da hatte ich ja doch Recht (mit dem Fehler...). >Aber nimm mal an, ich gestehe dir kein float zu. >Was ist das Problem im ursprünglichen Code? Fixpunktarithmetik dürfte helfen...
Rahul, der Trollige wrote: > Da hatte ich ja doch Recht (mit dem Fehler...). > >>Aber nimm mal an, ich gestehe dir kein float zu. >>Was ist das Problem im ursprünglichen Code? > > Fixpunktarithmetik dürfte helfen... Auf meinem System lautet die Ausgabe, dass der Druchschnitt 138547331 Grad betragen würde. Globale Erwärmung scheidet ebenfalls aus.
Ich seh keinen Fehler, wen man mal von der INT Division sum / count absieht. MFG Falk
Es gibt zwei Möglichkeiten: 1. signed int count; Dann bekommt man 0 Grad. 2. (wie früher bereits geschrieben) "float sum". Karoly
Der Fehler liegt hier sum / count sum ist ein signed count ist ein unsigned. Gemäss dem Promotionsregeln werden signed Datentypen zu unsigned promoted, bevor die Division duchgeführt wird. Da sum aber negativ ist, wird das zu einem riesengrossem unsigned. Dividiert durch count bleibt die Zahl immer noch extrem gross. Das andere Problem ist, dass die Division als Ergebnis einen unsigned Typ hat. %d ist aber nicht das richtige Formatzeichen dafür. Rein formal wäre %u angemessen auch wenn es das Problem nicht lösen würde. mögliche Abhilfen * count als signed ausführen. * count vor der Division auf signed casten sum / (signed)count Dadurch würde allerdings der erhoffte Vorteil des unsigned Datentyps verloren gehen und ein neues Wurmloch eröffnet. Bringt also nicht viel. Moral: unsigned und signed Datentypen mischen kann zu höchst unerwartetem Verhalten führen.
Ich habe auch ein nettes C++-Rätsel, das sich aus persönlicher Erfahrung ergeben hat:
1 | #include <fstream> |
2 | |
3 | int main() |
4 | {
|
5 | std::ofstream("test.txt").flush() << "foobar\n"; |
6 | }
|
Wozu dient der flush() in diesem Programm?
> return (!(c&~3))["(c&3)"]&c&3;
Verrät mir jemand, was es mit dieser ["(c & 3)"]-Syntax auf sich hat?
:-)
Ist ganz einfach. Der Index-Operator a[b] ist äquivalent zu *(a+b). Das beinhaltet auch, daß man a und b vertauschen kann. "abc"[2] == 2["abc"] == 'b'
Hrmpf... natürlich sehe ich es eine Zehntelsekunde nach dem Absenden. Es muß natürlich heißen: "abc"[2] == 2["abc"] == 'c'
Ich schätz mal, dass "foobar\n" direkt in die Datei test.txt geschrieben wird ...
mr.chip wrote: >> return (!(c&~3))["(c&3)"]&c&3; > > Verrät mir jemand, was es mit dieser ["(c & 3)"]-Syntax auf sich hat? > :-) Das ist ziemlich einfach, wenn man es weiss. In C ist Arrayzugriff so definiert: Die [] Zeichen sind 'syntactic sugar'. Sobald der Compiler diese Zeichen sind, wandelt er das ganze um a[b] <==> *( a + b ) und da + kommutativ ist, kann man das auch schreiben als *( a + b ) <==> *( b + a ) und das wiederum ist equivalent zu *( b + a ) <==> b[a] Ob du also int i[10]; j = i[5]; oder int i[10]; j = 5[i]; schreibst, ist in C eine reine Frage der Gewöhnung. Wir sind halt an ersteres gewöhnt. Intern macht der Compiler int i[10]; j = *( i + 5); Da i ein Array ist, reicht die blosse Erwähnung von i aus um daraus einen Zeiger auf das erste Array Element zu machen. In diesem Zusammenhang ist i also ein Pointer-Typ. Wie bei jeder Pointerarithmetik wird der nicht Pointer-Typ (in diesem Fall i) vor der Addition noch mit dem sizeof(*i) multipliziert. D.h. da steht dann irgendwann in der Verarbeitungskette des Compilers. j = *(int)( (unsigned char*)i + 5 * sizeof(*i) ); Und das liefert dann tatsächlich das gewünschte: Beginnend ab der Speicherposition i, 5 Elemente (wobei jedes in diesem Fall ein int gross ist) weiterzählen, und den int von dort holen. Diese a[i] <==> *(a+i) Dualität ist im übrigen wichtig. Sie sorgt dafür, dass Indexoperationen auch auf Pointer angewendet werden können: int * pI = malloc( 3 * sizeof(int) ); ... j = pI[2]; Wieder pI[2] <==> *(pI + 2); Anstatt dem Arraynamen i von vorher, steht jetzt bereits direkt ein Pointer da. Ab da läuft dann alles weiter wie im Arrayfall. Aber im Moment grüble ich noch über dern flush(). Rolf, gibst du einen Tip? Wenn es so formuliert sein würde (std::ofstream("test.txt") << "foobar\n").flush(); hätte es wahrscheinlich irgendwas mit dem Buffering zu tun und dem Umstand, dass da kein std::endl hinausgeschrieben wird. Irgendwas in der Art. Aber anders rum. Ich tappe im Dunkeln.
bei The International Obfuscated C Code Contest gibts mehr von so Zeug: www.ioccc.org Der '07 Contest geht 28. Feb. zu Ende, vielleicht wollt Ihr Euch daran ja beteiligen!? das ist der 'best one liner' von 1994: int _,O,__??('}'??);main(){while(O?gets((rand()%O++?':':_)+__)||puts(&__??(_ ??))&_:srand(time((O+++_)))||O);} Cheers Detlef
Die IOCCC ist nicht schlecht aber von wenig praktischem Nutzen. Wohingegen diese Rätsel hier http://www.gimpel.com/html/bugs.htm tatsächliche praktische Problemkreise aufzeigen. (Und natürlich, wie das beworbene Produkt sie meldet) (Da hab ich auch die signed/unsigned Falle her)
@Karl Heinz Ok, ein Tip: Es hat absolut nichts mit der eigentlichen Bedeutung der Funktion flush() oder irgendwelchen Buffers zu tun. An alle, die nicht ausprobieren wollen, was das Programm mit und ohne Flush in die Datei schreibt: mit flush() schreibt das Programm in die Datei: foobar ohne das flush() (bei mir - der Wert kann verschieden sein): 0x80487c1
Rolf Magnus wrote: > @Karl Heinz > > Ok, ein Tip: Es hat absolut nichts mit der eigentlichen Bedeutung der > Funktion flush() oder irgendwelchen Buffers zu tun. Das hab ich befürchtet. > ohne das flush() (bei mir - der Wert kann verschieden sein): > 0x80487c1 Da ist doch noch ein Hinweis versteckt: Ich lese da 'undefined behaviour' heraus. Also weitersuchen.
Warte mal. Kann es sein, dass du den flush() nur deswegen hast, damit da ein Sequence Point hineinkommt? ... Ich zieh die Frage zurück. Der operator<< müsste eigentlich als Sequence Point ausreichen.
"I'm barking up the wrong tree" Die Frage muss lauten: Was kann den Compiler dazu bewegen, den falschen op<< zu benutzen, sodass anstatt der Bytes die Adresse des Strings geschrieben wird? Und vor allen Dingen, wie kann flush() das beheben?
Mein nächster Gedankengang: Der Unterschied zwischen "mit flush" und "ohne flush" besteht darin, dass das eine mal ein ostream Objekt beteiligt ist, während es im anderen Fall ein ofstream Objekt ist. ofstream erbt ja den flush() von ostream. Hmm. Ich denke dass hat jetzt irgendwas mit dem Function-Lookup zu tun. ofstream hat selbst keinen op<< Es erbt allerdings einen ostream& ostream::operator<< (void*& val ); von ostream. Auf der anderen Seite gibt es einen globalen ostream& operator<< (ostream& out, const char* s ); Ich denke mal, dass der Compiler, solange es sich um ein ofstream Objekt handelt, die globale Funktion nicht benutzt, da es sich ja um ein ofstream Objekt und nicht um ein ostream Objekt handelt. Es kommt als der von ostream inheritated member op<< zum Zug. Sorgst du aber dafür, dass aus dem ofstream ein ostream wird, dann passt der Argumenttyp der globalen Funktion perfekt und der Compiler nimmt den. So mal 'aus dem Ärmel geschüttelt' und etwas konfus niedergeschrieben. Sicher bin ich mir nicht.
Hihi, da bin ich aber stolz, daß ich es geschafft habe, dich mit einer Programmierfrage so aus dem Tritt gebracht zu haben. Du bist schon recht weit gekommen, aber driftest in die falsche Richtung. Du wirst doch nicht vor dem Zielenlauf schlapp machen wollen? ;-)
Rolf Magnus wrote: > Hihi, da bin ich aber stolz, daß ich es geschafft habe, dich mit einer > Programmierfrage so aus dem Tritt gebracht zu haben. streams und vor allem templates sind meine Schwachstelle :-) Man merkt halt doch, dass ich seit einem Jahr kein C++ mehr gemacht habe. > Du bist schon recht weit gekommen, aber driftest in die falsche > Richtung. Mist. > Du wirst doch nicht vor dem Zielenlauf schlapp machen wollen? > ;-) Um ehrlich zu sein, hab ich im Moment keine Idee mehr. Ich denke mal der ofstream-ostream Teil der Überlegung stimmt. Die Sache mit dem ostream& ostream::operator<< (void*& val ); ist natürlich Unsinn, der kanns nicht sein. Ist ja ein Literal, da wird das nichts mit der Referenz. Also: Erzähl mal.
Die ostream/ofstream-Geschichte ist nicht die Lösung. Schreib mal die Operatoren-Aufrufe der Varianten mit und ohne flush() in Funktions-Syntax hin. Dann überleg dir, warum die Variante mit flush() ohne dieses nicht funktionieren kann. Wenn du keine Lust mehr hast, kann ich es auch auflösen, sofern kein anderer auf die Idee kommt. Bisher waren alle anderen doch eher ruhig. Da war das Rätsel wohl doch etwas zu schwierig. ;-)
> Schreib mal die Operatoren-Aufrufe der Varianten mit und ohne flush() > in Funktions-Syntax hin. War ne zeitlang weg. Also dann: (std::ofstream("test.txt").flush()).operator<<( "foobar\n" ); std::ofstream("test.txt").operator<<( "foobar\n" ); Nein, warte. Es gibt keinen member-op<< der einen const char * nimmt. Das ist eine globale Funktionen. operator<<( std::ofstream("test.txt").flush(), "foobar\n" ); operator<<( std::ofstream("test.txt"), "foobar\n" ); Irgendwie hab ich Tomaten auf den Augen. Ich sehs einfach nicht. Reihenfolge der Argumentauswertung ist es nicht, Slicing ist es auch nicht. Jetzt reichts. Ich schmeiss mal den Compiler an und debugge mich durch.
Beide Varianten stimmen nicht. Es muß heißen: std::ofstream("test.txt").operator<<( "foobar\n" ); operator<<( std::ofstream("test.txt").flush(), "foobar\n" ); Ohne flush() wird ja der für void* aufgerufen, und der ist ein Member. Noch ein Tip: Der Stream ist ein temporäres Objekt.
Muss ich jetzt extra nachsehen, wie die Lebensdauer von solchen Temps definiert ist? Dafür spricht auch, dass bei Digital beidesmal das gleiche rauskommt.
Rolf Magnus wrote: > Beide Varianten stimmen nicht. Es muß heißen: > > std::ofstream("test.txt").operator<<( "foobar\n" ); > Ohne flush() wird ja der für void* aufgerufen, und der ist ein Member. Den versteh ich nicht. Warum soll der Compiler die Member- Version nehme? Die Referenz die ich benutzt habe http://www.cplusplus.com/reference/iostream/ostream/operator<<.html listet nur eine Memberversion für einen void*& auf. Die dürfte aber eigentlich (wegen Referenz) nicht genommen werden. > Noch ein Tip: Der Stream ist ein temporäres Objekt. Jetzt hast du mich vollends verwirrt, das temporäre Objekt überlebt doch bis zum abschliessenden Sequence Point.
Das ist ein schönes Beispiel dafür, dass C++ einfach nur kaputt ist. Ich versteh auch nicht, warum sich der Code von Rolf falsch verhält. Und das, obwohl ich früher mal richtig viel in C++ geschrieben habe. Aber es ist auch schon zu lange her, damit ich mir noch den ganzen Müll von C++ merken könnte. Früher dachte ich mal, C++ sei eine Hochsprache. Heute weiß ich, C++ ist eine einzige Katastrophe.
Hmm. Diese Referenz ist unglaubwürdig. Da sind auch überall sonst Referenzen drin. Da bin ich hineingefallen. Für VC6 macht es übrigens auch keinen Unterschied ob mit oder ohne flush(). Will aber nicht viel heissen. VC6 ist alt und bei weitem nicht standardkonform. Ich probiers morgen mal mit dem VC8
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.