C-Puzzle: Ausgabe von printf sollte immer ‘5’ sein
Lesezeit: 7 Minuten
Sync-Master
Ich habe dieses Rätsel in einer Kapitulationsarbeit gefunden.
void change()
{
//write something in this function so that output of printf in main function
//should always give 5.you can't change the main function
}
int main()
{
int i = 5;
change();
i = 10;
printf("%d", i);
return 0;
}
Irgendwelche Lösungen.?
gähn… solche Sachen waren schon vor 20 Jahren Thema im Obfuscated C Contest.
– Thorsten79
2. Februar 2010 um 12:39 Uhr
Rätsel wie dieses sind sinnvoller, um zu testen, ob Leute einen Vermissten entdecken \n am Ende der Ausgabe oder eine fehlende Deklaration für printf Funktion.
– AnT steht zu Russland
2. Februar 2010 um 21:18 Uhr
+1 Trotz anfänglicher Skepsis gibt es hier einige wirklich hervorragende Antworten. Zweifler sollten sich die POSIX-kompatible Version ohne Makros ansehen.
– Chris Lutz
4. Februar 2010 um 6:20 Uhr
definieren?
#include <stdio.h>
void change()
{
//write something in this function so that output of printf in main function
//should always give 5.you can't change the main function
#define printf_ printf
#define printf(a, b) printf_("5");
}
int main()
{
int i = 5;
change();
i = 10;
printf("%d", i);
return 0;
}
Habe dieses Problem schon einmal gesehen und das war die Lösung.
– Gregor
2. Februar 2010 um 5:48 Uhr
Was zum Teufel ist die Moral dieser Geschichte? was soll das lehren?? Ich wusste nicht, dass sie Kurse darüber machen, wie man schrecklich unüberschaubaren und gehackten Code schreibt … (obwohl ich nach der Menge, die ich gesehen habe, nicht überrascht wäre, wenn sie es tun würden …)
– matt
2. Februar 2010 um 7:27 Uhr
@RC: Ich kann nicht wirklich glauben, dass dies die wirkliche Lösung ist. Was wäre der Sinn der change() Funktion? Wenn dies die erwartete Antwort ist, würde die Frage oben nicht einfach “Schreiben Sie hier etwas” sagen main() ohne sich um die Funktion zu kümmern?
– GrahamS
2. Februar 2010 um 12:20 Uhr
Eine andere Lösung mit nur einem Makro ist #define printf(a, b) (printf)("5") wodurch rekursive Makroerweiterungsprobleme vermieden werden (mein Compiler beschwert sich gerne, wenn Leute rekursive Makros haben, was wahrscheinlich gut ist). Obwohl ich die bevorzuge puts() Version, da sie zur besseren Lesbarkeit einen Zeilenumbruch hinzufügt.
– Chris Lutz
4. Februar 2010 um 5:49 Uhr
Zitat des Präprozessors: „Missbrauch mich! Missbrauche mich!
– Tim Post
28. März 2010 um 18:11 Uhr
Dies ist eine POSIX-Antwort, die wirklich das tut, was das Problem verlangt 🙂
Es funktioniert nicht auf einigen Architekturen/Compilern, aber hier.
BEARBEITEN: Dies sollte es für Leute mit Boykott-Headern robuster machen.
+1 erstaunlich. Ich musste die verschieben #includes nach oben, um es zum Kompilieren zu bringen, aber es funktioniert wie ein Zauber, und diese Technik ist ziemlich brutal. Sie gewinnen mindestens 3 Internets.
– Chris Lutz
4. Februar 2010 um 6:02 Uhr
Ich persönlich würde gerne faktorisieren sysconf(_SC_PAGESIZE) in eine Variable, anstatt aufzurufen sysconf 5 mal. 😛
– C. K. Young
4. Februar 2010 um 6:04 Uhr
@jbcreix – Ich mag deine Indizierung. Ich benutze gcc -Wall -Wextra -Werror standardmäßig. Ich wette, es würde gut kompilieren, wenn ich die Warnungen herunterdrehen würde. Ich vergesse sie einfach, weil sie meistens so nett sind. Update: Nein, auch ohne Warnungen muss ich einschalten -fnested-functions, und dann spuckt es noch eine Warnung aus. Ich gebe OS X die Schuld.
– Chris Lutz
4. Februar 2010 um 6:17 Uhr
@SiegeX – Die Funktion _change schaut in den Maschinencode der main Funktion, macht es beschreibbar, sucht nach einer 10 und ändert die 10 in eine 5. Also, when _change kehrt zurück zu change und change kehrt zurück zu maindie nächste Zeile, i = 10; wird geändert worden sein i = 5; im Maschinencode, der ausgeführt wird. Es ist selbstmodifizierender Code.
– Chris Lutz
12. Januar 2011 um 1:00 Uhr
@SiegeX – C++ verbietet das Aufrufen mainaber in C aufrufen main von einer anderen Funktion ist vollkommen legal. Und ja, der Zeiger zeigt auf das Codesegment. Es ist ein Funktionszeiger, der in einen regulären Zeiger umgewandelt wird (der UB ist, aber in der Praxis auf POSIX-Systemen funktioniert, von denen wir sowieso bereits ausgehen).
– Chris Lutz
12. Januar 2011 um 2:03 Uhr
void change()
{
//write something in this function so that output of printf in main function
//should always give 5.you can't change the main function
#define i a=5,b
}
-1, Das würde nicht funktionieren. printf("%d", i) würde durch den Präprozessor ersetzt werden printf("%d", a=5,) was nicht kompilieren würde.
– GrahamS
2. Februar 2010 um 11:59 Uhr
Nun, es wird durch ersetzt printf("%d", a=5,b). Kompiliert das? Ich bin mir nicht sicher, wie printf funktioniert
– Chris Burt-Brown
2. Februar 2010 um 12:24 Uhr
@Graham, @Chris: Ja, das funktioniert. Zusätzliche Argumente zu printf werden ignoriert.
– C. K. Young
2. Februar 2010 um 14:54 Uhr
@GrahamS es funktioniert. printf("%d", i) wird ersetzt durch printf("%d", a=5,b) (Ich bin mir nicht sicher, warum Sie dachten, das b würde entfallen). a=5 ist ein Ausdruck, der zu 5 ausgewertet wird b übergeben wird printf auch (printf verwendet varargs), aber printf ignoriert es, weil es nur einen Formatierungscode gibt (die %d). Einige Compiler warnen möglicherweise, dass Sie ein zusätzliches Argument an übergeben printfaber es ist immer noch gültiger Standard C, um dies zu tun.
– Laurence Gonsalves
3. Februar 2010 um 1:21 Uhr
@ Laurence Ja, ich stehe korrigiert. Ich bin mir nicht sicher, warum ich das ohne gelesen habe b (es war spät), aber printf scheint tatsächlich mit den zusätzlichen Argumenten umzugehen.
Dies ist viel portabler als das Ändern der Rücksendeadresse, erfordert jedoch, dass Sie (a) einen Compiler haben, der Zeichenfolgenliterale zusammenfasst (die meisten tun dies), und (b) einen Compiler haben, der keine Konstanten in einem schreibgeschützten Abschnitt platziert. oder auf einer Architektur ohne MMU ausgeführt werden (heutzutage unwahrscheinlich).
Leider bekomme ich unter OS X einen Bus-Fehler, bevor irgendetwas gedruckt wird, also habe ich keine Ahnung, ob das funktioniert hat. Aber es ist eine hervorragende Lösung.
– Chris Lutz
4. Februar 2010 um 6:13 Uhr
Verdammt! Das wollte ich gerade posten!
– MSN
5. Februar 2010 um 1:01 Uhr
Sie müssten mit bauen -fwritable-strings damit das funktioniert. Ich mag das!
– Donal Fellows
28. März 2010 um 11:01 Uhr
Ist das wirklich legal C oder nur das, was Compiler akzeptieren? Ich dachte, es gäbe eine Standardregel, Konstanten nicht durch Zeiger zu ändern.
– Ira Baxter
20. Januar 2011 um 21:49 Uhr
Das C ist vollkommen legal und wird überall kompiliert. Ob es zur Laufzeit tatsächlich funktioniert, ist unterschiedlich – der Compiler bündelt möglicherweise keine Literale (in diesem Fall wirkt sich die Änderung nicht auf den aufrufenden Code aus), oder die Literale werden möglicherweise in einem Speicherbereich platziert, in den die MMU-Blöcke schreiben (in dem Fall gibt es eine Ausnahme, wenn der Code ausgeführt wird).
– Mondschatten
21. Januar 2011 um 10:49 Uhr
Hat jemand daran gedacht, atexit zu verwenden?
void change (void)
{
static int i = 0;
if (i == 0) atexit (change);
if (i == 1) printf ("\r5 \b\n");
++i;
}
Beachten Sie, dass es in der Hauptfunktion keinen abschließenden Zeilenumbruch gibt. Wenn wir 2 Backspace-Zeichen an stdout senden, wird die 10 gelöscht und nur die 5 gedruckt.
Leider bekomme ich unter OS X einen Bus-Fehler, bevor irgendetwas gedruckt wird, also habe ich keine Ahnung, ob das funktioniert hat. Aber es ist eine hervorragende Lösung.
– Chris Lutz
4. Februar 2010 um 6:13 Uhr
Verdammt! Das wollte ich gerade posten!
– MSN
5. Februar 2010 um 1:01 Uhr
Sie müssten mit bauen -fwritable-strings damit das funktioniert. Ich mag das!
– Donal Fellows
28. März 2010 um 11:01 Uhr
Ist das wirklich legal C oder nur das, was Compiler akzeptieren? Ich dachte, es gäbe eine Standardregel, Konstanten nicht durch Zeiger zu ändern.
– Ira Baxter
20. Januar 2011 um 21:49 Uhr
Das C ist vollkommen legal und wird überall kompiliert. Ob es zur Laufzeit tatsächlich funktioniert, ist unterschiedlich – der Compiler bündelt möglicherweise keine Literale (in diesem Fall wirkt sich die Änderung nicht auf den aufrufenden Code aus), oder die Literale werden möglicherweise in einem Speicherbereich platziert, in den die MMU-Blöcke schreiben (in dem Fall gibt es eine Ausnahme, wenn der Code ausgeführt wird).
– Mondschatten
21. Januar 2011 um 10:49 Uhr
Rufen Sie das erforderliche #include auf und ersetzen Sie den Kommentar durch den Text ohne Klammern:
}
int printf(const char *s, ...) {
return fprintf(stdout,"%d",5);
Erfolgreich getestet. Danke an dreamlax und Chris Lutz für Bugfixes.
printf kehrt zurück inthaben Sie es so definiert, dass es keinen Rückgabewert hat.
– Traumlax
2. Februar 2010 um 10:35 Uhr
printf gibt ein zurück int aber Ihre Implementierung gibt keinen Wert zurück. return fprintf ist in ordnung, denke ich. Aber +1 für extreme Klugheit.
– Chris Lutz
4. Februar 2010 um 5:59 Uhr
11908000cookie-checkC-Puzzle: Ausgabe von printf sollte immer ‘5’ seinyes
gähn… solche Sachen waren schon vor 20 Jahren Thema im Obfuscated C Contest.
– Thorsten79
2. Februar 2010 um 12:39 Uhr
Rätsel wie dieses sind sinnvoller, um zu testen, ob Leute einen Vermissten entdecken
\n
am Ende der Ausgabe oder eine fehlende Deklaration fürprintf
Funktion.– AnT steht zu Russland
2. Februar 2010 um 21:18 Uhr
+1 Trotz anfänglicher Skepsis gibt es hier einige wirklich hervorragende Antworten. Zweifler sollten sich die POSIX-kompatible Version ohne Makros ansehen.
– Chris Lutz
4. Februar 2010 um 6:20 Uhr