Tipps zum Golfen in C

137

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.

Casey
quelle
8
Ich denke, der größte Ein-Satz-Hinweis lautet: Lies die Gewinncodes, die der IOCCC übermittelt wurden.
vsz

Antworten:

107

Verwenden Sie bitweises XOR, um die Ungleichheit zwischen Ganzzahlen zu überprüfen:

if(a^b)anstelle von if(a!=b)1 Zeichen speichert.

Lowjacker
quelle
73
a-bgibt Ihnen den gleichen Effekt.
Ugoren
22
Ebenso können Sie a*banstelle von verwenden a&&b(hat unterschiedliche Priorität, kann oder kann nicht schlecht sein). Wenn Sie ein / = -b kennen (z. B. ohne Vorzeichen), dann a||b==a+b
walpen
3
Besser noch, kombiniere es mit dem Elvis Operator ?:(anstelle von if): zum Beispiel, um nur etwas zu tun, wenn es anders ist: a^b?_diff_:;
Olivier Dulac
1
@OlivierDulac Gibt es einen Compiler, der eine leere ternäre, wenn falsche Verzweigung akzeptiert?
Jonathan Frech
1
@OlivierDulac Sie können überprüfen. Soweit ich weiß, hat GCC einen ?:Operator, der genau gleichbedeutend ist mita ? a : b
Chromium
75
  • mainDie Argumentliste von Missbrauch , um eine oder mehrere Ganzzahlvariablen zu deklarieren:

    main(a){for(;++a<28;)putchar(95+a);}
    

    (Antwort auf Das Alphabet in Programmiersprachen )

    Diese Lösung missbraucht auch die Tatsache, dass a(aka argc) als beginnt 1, vorausgesetzt, das Programm wird ohne Argumente aufgerufen.

  • Verwenden Sie globale Variablen, um Dinge auf Null zu initialisieren:

    t[52],i;main(c){for(;i<52;)(c=getchar())<11?i+=26:t[i+c-97]++;
    for(i=27;--i&&t[i-1]==t[i+25];);puts(i?"false":"true");}
    

    (Antwort auf Anagram Code Golf! )

Joey Adams
quelle
62

Der Komma-Operator kann verwendet werden, um mehrere Ausdrücke in einem einzigen Block auszuführen und dabei geschweifte Klammern zu vermeiden:

main(){                                                                                     

int i = 0;                                                                                  
int j = 1;                                                                                  
if(1)                                                                                       
    i=j,j+=1,printf("%d %d\n",i,j); // multiple statements are all executed                                                  
else                                                                                        
    printf("failed\n");                                                                     

}

Ausgänge: 1 2

Casey
quelle
Funktioniert nicht, wenn eine der Anweisungen ist break.
Maxim Mikhaylov
9
@MaxLawnboy weil breakist eine Aussage, und diese Antwort spricht über Ausdrücke.
NieDzejkob
59

Vermeiden Sie katastrophale Typdeklarationen für Funktionsargumente

Wenn Sie eine Funktion deklarieren, in der alle fünf Argumente ints sind, ist das Leben gut. du kannst einfach schreiben

f(a,b,c,d,e){

Angenommen d, es muss eine charoder sogar eine sein int*. Dann bist du fertig! Wenn einem Parameter ein Typ vorangestellt ist, müssen alle sein:

f(int a,int b,int c,int*d,int e){

Aber warte! Es gibt einen Ausweg aus dieser katastrophalen Explosion nutzloser Charaktere. Es geht so:

f(a,b,c,d,e) int *d; {

Dies spart sogar eine Standarddeklaration main, wenn Sie die Befehlszeilenargumente verwenden müssen:

main(c,v)char**v;{

ist zwei Bytes kürzer als

main(int c,char**v){

Ich war überrascht, dies zu entdecken, da ich es auf PPCG bisher nicht angetroffen habe.

Feersum
quelle
6
Warum um alles in der Welt funktioniert das?
Nathaniel
30
Anscheinend heißt das K & R-Stil und es geht ein Jahrzehnt vor ANSI C.
Dennis
Beachten Sie, dass die Verwendung von K & R-Funktionen und neueren (z. B. '99) Funktionen möglicherweise nicht möglich ist. Kommt auf deinen Compiler an.
dmckee
5
@ dmckee ist richtig. In C99 ist implizites Int -std=gnu99nicht 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. :)
luser droog
@luserdroog: Sie können -std=c89gcc oder clang anweisen , Ihren Code gemäß dem älteren Standard zu kompilieren, der implizite Int mit nur einer Warnung zulässt.
Peter Cordes
37

Anstelle von> = und <= können Sie einfach die Ganzzahldivision (/) verwenden, wenn die verglichenen Werte über Null liegen, wodurch ein Zeichen gespeichert wird. Zum Beispiel:

putchar(c/32&&126/c?c:46); //Prints the character, but if it is unprintable print "."

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).

putchar(c>31^c>126?c:46);

Der Trick der Ganzzahldivision ist beispielsweise nützlich, um zu entscheiden, ob eine Zahl kleiner als 100 ist, da hierdurch ein Zeichen gespeichert wird:

a<100 vs 99/a

Dies ist auch in Fällen sinnvoll, in denen eine höhere Priorität erforderlich ist.

Fors
quelle
Sie können schreibenputchar(c>31&c<127?c:46);
Jin X
37

Bestimmte Compiler, wie z. B. GCC, ermöglichen es Ihnen, grundlegende #includes-, param- und return-Typen für wegzulassen main.

Das Folgende ist ein gültiges C89- und C99-Programm, das (mit Warnungen) mit GCC kompiliert:

main(i) { printf("%d", i); }

Beachten Sie, dass #includefür stdio.h fehlt, der Rückgabetyp für mainfehlt und die Typdeklaration für ifehlt.

Casey
quelle
17
Technisch ist es nicht gültig gemäß den Standards, da main null oder zwei Parameter akzeptiert, nicht einen. Nicht, dass sich irgendjemand für Codegolf interessiert.
Konrad Borowski
Der Aufruf 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.
Nisse Engström
@ NisseEngström: Die Aufrufkonventionen für Mainstream-C-Implementierungen machen es sicher, verschiedene Funktionen ohne Prototypen aufzurufen. Die meisten C-Implementierungen definieren also das Verhalten.
Peter Cordes
29

Der ternäre Bedingungsoperator ?:kann oft als in für einfache Stand verwendet werden if- elseAussagen 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.

dmckee
quelle
Zusatz: Wenn Sie nur ein Wenn, aber kein Anderes benötigen, kann das Ternäre dennoch nützlich sein.
Casey
9
&&und ||kann auch verwendet werden: if(x==3)f()wird mit Ihrem Vorschlag x==3?f():0und kann weiter verbessert werden x==3&&f(). Seien Sie jedoch vorsichtig, wenn der Operator Vorrang hat. Wenn f()durch ersetzt wird y=1, ist für die &&Lösung ein zusätzlicher Satz Klammern erforderlich.
Ugoren
1
Ich hätte nie ?:gedacht , dass der gcc einen Wert ergibt. Kann ich das im Produktionscode verwenden? lol
Jeff Burdges
4
@ugoren: x==3&&f()kann bis zumx^3||f()
fgrieu 26.12.12
@fgrieu, ja, obwohl es hier nicht genau das Thema ist ( diese Antwort legt es nahe).
Ugoren
27

http://graphics.stanford.edu/~seander/bithacks.html

Bits sind nett.

~-x = x - 1
-~x = x + 1

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.

if(!(x&y)) x | y == x ^ y == x + y
if(!(~x&y)) x ^ y == x - y

Das ist esoterischer, aber ich hatte Gelegenheit, es zu benutzen. Wenn Ihnen das Kurzschließen egal ist

x*y == x && y
if(x!=-y) x+y == x || y

Ebenfalls:

if(x>0 && y>0) x/y == x>=y   
walpen
quelle
5
Der letzte Tipp ( (x/y) == (x>=y)) ist wirklich nützlich.
Ugoren
24

Verwenden Sie Lambdas (nicht portierbar)

Anstatt von

f(int*a,int*b){return*a>*b?1:-1;}
...
qsort(a,b,4,f);

oder (nur gcc)

qsort(a,b,4,({int L(int*a,int*b){a=*a>*b?1:-1;}L;}));

oder (llvm with blocks support)

qsort_b(a,b,4,^(const void*a,const void*b){return*(int*)a>*(int*)b?1:-1;});

versuchen Sie etwas wie

qsort(a,b,4,"\x8b\7+\6\xc3");

... 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 -Dund den entsprechenden Hex-Code in eine Zeichenfolge zu kopieren. Zum Beispiel,

int f(int*a, int*b){return *a-*b;}

... wenn es gcc -Os -cfür ein Linux x86_64-Ziel kompiliert wird, erzeugt es so etwas wie

0:   8b 07                   mov    (%rdi),%eax
2:   2b 06                   sub    (%rsi),%eax
4:   c3                      retq

GNU 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 gotoein paar Bytes sparen. Also statt

((int(*)())L"ﻫ")();

oder (wenn Ihre Umgebung keine arabischen Glyphen enthält)

((int(*)())L"\xfeeb")();

Versuchen

goto*&L"ﻫ";

oder

goto*&L"\xfeeb";

In diesem Beispiel eb feist x86 Maschinensprache für so etwas wie for(;;);und ein einfaches Beispiel für etwas, das keine Parameter akzeptiert und nicht zurückkehren wird :-)

Es hat sich herausgestellt, dass Sie gotoCode verwenden können, der an einen anrufenden Elternteil zurückgegeben wird.

#include<stdio.h>
int f(int a){
 if(!a)return 1;
 goto*&L"\xc3c031"; // return 0;
 return 2; // never gets here
}
int main(){
 printf("f(0)=%d f(1)=%d\n",f(0),f(1));
}

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 -zexecstackKompilierungsflag 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.

Ceilingcat
quelle
2
Ich habe gerade ein Skript geschrieben , um Teile einer C-Funktion von Standard in zu lesen und ein C-Lambda zu drucken. Könnte es wert sein, in Ihrer Antwort erwähnt zu werden, wäre vielleicht nur schön für Sie zu sehen, da Sie mir das an erster Stelle beigebracht haben.
MD XF
23

Verwenden Sie Cursor anstelle von Zeigern. Haken Sie das brk()am Anfang und verwenden Sie es als Basiszeiger .

char*m=brk();

Legen Sie dann ein #define für den Speicherzugriff fest.

#define M [m]

Mwird 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):

f(x){return x M;} //implicit ints, but they work like pointers
#define f(x) (x M)

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.

char *p = sbrk(sizeof(whatever)) - m;
strcpy(m+p, "hello world");

Diese Technik wird in meiner Antwort verwendet, um einen Interpreter für den untypisierten Lambda-Kalkül zu schreiben .

Luser Droog
quelle
21

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)*2kann werden x+y<<1.

ugoren
quelle
Oder einfach x+y*2, um noch einen Char zu retten.
Braden Best
4
@ B1KMusic x+y*2ist aufgrund der Rangfolge des Operators nicht dasselbe.
Ugoren
Richtig, lol. Das wäre x + (y * 2). Ich war auf das x+y<<1Beispiel fixiert, nahm an , dass es als ausgewertet wurde x+(y<<1), und schlug *2stattdessen das vor. Ich wusste nicht, dass Bitverschiebungsoperationen als zB(x+y)<<2
Braden Best
20

EOF == -1Verwenden Sie normalerweise den bitweisen Operator NOT, um nach EOF zu suchen: while(~(c=getchar()))oder while(c=getchar()+1)und ändern Sie den Wert von c an jeder Stelle

Lowjacker
quelle
1
Ich kenne C nicht gut genug, würde aber nicht while(1+c=getchar())funktionieren?
Dienstag,
6
@ ɐɔıɐɔuʇǝɥʇs Nein. Der Additionsoperator +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.
ace_HongKongIndependence
19

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:

if (t()) a = b, b = 0;  /* 15 chars */

Die übliche Vorgehensweise beim Golfen ist das Ersetzen ifdurch &&. Aufgrund der geringen Priorität des Komma-Operators benötigen Sie jedoch ein zusätzliches Klammerpaar:

t() && (a = b, b = 0);  /* still 15 chars */

Der mittlere Bereich des ternären Operators benötigt jedoch keine Klammern:

t() ? a = b, b = 0 : 0;  /* 14 chars */

Ähnliche Kommentare gelten für Array-Indizes.

Brot-Box
quelle
7
In diesem Beispiel b-=a=bist noch kürzer. Der ?:Trick ist immer noch hilfreich, -=weil er auch wenig Vorliebe hat.
Ugoren
Guter Punkt; Mein Beispiel war unnötig komplex.
Brotkasten
Ein weiterer Punkt ist, dass man manchmal die Bedingung umdrehen möchte: denn x>0||(y=3), x>0?0:(y=3)ist nutzlos, x<1?y=3:0erledigt aber den Job.
Ugoren
Sowohl clang als auch gcc lassen einen leeren wahren Fall im Ternary zu. Wenn weggelassen, ist sein Wert der Wert der Bedingung. Zum Beispielx>5?:y=1
Chris Uzdavinis
19

Jeder Teil Ihres Codes, der mehrmals wiederholt wird, kann durch den Vorprozessor ersetzt werden.

#define R return

Dies ist ein sehr häufiger Anwendungsfall, wenn Sie Code mit mehreren Funktionen verwenden. Andere longish Schlüsselwörter wie while, double, switchund casesind auch Kandidaten; sowie alles, was in Ihrem Code idomatisch ist.

Ich reserviere in der Regel Großbuchstaben für diesen Zweck.

dmckee
quelle
1
Ein kürzerer Ersatz wäre -DR=return. Beachten Sie, dass bei der Angabe bestimmter Zeichen möglicherweise einfache oder doppelte Anführungszeichen erforderlich sind -DP='puts("hello")'.
15

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 )

main(_){write(read(0,&_,1)&&main());}

Übung: diese Technik verwenden , eine gute Note zu bekommen hier .

Quixotic
quelle
Was meinst du mit auf jeder Schrittbasis ?
Casey
Casey: Ich denke, sie meinen, wenn das Programm etwas liest, es bearbeitet und eine Ausgabe schreibt. Sozusagen auf Streaming-Art. Im Gegensatz zu einem Ansatz, bei dem alle Eingaben gleichzeitig gelesen und verarbeitet werden müssen.
Joey
Joey hat recht, das habe ich auch gemeint. Tut mir leid, dass ich meinen Posteingang bis heute nicht überprüft habe.
Quixotic
8
Diese Stapelmanipulation ist großartig.
Andrea Biondo
14

Reverse Loops

Wenn Sie können, versuchen Sie zu ersetzen

for(int i=0;i<n;i++){...}

mit

for(int i=n;i--;){...}
Ceilingcat
quelle
13

Wenn Sie jemals ein einzelnes Zeilenumbruchzeichen ( \n) ausgeben müssen , verwenden Sie nicht putchar(10), verwenden Sie puts("").

ace_HongKongIndependence
quelle
12

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:

close(fd);foo=0;   →  foo=close(fd);    /* saves two bytes */
putchar(c);bar=0;  →  bar=!putchar(c);  /* saves one byte  */
MvG
quelle
12

Zuweisen statt zurückgeben.

Dies ist nicht wirklich Standard C, funktioniert aber mit jedem Compiler und jeder CPU, die ich kenne:

int sqr(int a){return a*a;}

hat den gleichen Effekt wie:

int sqr(int a){a*=a;}

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

GB
quelle
3
Ich habe dies zitiert und wurde korrigiert: Das ist einfach nicht wahr. Es funktioniert nur mit Multiplikationen und Divisionen und wenn Optimierungen deaktiviert sind. Dies liegt daran, dass beide Operationen ihre Ergebnisse zufällig in eax ablegen, dem gemeinsamen Register für die Rückgabe. Die Parameter werden entweder im Stack oder in ecx oder edx gespeichert. Versuch es selber.
Gaspa79
3
Sie haben Recht, es ist undefiniertes Verhalten, es hängt auch vom Compiler und von der Architektur ab. Normalerweise überprüfe ich x86 und armv7 mit gcc, bevor ich eine Antwort mit diesem Trick poste. Wenn Sie die Optimierung aktivieren, löscht jeder Smart Compiler die unnötige Multiplikation.
GB
3
Ich habe diese Arbeit mit GCC gesehen, aber nicht mit anderen
Albert Renshaw,
1
@ Gaspa79: gcc with -O0wä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 ist gcc -O0, nicht C , und Sie sollten Ihre Antwort entsprechend beschriften, nicht als C . Es schlägt auf jeder anderen Optimierungsstufe als dem -O0Debug-Modus fehl und funktioniert nicht mit Clang IIRC.
Peter Cordes
11
  1. Verwenden Sie *aanstelle von, a[0]um auf das erste Element eines Arrays zuzugreifen.

  2. Relationale Operatoren ( !=, >usw.) geben 0oder 1. 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]wenn i >= 3und a[3]andernfalls.

es1024
quelle
11
a[i<3?3:1]ist zwei Zeichen kürzer als a[1+2*(i<3)].
Reto Koradi
10

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

#define P printf(
Andreas Krey
quelle
16
Die nicht übereinstimmenden Klammern haben an sich keinen Wert. Der Punkt ist, so viel wie möglich des sich wiederholenden Musters zu definieren. Vielleicht möchten Sie noch weiter gehen, mit #define P;printf(.
Ugoren
Wie verkürzt sich die Anzahl der Bytes? Vielleicht ein Beispiel geben?
Cyoce
2
@Cyoce Siehe zum Beispiel diese Antwort .
Jonathan Frech
8

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 Letzten iin der Schleife

for(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 entfernen

MegaTom
quelle
Die Verwendung des Komma-Operators ist eine weitere Möglichkeit, um in einigen Fällen Klammern zu vermeiden.
Peter Cordes
8

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 returnfast das gesamte Boilerplate für die Definition einer Funktion über- und ausklammern.

#define D(f,...)f(x){return __VA_ARGS__;}
D(f,x+2)
D(g,4*x-4)
D(main,g(4))

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.

#define D(f,b)f(x){return b;}
Luser Droog
quelle
7
  1. 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.

  2. 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]);}

Neeraj Gupta
quelle
2
Eigentlich nutze ich %*dnicht nur Golf, weil es auch in Situationen nützlich ist, in denen man zum Beispiel einen Zeilenumbruch überspringen möchte scanf("%[^\n]%*c",str);:)
Tomsmeding
6

Drucken Sie ein Zeichen und dann einen Wagenrücklauf anstelle von:

printf("%c\n",c);

oder

putchar(c);putchar('\n'); // or its ascii value, whatever!

deklarieren Sie einfach c als int und:

puts(&c);
moala
quelle
9
Es ist wahrscheinlich erwähnenswert, dass dies von einer Little-Endian-Architektur abhängt. Wenn c ein Big-Endian-Int ist, erhalten Sie nur den Wagenrücklauf. (Auf der anderen Seite, wenn c ein Zeichen ist, können Sie zufälligen Müll nach anstelle eines Wagenrücklaufs bekommen.)
Brotkasten
@breadbox yep, du hast vollkommen recht; Ich habe gerade editiert: Der letzte Auszug sollte c als int verwenden (was häufig leicht als solches zu deklarieren ist).
Moala
Funktioniert das puts(&c)wirklich? Das wäre nicht unbedingt nullterminiert.
Esolanging Fruit
1
@EsolangingFruit Auf Little-Endian-Systemen mit 32-Bit-Ints wird ein int 0 ≤ c <256 als Byte-Sequenz c 0 0 0 gespeichert . Wenn wir die Adresse von c als interpretieren char *, sehen wir einen Singleton-String: das Zeichen c , gefolgt von einem Null-Byte.
Dennis
6

Die Verwendung asprintf()erspart Ihnen das explizite Zuweisen und Messen der Länge eines Strings aka char*! 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:

#define _GNU_SOURCE
#include <stdio.h>

int main(int argc, char** argv) {
  char* foo;
  asprintf(&foo, "%s", argv[1]);
  printf("%s",foo);
}
klingt.net
quelle
6

import wenn du musst

Wie bereits in der ersten Antwort erwähnt , können Sie bei einigen Compilern (insbesondere GCC und Clang) #includes 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.

Dennis
quelle
6

Versuchen Sie es cpow()stattcos()

Anstatt von

double y=cos(M_PI*2*x);

versuchen Sie etwas wie

double y=cpow(-1,x*2);

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).

\ cos 2 \ pi x + j \ sin 2 \ pi x = e ^ {j2 \ pi x} = e ^ {j \ pi 2x} = (- 1) ^ {2x}

Diese Art von Trick kann verwendet werden, um zu reduzieren

double y=cpow(-1,x/2);

in

double y=cpow(1i,x);

da (-1) ^ \ frac {x} {2} = j ^ {\ frac {2x} {2}} = j ^ x

Ceilingcat
quelle
5

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:

r = /* Some random expression */
printf("%d", r);

Mach das:

printf("%d", r = /* Some random expression */);

Mehrere Variablen zusammen initialisieren (wenn möglich)

An Stelle von:

for(i=0,j=0;...;...){ /* ... */ }

Mach das:

for(i=j=0;...;...){ /* ... */ }

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:

n=2*n+isupper(s[j])?1:0; /* 24 */

Sie könnten stattdessen Folgendes tun:

n=n*2+!!isupper(s[j]); /* 22 */

Ein anderes Beispiel:

r=R+(memcmp(b+6,"---",3)?R:0); /* 30 */

Könnte umgeschrieben werden als:

r=R+R*!!memcmp(b+6,"---",3)); /* 29 */
Cole Cameron
quelle
1
vielleichtR*-~!!mxxxx
l4m2
5

Wenn Sie die grundlegenden logischen Gleichheiten kennen, können möglicherweise einige Bytes eingespart werden. if (!(a&&b)){}Verwenden Sie beispielsweise stattdessen das DeMorgan-Gesetz if (!a||!b){}. Gleiches gilt für bitweise Funktionen: statt ~(a|b)do ~a&~b.

tox123
quelle
Vgl. De Morgans Gesetze .
Jonathan Frech