Welche allgemeinen Tipps haben Sie zum Golfen in C? Ich bin auf der Suche nach Ideen, die sich auf Code-Golf-Probleme im Allgemeinen anwenden lassen, die zumindest etwas spezifisch für C sind (z. B. "Kommentare entfernen" ist keine Antwort). Bitte posten Sie einen Tipp pro Antwort. Bitte geben Sie auch an, ob Ihr Tipp für C89 und / oder C99 gilt und ob er nur auf bestimmten Compilern funktioniert.
137
Antworten:
Verwenden Sie bitweises XOR, um die Ungleichheit zwischen Ganzzahlen zu überprüfen:
if(a^b)
anstelle vonif(a!=b)
1 Zeichen speichert.quelle
a-b
gibt Ihnen den gleichen Effekt.a*b
anstelle von verwendena&&b
(hat unterschiedliche Priorität, kann oder kann nicht schlecht sein). Wenn Sie ein / = -b kennen (z. B. ohne Vorzeichen), danna||b
==a+b
?:
(anstelle von if): zum Beispiel, um nur etwas zu tun, wenn es anders ist:a^b?_diff_:;
?:
Operator, der genau gleichbedeutend ist mita ? a : b
main
Die Argumentliste von Missbrauch , um eine oder mehrere Ganzzahlvariablen zu deklarieren:(Antwort auf Das Alphabet in Programmiersprachen )
Diese Lösung missbraucht auch die Tatsache, dass
a
(akaargc
) als beginnt1
, vorausgesetzt, das Programm wird ohne Argumente aufgerufen.Verwenden Sie globale Variablen, um Dinge auf Null zu initialisieren:
(Antwort auf Anagram Code Golf! )
quelle
Der Komma-Operator kann verwendet werden, um mehrere Ausdrücke in einem einzigen Block auszuführen und dabei geschweifte Klammern zu vermeiden:
Ausgänge:
1 2
quelle
break
.break
ist eine Aussage, und diese Antwort spricht über Ausdrücke.Vermeiden Sie katastrophale Typdeklarationen für Funktionsargumente
Wenn Sie eine Funktion deklarieren, in der alle fünf Argumente
int
s sind, ist das Leben gut. du kannst einfach schreibenAngenommen
d
, es muss einechar
oder sogar eine seinint*
. Dann bist du fertig! Wenn einem Parameter ein Typ vorangestellt ist, müssen alle sein:Aber warte! Es gibt einen Ausweg aus dieser katastrophalen Explosion nutzloser Charaktere. Es geht so:
Dies spart sogar eine Standarddeklaration
main
, wenn Sie die Befehlszeilenargumente verwenden müssen:ist zwei Bytes kürzer als
Ich war überrascht, dies zu entdecken, da ich es auf PPCG bisher nicht angetroffen habe.
quelle
-std=gnu99
nicht zulässig. Sie müssen es also verwenden und sind jetzt nicht mehr portabel. Im Klartext schreiben Sie nicht einmal "C" -Code an sich, sondern "Gnu99-C". „Hier ignorieren wir das meistens, aber es ist gut, es zu erwähnen, wenn Sie einen Code veröffentlichen, der compilerspezifisch ist. Manchmal tatsächlich Menschen tun herunterladen und diese Programme von uns ausführen. :)-std=c89
gcc oder clang anweisen , Ihren Code gemäß dem älteren Standard zu kompilieren, der implizite Int mit nur einer Warnung zulässt.Anstelle von> = und <= können Sie einfach die Ganzzahldivision (/) verwenden, wenn die verglichenen Werte über Null liegen, wodurch ein Zeichen gespeichert wird. Zum Beispiel:
Was natürlich immer noch schrumpfbar ist, wenn man zum Beispiel nur> und ^ verwendet (eine clevere Möglichkeit, um in einigen Fällen das Schreiben von && oder || zu vermeiden).
Der Trick der Ganzzahldivision ist beispielsweise nützlich, um zu entscheiden, ob eine Zahl kleiner als 100 ist, da hierdurch ein Zeichen gespeichert wird:
Dies ist auch in Fällen sinnvoll, in denen eine höhere Priorität erforderlich ist.
quelle
putchar(c>31&c<127?c:46);
Bestimmte Compiler, wie z. B. GCC, ermöglichen es Ihnen, grundlegende
#include
s-, param- und return-Typen für wegzulassenmain
.Das Folgende ist ein gültiges C89- und C99-Programm, das (mit Warnungen) mit GCC kompiliert:
Beachten Sie, dass
#include
für stdio.h fehlt, der Rückgabetyp fürmain
fehlt und die Typdeklaration füri
fehlt.quelle
printf()
(oder eine andere Funktion) ohne Prototyp führt zu undefiniertem Verhalten . Standardmäßig kompiliert GCC Standard C nicht. Wenn Sie gcc im C89-Modus (gcc -ansi -pedantic
) oder C99-Modus (gcc -std=c99 -pedantic
) aufrufen , werden Sie zumindest im letzteren Fall einige Beschwerden bekommen.Der ternäre Bedingungsoperator
?:
kann oft als in für einfache Stand verwendet werdenif
-else
Aussagen zu erheblichen Einsparungen.Im Gegensatz zum c ++ - Äquivalent gibt der Operator formal keinen lvalue aus , aber einige Compiler (insbesondere gcc) lassen Sie damit davonkommen, was ein netter Bonus ist.
quelle
&&
und||
kann auch verwendet werden:if(x==3)f()
wird mit Ihrem Vorschlagx==3?f():0
und kann weiter verbessert werdenx==3&&f()
. Seien Sie jedoch vorsichtig, wenn der Operator Vorrang hat. Wennf()
durch ersetzt wirdy=1
, ist für die&&
Lösung ein zusätzlicher Satz Klammern erforderlich.?:
gedacht , dass der gcc einen Wert ergibt. Kann ich das im Produktionscode verwenden? lolx==3&&f()
kann bis zumx^3||f()
http://graphics.stanford.edu/~seander/bithacks.html
Bits sind nett.
Aber mit unterschiedlichen Prioritäten, und ändern Sie nicht x wie ++ und -. Sie können dies auch in ganz bestimmten Fällen verwenden: ~ 9 ist kürzer als -10.
Das ist esoterischer, aber ich hatte Gelegenheit, es zu benutzen. Wenn Ihnen das Kurzschließen egal ist
Ebenfalls:
quelle
(x/y) == (x>=y)
) ist wirklich nützlich.Verwenden Sie Lambdas (nicht portierbar)
Anstatt von
oder (nur gcc)
oder (llvm with blocks support)
versuchen Sie etwas wie
... wobei die angeführte Zeichenfolge die maschinensprachlichen Anweisungen Ihrer "Lambda" -Funktion enthält (gemäß allen Anforderungen der Plattform ABI).
Dies funktioniert in Umgebungen, in denen Zeichenfolgenkonstanten als ausführbar markiert sind. Standardmäßig gilt dies für Linux und OSX, nicht jedoch für Windows.
Eine dumme Methode, um zu lernen, wie man eigene "Lambda" -Funktionen schreibt, besteht darin, die Funktion in C zu schreiben, zu kompilieren, sie mit etwas ähnlichem zu untersuchen
objdump -D
und den entsprechenden Hex-Code in eine Zeichenfolge zu kopieren. Zum Beispiel,... wenn es
gcc -Os -c
für ein Linux x86_64-Ziel kompiliert wird, erzeugt es so etwas wieGNU CC
goto
:Sie können diese "Lambda-Funktionen" direkt aufrufen, aber wenn der aufgerufene Code keine Parameter annimmt und nicht zurückgibt, können Sie
goto
ein paar Bytes sparen. Also stattoder (wenn Ihre Umgebung keine arabischen Glyphen enthält)
Versuchen
oder
In diesem Beispiel
eb fe
ist x86 Maschinensprache für so etwas wiefor(;;);
und ein einfaches Beispiel für etwas, das keine Parameter akzeptiert und nicht zurückkehren wird :-)Es hat sich herausgestellt, dass Sie
goto
Code verwenden können, der an einen anrufenden Elternteil zurückgegeben wird.Das obige Beispiel (kann unter Linux kompiliert und ausgeführt werden
gcc -O
) ist abhängig vom Stack-Layout.BEARBEITEN: Abhängig von Ihrer Toolchain müssen Sie möglicherweise das
-zexecstack
Kompilierungsflag verwenden.Wenn es nicht sofort ersichtlich ist, wurde diese Antwort hauptsächlich für die Lols geschrieben. Ich übernehme keine Verantwortung für besseres oder schlechteres Golfen oder negative psychologische Ergebnisse, wenn ich dies lese.
quelle
Verwenden Sie Cursor anstelle von Zeigern. Haken Sie das
brk()
am Anfang und verwenden Sie es als Basiszeiger .Legen Sie dann ein #define für den Speicherzugriff fest.
M
wird zu einem Postfix*
, der auf ganze Zahlen angewendet wird. (Der alte a [x] == x [a] Trick.)Aber es gibt noch mehr! In Funktionen, die kürzer als Makros sind, können Sie Zeigerargumente und -rückgaben verwenden (insbesondere, wenn Sie 'return' abkürzen):
Um aus einem Zeiger einen Cursor zu machen, subtrahieren Sie den Basiszeiger, und Sie erhalten ein ptrdiff_t, das in ein int abschneidet. Verluste sind Ihr Geschäft.
Diese Technik wird in meiner Antwort verwendet, um einen Interpreter für den untypisierten Lambda-Kalkül zu schreiben .
quelle
Definieren Sie Parameter anstelle von Variablen.
f(x){int y=x+1;...}
f(x,y){y=x+1;...}
Sie müssen den zweiten Parameter nicht wirklich übergeben.
Sie können auch die Operatorrangfolge verwenden, um Klammern zu speichern.
Zum Beispiel
(x+y)*2
kann werdenx+y<<1
.quelle
x+y*2
, um noch einen Char zu retten.x+y*2
ist aufgrund der Rangfolge des Operators nicht dasselbe.x+y<<1
Beispiel fixiert, nahm an , dass es als ausgewertet wurdex+(y<<1)
, und schlug*2
stattdessen das vor. Ich wusste nicht, dass Bitverschiebungsoperationen als zB(x+y)<<2
EOF == -1
Verwenden Sie normalerweise den bitweisen Operator NOT, um nach EOF zu suchen:while(~(c=getchar()))
oderwhile(c=getchar()+1)
und ändern Sie den Wert von c an jeder Stellequelle
while(1+c=getchar())
funktionieren?+
hat eine höhere Priorität als der Zuweisungsoperator=
,1+c=getchar()
ist also äquivalent zu(1+c)=getchar()
, was nicht kompiliert(1+c)
wird , da es sich nicht um einen l-Wert handelt.Der ternäre Operator
?:
ist insofern ungewöhnlich, als er zwei separate Teile hat. Aus diesem Grund bietet es eine kleine Lücke zu den Standardregeln für die Rangfolge von Operatoren. Dies kann hilfreich sein, um Klammern zu vermeiden.Nehmen Sie das folgende Beispiel:
Die übliche Vorgehensweise beim Golfen ist das Ersetzen
if
durch&&
. Aufgrund der geringen Priorität des Komma-Operators benötigen Sie jedoch ein zusätzliches Klammerpaar:Der mittlere Bereich des ternären Operators benötigt jedoch keine Klammern:
Ähnliche Kommentare gelten für Array-Indizes.
quelle
b-=a=b
ist noch kürzer. Der?:
Trick ist immer noch hilfreich,-=
weil er auch wenig Vorliebe hat.x>0||(y=3)
,x>0?0:(y=3)
ist nutzlos,x<1?y=3:0
erledigt aber den Job.x>5?:y=1
Jeder Teil Ihres Codes, der mehrmals wiederholt wird, kann durch den Vorprozessor ersetzt werden.
Dies ist ein sehr häufiger Anwendungsfall, wenn Sie Code mit mehreren Funktionen verwenden. Andere longish Schlüsselwörter wie
while
,double
,switch
undcase
sind auch Kandidaten; sowie alles, was in Ihrem Code idomatisch ist.Ich reserviere in der Regel Großbuchstaben für diesen Zweck.
quelle
-DR=return
. Beachten Sie, dass bei der Angabe bestimmter Zeichen möglicherweise einfache oder doppelte Anführungszeichen erforderlich sind-DP='puts("hello")'
.Wenn Ihr Programm in jedem Schritt einzeln liest oder schreibt, versuchen Sie immer, die Lese- und Schreibfunktion anstelle von getchar () und putchar () zu verwenden .
Beispiel ( stdin umkehren und auf stdout setzen )
Übung: diese Technik verwenden , eine gute Note zu bekommen hier .
quelle
Reverse Loops
Wenn Sie können, versuchen Sie zu ersetzen
mit
quelle
Wenn Sie jemals ein einzelnes Zeilenumbruchzeichen (
\n
) ausgeben müssen , verwenden Sie nichtputchar(10)
, verwenden Sieputs("")
.quelle
Verwenden Sie Rückgabewerte, die auf Null gesetzt sind. Wenn Sie eine Funktion aufrufen und diese Funktion unter normalen Bedingungen Null zurückgibt, können Sie sie an einer Stelle platzieren, an der Null erwartet wird. Ebenso, wenn Sie wissen, dass die Funktion ungleich Null zurückgibt, mit der Hinzufügung eines Knalls. Schließlich machen Sie in einem Code-Golf auf keinen Fall die richtige Fehlerbehandlung, oder?
Beispiele:
quelle
Zuweisen statt zurückgeben.
Dies ist nicht wirklich Standard C, funktioniert aber mit jedem Compiler und jeder CPU, die ich kenne:
hat den gleichen Effekt wie:
Weil das erste Argument im selben CPU-Register gespeichert ist wie der Rückgabewert.
Hinweis: Wie in einem Kommentar erwähnt, ist dies ein undefiniertes Verhalten und es kann nicht garantiert werden, dass es für jede Operation funktioniert. Und jede Compileroptimierung überspringt sie einfach.
X-Makros
Ein weiteres nützliches Feature: X-Macros können Ihnen helfen, wenn Sie eine Liste von Variablen haben und einige Operationen ausführen müssen, die alle betreffen:
https://en.wikipedia.org/wiki/X_Macro
quelle
-O0
wählt immer, Ausdrücke im Rückgabewertregister auszuwerten. Ich habe mir mindestens x86, ARM und MIPS angeschaut (auf gcc.godbolt.org ), und gcc scheint sich Mühe zu geben, dies zu tun-O0
. Aber denken Sie daran , wenn Sie dies nutzen, die Sprache , die Sie programmieren in istgcc -O0
, nicht C , und Sie sollten Ihre Antwort entsprechend beschriften, nicht als C . Es schlägt auf jeder anderen Optimierungsstufe als dem-O0
Debug-Modus fehl und funktioniert nicht mit Clang IIRC.Verwenden Sie
*a
anstelle von,a[0]
um auf das erste Element eines Arrays zuzugreifen.Relationale Operatoren (
!=
,>
usw.) geben0
oder1
. Verwenden Sie dies mit arithmetischen Operatoren, um unterschiedliche Offsets anzugeben, je nachdem, ob die Bedingung wahr oder falsch ist:a[1+2*(i<3)]
Greifen Sie darauf zu,a[1]
wenni >= 3
unda[3]
andernfalls.quelle
a[i<3?3:1]
ist zwei Zeichen kürzer alsa[1+2*(i<3)]
.Sie können in die IOCCC-Archive schauen (internationaler C-Code-Wettbewerb).
Ein bemerkenswerter Trick besteht darin, # Makros zu definieren, deren Erweiterung nicht ausgeglichene Klammern / Klammern enthält, wie z
quelle
#define P;printf(
.for(int i=0;i<n;i++){a(i);b(i);}
kann auf einige Arten verkürzt werden:for(int i=0;i<n;){a(i);b(i++);}
-1 zum Verschieben des++
zum Letzteni
in der Schleifefor(int i=0;i<n;b(i++))a(i);
-3 weitere, um alle Anweisungen bis auf eine in die oberste und aus der Hauptschleife zu verschieben und die geschweiften Klammern zu entfernenquelle
Gehen Sie funktionsfähig!
Wenn Sie Ihr Problem auf einfache Funktionen mit der gleichen Signatur und definiert als einzelne Ausdrücke reduzieren können, können Sie
#define r return
fast das gesamte Boilerplate für die Definition einer Funktion über- und ausklammern.Das Programmergebnis ist der Statuswert, der an das Betriebssystem oder die steuernde Shell oder IDE zurückgegeben wird.
Mit
__VA_ARGS__
können Sie den Komma-Operator verwenden, um Sequenzpunkte in diese Funktionsausdrücke einzufügen . Wenn dies nicht benötigt wird, kann das Makro kürzer sein.quelle
Verwenden Sie
scanf("%*d ");
, um den Dummy-Eingang zu lesen. (falls diese Eingabe in einem weiteren Programm bedeutungslos ist) Sie ist kürzer als dort,scanf("%d",&t);
wo Sie auch die Variable t deklarieren müssen.Das Speichern von Zeichen im Int-Array ist viel besser als das Speichern von Zeichen im Array. Beispiel.
s[],t;main(c){for(scanf("%*d ");~(c=getchar());s[t++]=c)putchar(s[t]);}
quelle
%*d
nicht nur Golf, weil es auch in Situationen nützlich ist, in denen man zum Beispiel einen Zeilenumbruch überspringen möchtescanf("%[^\n]%*c",str);
:)Drucken Sie ein Zeichen und dann einen Wagenrücklauf anstelle von:
oder
deklarieren Sie einfach c als int und:
quelle
puts(&c)
wirklich? Das wäre nicht unbedingt nullterminiert.char *
, sehen wir einen Singleton-String: das Zeichen c , gefolgt von einem Null-Byte.Die Verwendung
asprintf()
erspart Ihnen das explizite Zuweisen und Messen der Länge eines Strings akachar*
! Dies ist vielleicht nicht allzu nützlich für das Code-Golfen, erleichtert aber die tägliche Arbeit mit einem Char-Array. Es gibt einige weitere gute berät in 21st Century C .Anwendungsbeispiel:
quelle
import
wenn du musstWie bereits in der ersten Antwort erwähnt , können Sie bei einigen Compilern (insbesondere GCC und Clang)
#include
s für Standardbibliotheksfunktionen weglassen .Auch wenn Sie das nicht einfach entfernen können
#include
, gibt es möglicherweise andere Möglichkeiten, dies zu vermeiden , aber das ist nicht immer praktisch oder besonders golfen.In den übrigen Fällen können Sie
#import<header file>
anstelle von#include<header file>
ein Byte speichern. Dies ist eine GNU-Erweiterung und gilt als veraltet, funktioniert aber mindestens in gcc 4.8, gcc 5.1 und clang 3.7.quelle
Versuchen Sie es
cpow()
stattcos()
Anstatt von
versuchen Sie etwas wie
Dies basiert auf der Euler-Formel , einer kleinen komplexen Analyse und der Beobachtung, dass die Zuweisung eines Komplexes zu einem Double den Realteil ergibt (Vorsicht bei verschiedenen Funktionsaufrufen und anderen Feinheiten).
Diese Art von Trick kann verwendet werden, um zu reduzieren
in
da
quelle
Hier sind ein paar Tipps, die ich zu meinem Vorteil genutzt habe. Ich habe sie schamlos von anderen gestohlen.
Belegung mit Funktionsaufrufen kombinieren
An Stelle von:
Mach das:
Mehrere Variablen zusammen initialisieren (wenn möglich)
An Stelle von:
Mach das:
Reduzieren Sie Null- / Nicht-Null-Werte
Dies ist ein netter Trick, den ich von jemandem hier aufgegriffen habe (ich erinnere mich nicht an wen, sorry). Wenn Sie einen ganzzahligen Wert haben und ihn auf 1 oder 0 reduzieren müssen, können
!!
Sie dies auf einfache Weise tun. Dies ist manchmal vorteilhaft für andere Alternativen wie?:
.Nehmen Sie diese Situation:
Sie könnten stattdessen Folgendes tun:
Ein anderes Beispiel:
Könnte umgeschrieben werden als:
quelle
R*-~!!mxxxx
Wenn Sie die grundlegenden logischen Gleichheiten kennen, können möglicherweise einige Bytes eingespart werden.
if (!(a&&b)){}
Verwenden Sie beispielsweise stattdessen das DeMorgan-Gesetzif (!a||!b){}
. Gleiches gilt für bitweise Funktionen: statt~(a|b)
do~a&~b
.quelle