Ich hab' mal den Quelltext ein wenig 'aufbereitet'
- mit gcc -E -trigraphs die Trigraphen aufgelöst
- _ und durch lesbare bezeichner ersetzt
- Zeilenumbrüche eingefügt
- das return foo|foo durch return foo ersetzt
wird. Jetzt muss man nur noch überlegen, was 104 ist ...
[Nachtrag:]
Ich habe natürlich foo und bar miteinander vertauscht, warum sollte
sowas ausgerechnet mir nicht passieren?
Lukas K. schrieb:> Doch ist die Ausgabe bei jedem Aufruf anders, auch beim unmodifizierten> Quelltext.> Mein Compiler ist gcc (GCC) 4.6.2 20111125 (prerelease)
Hab ich auch schon bemerkt gerade, zwischen dem h und allo steht
irgendein Mist, aber mit gcc (mingw32) 4.4.1 gehts.
Mach einfach aus dem char j??(2??) ein char j??(1??), dann ist der Wert
nicht uninitialisiert
Rufus Τ. Firefly schrieb:> Deine Übersetzung ist nicht korrekt.
Der Unterschied will mir nicht recht klar werden...
Das Eliminieren des leeren Blocks {} sollte eigentlich folgenlos
bleiben.
1
(foo | foo) == foo
oder habe ich da was falsch verstanden?
EDIT:
Wenn man die Deklaration von j global macht, ist j[1] auch wie
vorgesehen mit 0 befüllt und das Programm funktioniert.
Hallo Leute,
in jeder Programmiersprache kann man unleserlichen Code schreiben.
Irgendwer bezeichnete C mal als Gürteltiesprache...
Der Quellcode sieht nämlich so aus, als sei ein Gürteltier über die
Tastatur gerollt.
Ich hoffe, Ihr könnt darüber lachen?
Nix für Ungut.
73
Wilhelm
Rufus Τ. Firefly schrieb:> Du hast anscheinend die Trigraph-Sequenz ??! als ? interpretiert, und> nicht als |.
Ich hab keine Trigraphs interpretiert, das hat der gcc für mich gemacht
;)
Das aus dem foo|foo eine foo wurde, war eine von mir vorgenommene
Vereinfachung.
siehe Beitrag "Re: Spass mit C Code"Sven P. schrieb:> Also bei mir macht das Segfaults, oder hab ich da was verpasst?
Mit dem Code aus dem Ursprungspost kann das durchaus passieren, wenn
printf nach der 0 sucht und keine 'früh genug' im Speicher findet.
Mit der Definition von j außerhalb von main hat man dieses Problem nicht
mehr.
Nein das array auf die grösse 1 setzen. Somit ist es unnötig das array
global zu setzen, weil damit der unitialisierte wert wegfällt. Das war
noch auf 2, weil vorher noch zwei zeichen drin sein sollten
mike schrieb:> Nein das array auf die grösse 1 setzen. Somit ist es unnötig das array> global zu setzen, weil damit der unitialisierte wert wegfällt. Das war> noch auf 2, weil vorher noch zwei zeichen drin sein sollten
Du schreibst im äußeren Aufruf von main aber auf jeden Fall hinter das
Ende des Arrays und hast somit einen Buffer Overflow.
Zehn Aufrufe des Programms geben bei mir übrigens folgende zehn Texte
aus:
h��allo
h�allo
hߜallo
h�allo
h�zallo
h��allo
hkallo
h_�allo
hOIallo
h��allo
Und wenn man dann noch beim Start Kommandozeilenparameter übergibt,
wird's bizarr.
Vielleicht sollte man erst C lernen, bevor man mit obfuscated
weitermacht.
Dann weiß man nämlich auch, was man schreiben muß, damit es nicht nur
zufällig funktioniert.
Nicht jedes verkorkste Programm ist automatisch interessanter obfuscated
code.
Rolf Magnus schrieb:> Mit oder ohne eingeschaltete Optimierungen?
Gute Frage. Habs grad nochmal ausprobiert und ich erhalte nur ohne
Optimierungen oder Optimierungen vom Grad 1 und 2 die richtige Ausgabe.
Ab 3 wirds zufällig.
Mike Mike schrieb:> Vielleicht sollte man erstmal lernen im Leben nicht alles so Tod ernst> zu nehmen und jeden runterzustufen ;)
So ist das auch nicht gemeint gewesen, aber unleserlichen Code, der nur
mit Glück funktioniert, kann jeder schreiben. Das ist ja quasi das
erste, was man lernt ;-). Klaus Wachtler hat's gut formuliert:
> Nicht jedes verkorkste Programm ist automatisch interessanter obfuscated> code.Samuel K. schrieb:> int a,b,j,i;main(){j=++j*++j;i=j+j;b=j=i|i<<--j;a=i+i<<i/i|i^++b;j|=a<<i;pri
ntf(&j);j%=++i;b=i<<++j^a|a^b+a/a;a=b^i/++j;b=b<<--i<<i|a<<i|a;printf(&b );}
Das Programm wiederum funktioniert bei mir nur mit eingeschalteten
Optimierungen, was ungewöhnlich ist. Aber ich glaub nicht, daß das auf
meiner Dockstar auch tut ;-)
Bei mir tut es in DevC++ auch ohne Optimierung. Seltsam, dass es ohne
bei manchen nicht funktioniert.
Um den "Fehler" zu finden, könnte die Zwischenergebnisse des Codes
vergleichen.
- wieso soll j am Anfang 4 sein?
- wo kommen für die printf die abschließenden Nullen her? Nur weil eine
int immer mindestens vier Byte hat? Irgendwo in diesem Forum wurden auch
schon mal andere Größen für int genannt.
- Dank der genauen Vorgaben seit K&R ist bestimmt jeder Rechner little
endian :-)
Samuel K. schrieb:> Bei mir tut es in DevC++ auch ohne Optimierung. Seltsam, dass es ohne> bei manchen nicht funktioniert.
Naja, das Programm enthält Code, der undefiniertes Verhalten hervorruft.
Allerdings tendiert so ein Programm meist eher bei eingeschalteten
Optimierungen zu Fehlverhalten.
> Um den "Fehler" zu finden, könnte die Zwischenergebnisse des Codes> vergleichen.int a,b,j,i;> main()> {> j=++j*++j; //j=4
Undefiniertes Verhalten. Du veränderst j mehr als einmal ohne
dazwischenliegenden Sequenzpunkt.
Bei mir ist es an dieser Stelle ohne Optimierungen 2, mit Optimierungen
4.
Daß du printf ohne vorherige Deklaration nutzt, ihm dann noch den
falschen Zeigertyp übergibst und Annahmen darüber machst, wie groß ein
int ist und in welcher Reihenfolge die Bytes da drin liegen, kommt noch
dazu.
Klaus Wachtler schrieb:> - wieso soll j am Anfang 4 sein?
Weil für die übernächste Zuweisung eine 3 = --j gebraucht wird.
> - wo kommen für die printf die abschließenden Nullen her? Nur weil eine> int immer mindestens vier Byte hat? Irgendwo in diesem Forum wurden auch> schon mal andere Größen für int genannt.
Ich denke nicht, dass jemand den Code für eine <= 16bit Maschine
kompilieren wird. Man kann es auch durch long ersetzen.
> - Dank der genauen Vorgaben seit K&R ist bestimmt jeder Rechner little> endian :-)
Es könnte ein Problem mit dem Lesen des &int Pointers geben, das habe
ich einfacherhalber ignoriert.
Rolf Magnus schrieb:> Undefiniertes Verhalten.
Ich finde es definiert: Nach der Operatorenpriorität ist ++ höherwertig
als *. j muss erst 2 mal inkrementiert und erst dann darf multipliziert
werden. Oder habe ich jetzt einen Denkfehler?
Den Zeiger kann man auch casten, aber das macht nur einen Unterschied
für den Compiler. Zeiger selbst sind alle gleich, egal auf welches
Objekt sie zeigen.
Samuel K. schrieb:> Klaus Wachtler schrieb:>> - wieso soll j am Anfang 4 sein?> Weil für die übernächste Zuweisung eine 3 = --j gebraucht wird.
Ich wollte nicht darauf hinaus, warum DU die 4 haben willst, sondern
warum sich die 4 ergeben soll.
Schließlich steht j anfangs auf 0 (weil global, ok - nicht bei AVR).
Es steht dem Compiler durchaus frei, mit dem ersten ++ um 1 zu erhöhen,
ergibt also 1 für den Term links vom Sternchen, dann och einmal zu
erhöhen - das gibt 2 für den Term rechts. 1*2 macht 2, das wird
zugewiesen.
Wenn bei dir 4 herauskommt, ist das nett für dich, aber beileibe nicht
verlässlich.
Samuel K. schrieb:>> - Dank der genauen Vorgaben seit K&R ist bestimmt jeder Rechner little>> endian :-)> Es könnte ein Problem mit dem Lesen des &int Pointers geben, das habe> ich einfacherhalber ignoriert.
Auch ignoriert hast du, daß bei eine BE-Maschine die ASCII-Werte dann
bei höheren Adressen im Speicher stehen und die abschließende 0 unten.
Da wird printf nicht mehr viel retten können.
Daß du dich auf ASCII verlässt, meckere ich gar nicht an, viele Systeme
gibt es wirklich nicht mehr ohne.
Samuel K. schrieb:> Zeiger selbst sind alle gleich, egal auf welches> Objekt sie zeigen.
Das ist eine gewagte Aussage.
In dieser Richtung wird es tatsächlich in aller Regel gut gehen (int*
als char* interpretieren), aber andersrum geht es schnell schief, wenn
ein char an einer ungeraden Adresse liegt und seine Adresse als int*
verwendet wird, was je nach System wahrscheinlich auf einer durch 2 oder
4 teilbaren Adresse liegen muß.
Samuel K. schrieb:>> - wo kommen für die printf die abschließenden Nullen her? Nur weil eine>> int immer mindestens vier Byte hat? Irgendwo in diesem Forum wurden auch>> schon mal andere Größen für int genannt.> Ich denke nicht, dass jemand den Code für eine <= 16bit Maschine> kompilieren wird. Man kann es auch durch long ersetzen.
Ich hatte schon überlegt, es mal spaßeshalber für AVR zu compilieren und
zu schauen, was da rauskommt.
>> - Dank der genauen Vorgaben seit K&R ist bestimmt jeder Rechner little>> endian :-)> Es könnte ein Problem mit dem Lesen des &int Pointers geben, das habe> ich einfacherhalber ignoriert.
Es gibt vor allem ein Problem mit der Reihenfolge der einzelnen Zeichen,
wenn du das auf einem big-endian-System ausführst.
> Rolf Magnus schrieb:>> Undefiniertes Verhalten.> Ich finde es definiert:
ISO C++ sagt ganz klar, daß das Verhalten undefiniert ist, wenn man eine
Variable zweimal beschreibt oder beschreibt und ausliest, ohne daß
dazwischen ein Sequenzpunkt liegt, und ++j*++j enthält keinen
Sequenzpunkt. Da ist es eigentlich egal, was du findest. ;-)
> Nach der Operatorenpriorität ist ++ höherwertig als *. j muss erst 2 mal> inkrementiert und erst dann darf multipliziert werden.
Das ginge auch von links nach rechts: Für die Multiplikation wird erst
der linke Operand ermittelt, also wird ++j ausgeführt, also ist der
linke Operand 1. Dann wird rechts das selbe gemacht, und der Operand ist
2. Dann kommt die Multiplikation. Ergebnis ist dann 2. Theoretisch
dürfte der Compiler aber sogar die beiden ++j parallelisieren, sofern
die Hardware sowas kann. Dann kommt halt irgendwas raus, das bis zu
einer "Trap" gehen kann, also dem Abbruch der Operation wegen Fehler in
der CPU. Das wäre alles nach ISO-C erlaubt. Ich kann dir jetzt keine
konkrete Architektur nennen, auf der das so ist, aber das bedeutet
nicht, daß es keine gibt.
> Den Zeiger kann man auch casten, aber das macht nur einen Unterschied> für den Compiler. Zeiger selbst sind alle gleich, egal auf welches> Objekt sie zeigen.
Allerdings gibt es Compiler, bei denen sich die Art der
Parameterübergabe zwischen normalen Funktionen und solchen mit variablen
Argumentlisten unterscheidet. Da printf vorher nicht deklariert war,
nimmt der Compiler an, daß die Funktion keine variable Argumentliste
hat.
Samuel K. schrieb:> Zeiger selbst sind alle gleich, egal auf welches> Objekt sie zeigen.
Das ist in C sogar sehr gewagt.
In C++ ist es sogar schlicht falsch, wenn es um Zeiger auf polymorphe
Klassen geht.
C ist schon eine spaßige Sprache, bei der ein und derselbe Quelltext
auf verschiedenen Rechnern und mit verschiedenen Kompilern immer neue
polymorphe Ergebnisse liefert.
Zitat Mr. Spock
"FASZINIEREND!"
;-)
MfG Paul
ich dachte, bei AVR mit der gcc-Toolchain würden globale Variablen nicht
zwangsläufig mit 0 initialisiert? Da lag ich wohl falsch, es scheint
auch hier alles Globale initialisiert zu werden.
Hm, man wird alt...
Paul Baumann schrieb:> C ist schon eine spaßige Sprache, bei der ein und derselbe Quelltext> auf verschiedenen Rechnern und mit verschiedenen Kompilern immer neue> polymorphe Ergebnisse liefert.
Ja, eine spaßige Sprache für lichtscheue Typen, denen vollkommen egal
ist, ob das Kompilat das macht, was es soll. Aber aufgrund genau des
beschriebenen Verhaltens disqualifiziert sich C aus dem Bereich
ernstzunehmender Programmiersprachen.
walther schrieb:> Aber aufgrund genau des> beschriebenen Verhaltens disqualifiziert sich C aus dem Bereich> ernstzunehmender Programmiersprachen.
Das ist quark. Wenn man lauter Programmierfehler aneinanderreiht, ist
nicht die Sprache disqualifiziert.
Rufus Τ. Firefly schrieb:> Das ist quark. Wenn man lauter Programmierfehler aneinanderreiht, ist> nicht die Sprache disqualifiziert.
Es scheint sich aber offensichtlich nicht um "Programmierfehler" zu
handeln, da die verschiedenen Kompiler den Mist ja übersetzen! Ich kenne
keine weitere Sprache (von noch blödsinnigeren Experimentalkrams wie
z.B. "Brainf_ck" einmal abgesehen) wo die Syntax so un-eindeutig und
fehlerträchtig ist, wie bei C. Sobald die Leute anfangen ihren Quellkode
zu "optimieren" oder "Tricks zu verwenden" kommt meist nur unwartbarer
Blödsinn bei heraus, der spätestens dann neu geschrieben werden muß,
wenn der "Programmierer", welcher das verzapft hat, das Unternehmen
verlassen hat oder vor versammelter Mannschaft eingestehen muß, das er
den Sch..ß selbst nicht mehr pflegen kann... Für ernsthafte Projekt z.B.
im Bereich Sicherheits- oder Medizintechnik ist dieses C-Gebrabbel
vollkommen ungeeignet. Oder möchtest Du mit einem Herzschrittmacher
durch die Gegend latschen, der je nach Kompiler nach kurzer Zeit
aufgrund eines Integer-Überlaufs den Dienst einstellt?
Viel Spaß & frohe Festtage,
Walther
walther schrieb:> Für ernsthafte Projekt z.B.> im Bereich Sicherheits- oder Medizintechnik ist dieses C-Gebrabbel> vollkommen ungeeignet. Oder möchtest Du mit einem Herzschrittmacher> durch die Gegend latschen, der je nach Kompiler nach kurzer Zeit> aufgrund eines Integer-Überlaufs den Dienst einstellt?
Da hab ich aber schon deutlich mehr Projekte an solch durchdachten
Sprachen wie etwa Visual Basic, LabVIEW und FoxPro scheitern gesehen.
Du wirst überdies erstaunt sein, womit sicherheitskritische Systeme
programmiert werden, zum Beispiel im Automotive und in der Industrie.
Und nein, es ist nicht Basic und auch nicht C#.
walther schrieb:> Sobald die Leute anfangen ihren Quellkode> zu "optimieren" oder "Tricks zu verwenden" kommt meist nur unwartbarer> Blödsinn bei heraus
Um genau das zu verhindern, gibt es Kodierrichtlinien, Tools zur
statischen Codeanalyse und Code Reviews. Wenn eine Firma dumm genug ist,
jeden nach seiner Façon programmieren zu lassen, dann wird das Ergebnis
auch entsprechend sein - daran ist aber nicht die Programmiersprache
schuld.