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.
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?
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:
> 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.
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.
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