Conseils pour jouer au golf en C

138

Quels conseils généraux avez-vous pour jouer au golf en C? Je recherche des idées pouvant être appliquées aux problèmes de code de golf en général, qui sont au moins quelque peu spécifiques à C (par exemple, "supprimer les commentaires" n'est pas une réponse). Merci de poster un pourboire par réponse. Indiquez également si votre conseil s’applique à C89 et / ou C99 et s’il ne fonctionne que sur certains compilateurs.

Casey
la source
9
Je pense que le plus gros indice en une phrase est: Lisez les codes gagnants soumis à IOCCC.
vsz

Réponses:

107

Utilisez le bitor XOR pour vérifier l’inégalité entre les entiers:

if(a^b)au lieu de if(a!=b)sauvegarder 1 caractère.

Lowjacker
la source
73
a-bvous donne le même effet.
Ugoren
22
De la même façon, vous pouvez utiliser a*bau lieu de a&&b(a une précédence différente, peut être ou ne pas être mauvais). Si vous connaissez un / = -b (par exemple, ils ne sont pas signés), alors a||b==a+b
walpen
3
Mieux encore, combinez-le avec l'opérateur Elvis ?:(au lieu de if): par exemple pour faire quelque chose si différent: a^b?_diff_:;
Olivier Dulac
1
@OlivierDulac Existe-t-il un compilateur qui accepte un ternaire vide si fausse branche?
Jonathan Frech
1
@OlivierDulac Vous pouvez vérifier. D'après ce que je sais, GCC a un ?:opérateur qui équivaut juste àa ? a : b
Chromium
75
  • mainListe des arguments abusifs pour déclarer une ou plusieurs variables entières:

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

    (réponse à l'alphabet dans les langages de programmation )

    Cette solution abuse également du fait que a(aka argc) commence comme 1, à condition que le programme soit appelé sans arguments.

  • Utilisez des variables globales pour initialiser les éléments à zéro:

    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");}
    

    (réponse à Anagram Code Golf! )

Joey Adams
la source
62

L'opérateur de virgule peut être utilisé pour exécuter plusieurs expressions dans un seul bloc tout en évitant les accolades:

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");                                                                     

}

Les sorties: 1 2

Casey
la source
Ne fonctionne pas si l'une des déclarations est break.
Maxim Mikhaylov
9
@ MaxLawnboy car breakest une déclaration, et cette réponse parle d'expressions.
NieDzejkob
59

Évitez les déclarations catastrophiques de type argument de fonction

Si vous déclarez une fonction où les cinq arguments sont tous des ints, la vie est belle. vous pouvez simplement écrire

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

Mais supposons que ce dsoit un char, voire un int*. Alors vous êtes foutu! Si un paramètre est précédé d'un type, tous doivent être:

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

Mais attendez! Il y a un moyen de contourner cette explosion désastreuse de personnages inutiles. Ça va comme ça:

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

Cela enregistre même une maindéclaration standard si vous devez utiliser les arguments de ligne de commande:

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

est deux octets plus court que

main(int c,char**v){

J'ai été surpris de découvrir cela, car je ne l'ai pas encore rencontré sur PPCG.

feersum
la source
6
Pourquoi est-ce que ça marche ??
Nathaniel
30
Apparemment, cela s'appelle le style K & R et il précède ANSI C d'une décennie.
Dennis
Notez que l’utilisation simultanée des fonctions K & R et des nouvelles (par exemple, '99) peut être impossible ou impossible. Cela dépend de votre compilateur.
dmckee
5
@dmckee a raison. C99 n'autorise pas les int implicites, vous devez donc les utiliser -std=gnu99et vous n'êtes plus portable. En clair, vous n'écrivez même pas le code "C" en tant que tel, mais "Gnu99-C". 'Ici, nous l'ignorons surtout, mais il est bon de le mentionner si vous postez du code spécifique au compilateur. Parfois , les gens effectivement ne télécharger et exécuter ces programmes de la nôtre. :)
luser droog
@luserdroog: Vous pouvez utiliser -std=c89pour dire à gcc ou à clang de compiler votre code conformément à cette norme plus ancienne, qui n'autorise implicitement int qu'avec un avertissement.
Peter Cordes
37

Au lieu de> = et <=, vous pouvez simplement utiliser une division entière (/) lorsque les valeurs comparées sont supérieures à zéro, ce qui enregistre un caractère. Par exemple:

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

Ce qui est bien sûr toujours rétrécissable, en utilisant par exemple juste> et ^ (un moyen intelligent d’éviter d’écrire && ou || dans certains cas).

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

L'astuce de la division entière est par exemple utile pour décider si un nombre est inférieur à 100, car cela enregistre un caractère:

a<100 vs 99/a

Ceci est également utile dans les cas où une priorité plus élevée est nécessaire.

Fors
la source
Vous pouvez écrireputchar(c>31&c<127?c:46);
Jin X
37

Certains compilateurs, tels que GCC, vous permettent d’omettre les #includetypes de base s, param et return pour main.

Ce qui suit est un programme valide C89 et C99 qui compile (avec des avertissements) avec GCC:

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

Notez que le #includefichier stdio.h est manquant, le type de retour pour mainmanquant et la déclaration de type pour imanquant.

Casey
la source
17
Techniquement, ce n'est pas conforme aux normes, car principal accepte zéro ou deux paramètres, pas un. Pas que quiconque se soucie de code-golf.
Konrad Borowski
L'appel printf()(ou toute fonction variadique) sans prototype provoque un comportement indéfini . GCC ne compile pas le C standard par défaut. Si vous appelez gcc en mode C89 ( gcc -ansi -pedantic) ou C99 mode ( gcc -std=c99 -pedantic), vous aurez plusieurs plaintes, du moins dans ce dernier cas.
Nisse Engström
@ NisseEngström: les conventions d'appel relatives aux implémentations C standard permettent de faire appel en toute sécurité à des fonctions variadiques sans prototypes. Donc, la plupart des implémentations C définissent le comportement.
Peter Cordes
29

L'opérateur conditionnel ternaire ?:peut souvent être utilisé comme support dans des simples if- elsedéclarations à des économies considérables.

Contrairement à l’équivalent c ++, l’opérateur ne donne pas formellement de valeur , mais certains compilateurs (notamment gcc) vous laisseront vous en tirer, ce qui est un bonus appréciable.

dmckee
la source
Ajout: Si vous avez seulement besoin d'un if, mais pas d'un autre, le ternaire peut toujours être utile.
Casey
9
&&et ||peut également être utilisé: if(x==3)f()devient avec votre suggestion x==3?f():0, et peut être encore amélioré x==3&&f(). Mais soyez prudent avec la priorité des opérateurs - si f()est remplacé par y=1, la &&solution nécessite un ensemble supplémentaire de parenthèses.
Ugoren
1
Je n'avais jamais réalisé que gcc ?:rapportait une valeur. Puis-je utiliser cela dans le code de production? lol
Jeff Burdges
4
@ugoren: x==3&&f()peut encore être joué au x^3||f()
golf
@fgrieu, oui, bien que ce ne soit pas exactement le sujet ici ( cette réponse le suggère).
Ugoren
27

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

Les bits sont gentils.

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

Mais avec des priorités différentes, et ne changez pas x comme ++ et -. Vous pouvez aussi l'utiliser dans des cas très spécifiques: ~ 9 est plus court que -10.

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

C'est plus ésotérique, mais j'ai eu l'occasion de l'utiliser. Si vous ne vous souciez pas du court-circuit

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

Aussi:

if(x>0 && y>0) x/y == x>=y   
walpen
la source
5
Le dernier conseil ( (x/y) == (x>=y)) est vraiment utile.
Ugoren
24

Utilisez lambdas (unportable)

Au lieu de

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

ou (gcc seulement)

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

ou (llvm avec le support de blocs)

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

essayez quelque chose comme

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

... où la chaîne entre guillemets contient les instructions en langage machine de votre fonction "lambda" (conformes à toutes les exigences de la plate-forme ABI).

Cela fonctionne dans les environnements dans lesquels les constantes de chaîne sont marquées comme étant exécutables. Par défaut, cela est vrai sous Linux et OSX mais pas sous Windows.

Une façon idiote d'apprendre à écrire vos propres fonctions "lambda" consiste à écrire la fonction en C, à la compiler, à l'inspecter avec quelque chose de similaire objdump -Det à copier le code hexadécimal correspondant dans une chaîne. Par exemple,

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

... lorsque compilé avec gcc -Os -cpour une cible Linux x86_64 génère quelque chose comme

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

GNU CC goto:

Vous pouvez appeler ces "fonctions lambda" directement, mais si le code que vous appelez ne prend pas de paramètres et ne retourne pas, vous pouvez utiliser gotopour enregistrer quelques octets. Donc au lieu de

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

ou (si votre environnement n'a pas de glyphes arabes)

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

Essayer

goto*&L"ﻫ";

ou

goto*&L"\xfeeb";

Dans cet exemple, eb feest le langage machine x86 pour quelque chose comme for(;;);et est un exemple simple de quelque chose qui ne prend pas de paramètres et ne retournera pas :-)

Il s'avère que vous pouvez gotocoder pour retourner à un parent appelant.

#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));
}

L'exemple ci-dessus (pouvant être compilé et exécuté sous Linux avec gcc -O) est sensible à la disposition de la pile.

EDIT: En fonction de votre chaîne d’outils, vous devrez peut-être utiliser le -zexecstackdrapeau de compilation.

Si ce n'est pas immédiatement évident, cette réponse a été principalement écrite pour les enfants. En lisant ceci, je n’assume aucune responsabilité pour le golf, meilleur ou pire, ni pour les résultats psychologiques défavorables.

plafondcat
la source
2
Je viens d'écrire un script pour lire des parties d'une fonction C de manière standard et imprimer un fichier C lambda. Cela vaut peut-être la peine de le mentionner dans votre réponse. Ce serait peut-être agréable à voir puisque vous m'avez appris à le faire en premier lieu.
MD XF
23

Utilisez des curseurs au lieu de pointeurs. Accrochez le brk()au début et utilisez-le comme pointeur de base .

char*m=brk();

Puis définissez une #define pour l’accès à la mémoire.

#define M [m]

Mdevient un postfix *appliqué aux entiers. (L'ancien a [x] == x [un] tour.)

Mais il y a plus! Ensuite, vous pouvez avoir des arguments et des retours de pointeur dans des fonctions plus courtes que les macros (en particulier si vous abrégez "return"):

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

Pour créer un curseur à partir d'un pointeur, vous soustrayez le pointeur de base, ce qui donne un ptrdiff_t, qui est tronqué en un entier, les pertes sont votre biz.

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

Cette technique est utilisée dans ma réponse à Ecrire un interprète pour le calcul lambda non typé .

luser droog
la source
21

Définissez des paramètres plutôt que des variables.

f(x){int y=x+1;...}

f(x,y){y=x+1;...}

Vous n'avez pas besoin de passer le deuxième paramètre.

En outre, vous pouvez utiliser la priorité des opérateurs pour enregistrer les parenthèses.
Par exemple, (x+y)*2peut devenir x+y<<1.

Ugoren
la source
Ou juste x+y*2, sauver encore un autre personnage.
Braden Best
4
@ B1KMusic, x+y*2n'est pas le même, en raison de la priorité des opérateurs.
Ugoren
Droit, lol. Ce serait x + (y * 2). J'étais obsédé par l' x+y<<1exemple, en supposant qu'il était évalué ainsi x+(y<<1), et le suggérai *2. Je ne savais pas que les opérations de bitshift avaient été évaluées comme par exemple(x+y)<<2
Braden Best le
20

Comme d'habitude EOF == -1, utilisez l'opérateur NOT au niveau du bit pour vérifier EOF: while(~(c=getchar()))ou while(c=getchar()+1)et modifiez la valeur de c à chaque endroit

Lowjacker
la source
1
Je ne connais pas assez bien C, mais ça ne while(1+c=getchar())marcherait pas ?
ıʇǝɥʇuʎs
6
@ ɐɔıʇǝɥʇuʎs Non L'opérateur d'addition +a une priorité plus élevée que l'opérateur d'affectation =, de sorte 1+c=getchar()équivaut à (1+c)=getchar(), qui ne compile pas parce que (1+c)n'est pas une lvalue.
ace_HongKongIndependence
19

L’opérateur ternaire a la ?:particularité de comporter deux pièces distinctes. Pour cette raison, il fournit une petite échappatoire aux règles de priorité des opérateurs standard. Cela peut être utile pour éviter les parenthèses.

Prenons l'exemple suivant:

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

L’approche habituelle du golf consiste à remplacer le ifavec &&, mais à cause de la faible préséance de l’exploitant virgule, vous avez besoin d’une paire de parenthèses supplémentaire:

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

La partie centrale de l'opérateur ternaire n'a pas besoin de parenthèses, cependant:

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

Des commentaires similaires s'appliquent aux indices de tableau.

boite à pain
la source
7
Dans cet exemple, b-=a=bc'est encore plus court. L' ?:astuce est toujours utile, -=car a également une faible préférence.
Ugoren
Bon point; mon exemple était inutilement complexe.
breadbox
Un autre point est que, parfois, vous souhaitez inverser la condition: car x>0||(y=3), x>0?0:(y=3)est inutile, mais x<1?y=3:0fait le travail.
Ugoren
à la fois clang et gcc permettent de créer un cas réel vide dans le ternaire. Si omis, sa valeur est la valeur de la condition. Par exemple,x>5?:y=1
Chris Uzdavinis
19

Toute partie de votre code qui se répète plusieurs fois est susceptible d'être remplacée par le pré-processeur.

#define R return

est un cas d'utilisation très courant si votre code implique plus que quelques fonctions. D' autres mots - clés comme oblongues while, double, switchet casesont également des candidats; ainsi que tout ce qui est idomatic dans votre code.

Je réserve généralement le caractère majuscule à cette fin.

dmckee
la source
1
Un remplacement plus court serait -DR=return. Notez que si vous incluez certains caractères, il peut s'avérer nécessaire de placer des guillemets simples ou doubles autour du define -DP='puts("hello")'.
15

Si votre programme lit ou écrit sur une base à chaque étape, essayez toujours d'utiliser les fonctions lecture et écriture au lieu de getchar () et putchar () .

Exemple ( inverser stdin et placer sur stdout )

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

Exercice: Utilisez cette technique pour obtenir un bon score ici .

Chimérique
la source
Qu'entendez-vous par étape par étape ?
Casey
Casey: Je suppose qu'ils veulent dire que si le programme lit quelque chose, opère dessus et écrit la sortie. De manière continue, pour ainsi dire. Contrairement à une approche où toutes les contributions doivent être lues et traitées en même temps.
Joey
Joey a raison, je voulais dire la même chose, désolé de ne pas avoir consulté ma boîte de réception jusqu'à aujourd'hui.
Quixotic
8
Cette manipulation de pile est magnifique.
Andrea Biondo
14

Boucles inverses

Si vous le pouvez, essayez de remplacer

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

avec

for(int i=n;i--;){...}
plafondcat
la source
13

Si vous avez besoin de générer un seul caractère de nouvelle ligne ( \n), n'utilisez pas putchar(10), utilisez puts("").

ace_HongKongIndependence
la source
12

Utilisez les valeurs de retour à zéro. Si vous appelez une fonction et que cette fonction renvoie zéro dans des conditions normales, vous pouvez la placer à un emplacement où zéro est attendu. De même, si vous savez que la fonction retournera une valeur nulle, avec l'ajout d'un coup. Après tout, vous ne faites en aucun cas le traitement correct des erreurs dans un code de golf, n'est-ce pas?

Exemples:

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

Attribuer au lieu de retourner.

Ce n'est pas vraiment C standard, mais fonctionne avec tous les compilateurs et processeurs que je connais:

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

a le même effet que:

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

Parce que le premier argument est stocké dans le même registre de la CPU que la valeur de retour.

Remarque: comme indiqué dans un commentaire, il s'agit d'un comportement non défini et non garanti pour chaque opération. Et toute optimisation du compilateur l'ignorera simplement.

X-Macros

Autre fonctionnalité utile: X-Macros peut vous aider lorsque vous avez une liste de variables et que vous devez effectuer certaines opérations qui les impliquent toutes:

https://en.wikipedia.org/wiki/X_Macro

GB
la source
3
J'ai cité ceci et j'ai été corrigé, ce n'est tout simplement pas vrai. Cela fonctionnera uniquement avec les multiplications et les divisions et lorsque les optimisations sont désactivées. En effet, les deux opérations mettent leurs résultats dans eax, qui est le registre commun pour le retour. Les paramètres sont stockés soit dans la pile, soit dans ecx ou edx. Essayez vous-même.
Gaspa79
3
Vous avez raison, c'est un comportement indéfini, cela dépend aussi du compilateur et de l'architecture, je vérifie généralement avec gcc sur x86 et armv7 avant d'envoyer toute réponse utilisant cette astuce. Et bien sûr, si vous activez l'optimisation, tout compilateur intelligent supprimera simplement la multiplication inutile.
GB
3
J'ai vu ce travail avec GCC mais pas avec d'autres
Albert Renshaw
1
@ Gaspa79: gcc with -O0choisit toujours d'évaluer les expressions dans le registre de valeur de retour. J'ai au moins regardé x86, ARM et MIPS (sur gcc.godbolt.org ), et gcc semble faire de son mieux pour le faire -O0. Mais rappelez - vous si vous prenez avantage de cela, la langue que vous la programmation en est - gcc -O0, pas C , et vous devriez étiqueter votre réponse en conséquence, non pas comme C . Il échoue à tous les niveaux d'optimisation autres que le -O0mode débogage et ne fonctionne pas avec Clang IIRC.
Peter Cordes
11
  1. Utilisez *aau lieu de a[0]pour accéder au premier élément d'un tableau.

  2. Les opérateurs relationnels ( !=, >, etc.) donnent 0ou 1. Utilisez ceci avec des opérateurs arithmétiques pour donner différents décalages selon que la condition est vraie ou fausse: a[1+2*(i<3)]accéderait a[1]si i >= 3et a[3]autrement.

es1024
la source
11
a[i<3?3:1]est deux caractères plus court que a[1+2*(i<3)].
Reto Koradi
10

Vous pouvez consulter les archives de l'IOCCC (concours international de code C obfusqué).

Une astuce notable consiste à # définir des macros dont le développement comporte des accolades / parenthèses non équilibrées, comme

#define P printf(
Andreas Krey
la source
16
Les parenthèses incompatibles n'ont aucune valeur en elles-mêmes. Le but est de définir autant de motifs répétitifs que possible. Vous voudrez peut-être aller plus loin, avec #define P;printf(.
Ugoren
Comment cela réduit-il le nombre d'octets? Peut-être donner un exemple?
Cyoce
2
@Cyoce Voir par exemple cette réponse .
Jonathan Frech
8

for(int i=0;i<n;i++){a(i);b(i);} peut être raccourci de plusieurs manières:

for(int i=0;i<n;){a(i);b(i++);} -1 pour déplacer ++le dernier idans la boucle

for(int i=0;i<n;b(i++))a(i); -3 de plus pour déplacer toutes les instructions sauf une dans le haut et hors de la boucle principale, en supprimant les accolades

MegaTom
la source
L'utilisation de l'opérateur de virgule est un autre moyen d'éviter les accolades dans certains cas.
Peter Cordes
8

Allez fonctionnel!

Si vous pouvez réduire votre problème à des fonctions simples avec la même signature et définies comme des expressions uniques, vous pouvez alors faire mieux que de #define r returnfactoriser la quasi-totalité du passe-partout pour définir une fonction.

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

Le résultat du programme est sa valeur d'état renvoyée au système d'exploitation ou au shell ou IDE de contrôle.

Utiliser __VA_ARGS__vous permet d'utiliser l'opérateur de virgule pour introduire des points de séquence dans ces expressions de fonction . Si cela n'est pas nécessaire, la macro peut être plus courte.

#define D(f,b)f(x){return b;}
luser droog
la source
7
  1. utiliser scanf("%*d ");pour lire l'entrée factice. (si cette entrée n'a pas de sens dans le programme suivant), elle est plus courte que celle pour scanf("%d",&t);laquelle vous devez également déclarer la variable t.

  2. stocker des caractères dans un tableau int est bien meilleur qu'un tableau de caractères. exemple.

    s[],t;main(c){for(scanf("%*d ");~(c=getchar());s[t++]=c)putchar(s[t]);}

Neeraj Gupta
la source
2
En fait, je l'utilise %*dnon seulement dans le golf, mais aussi dans les situations où l'on voudrait, par exemple, sauter une nouvelle ligne dans scanf("%[^\n]%*c",str);:)
tomsmeding
6

Imprimer un caractère puis un retour chariot au lieu de:

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

ou

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

tout simplement, déclarez c comme un int et:

puts(&c);
Moala
la source
9
Cela vaut probablement la peine de souligner que cela dépend d'une architecture little-endian. Si c est un int big-endian, alors vous aurez juste le retour chariot. (D'un autre côté, si c est un caractère, vous pourriez avoir des ordures aléatoires après le retour à la ligne.)
breadbox
@breadbox Oui, vous avez tout à fait raison. Je viens d'éditer: le dernier extrait devrait utiliser c comme un int (qui est souvent facile à déclarer comme tel).
Moala
Fonctionne puts(&c)vraiment? Cela ne serait pas nécessairement terminé par un zéro.
Esolanging Fruit
1
@EsolangingFruit Sur little-endian avec des bits de 32 bits, un int 0 ≤ c <256 est stocké en tant que séquence d'octets c 0 0 0 . Quand on interprète l'adresse de c as char *, on voit une chaîne singleton: le caractère c , suivi d'un octet nul.
Dennis
6

L'utilisation asprintf()vous permet d'économiser l'allocation explicite et la mesure de la longueur d'une chaîne aka char*! Ce n'est peut-être pas très utile pour le golf de code, mais facilite le travail quotidien avec un tableau de caractères. Il y a encore d'autres bons conseils au 21ème siècle .

Exemple d'utilisation:

#define _GNU_SOURCE
#include <stdio.h>

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

import si tu dois

Comme indiqué dans la toute première réponse , certains compilateurs (notamment GCC et clang) vous permettent d'omettre #includes pour omettre les fonctions de bibliothèque standard.

Même si vous ne pouvez pas simplement supprimer le fichier #include, il existe peut-être d' autres moyens de l'éviter , mais ce n'est pas toujours pratique ni particulièrement risqué.

Dans les cas restants, vous pouvez utiliser #import<header file>au lieu de #include<header file>sauvegarder un octet. Ceci est une extension GNU et il est considéré comme obsolète, mais cela fonctionne au moins dans gcc 4.8, gcc 5.1 et clang 3.7.

Dennis
la source
6

Essayer cpow()au lieu decos()

Au lieu de

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

essayez quelque chose comme

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

Ceci utilise la formule d'Euler , une petite analyse complexe et l'observation qui attribue un complexe à un double donne la partie réelle (attention aux appels de fonctions variadiques et autres subtilités).

cos2πx+jsin2πx=ej2πx=ejπ2x=(1)2x

Ce type d’astuce peut être utilisé pour réduire

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

dans

double y=cpow(1i,x);

(1)x2=j2x2=jx

LATEX

plafondcat
la source
5

Voici quelques conseils que j'ai utilisés à mon avantage. Je les ai volés sans vergogne aux autres, soyez donc reconnaissant envers quiconque sauf moi:

Combiner une affectation avec des appels de fonction

Au lieu de cela:

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

Faire ceci:

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

Initialiser plusieurs variables ensemble (si possible)

Au lieu de cela:

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

Faire ceci:

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

Réduire les valeurs nulles / non nulles

C’est un bon tour que j’ai appris de quelqu'un d’ici (je ne me souviens pas de qui, pardon). Lorsque vous avez une valeur entière et que vous devez la réduire à 1 ou à 0, vous pouvez l'utiliser !!pour le faire facilement. C'est parfois avantageux pour d'autres alternatives comme ?:.

Prenez cette situation:

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

Vous pourriez plutôt faire ceci:

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

Un autre exemple:

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

Pourrait être réécrit comme:

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

Connaître les égalités logiques de base peut permettre de sauver quelques octets. Par exemple, au lieu d' if (!(a&&b)){}essayer d'utiliser la loi de DeMorgan if (!a||!b){}. La même chose s'applique aux fonctions au niveau du bit: au lieu de ~(a|b)faire ~a&~b.

tox123
la source
Cf. Les lois de De Morgan .
Jonathan Frech