Consejos para jugar golf en C

138

¿Qué consejos generales tienes para jugar al golf en C? Estoy buscando ideas que se puedan aplicar a los problemas de golf de código en general que sean al menos algo específicos para C (por ejemplo, "eliminar comentarios" no es una respuesta). Por favor, publique un consejo por respuesta. Además, incluya si su sugerencia se aplica a C89 y / o C99 y si solo funciona en ciertos compiladores.

Casey
fuente
99
Creo que la pista más importante para una sola oración es: Lea los códigos ganadores enviados a IOCCC.
vsz

Respuestas:

107

Use XOR bit a bit para verificar la desigualdad entre enteros:

if(a^b)en lugar de if(a!=b)guardar 1 personaje.

Lowjacker
fuente
73
a-bte da el mismo efecto.
Ugoren
22
Del mismo modo, puede usar en a*blugar de a&&b(tiene una precedencia diferente, puede o no ser malo). Si conoce a / = -b (por ejemplo, no están firmados) entonces a||b==a+b
walpen
3
mejor aún, combínelo con el Operador de Elvis ?:(en lugar de si): por ejemplo, simplemente haga algo si es diferente: a^b?_diff_:;
Olivier Dulac
1
@ OlivierDulac ¿Hay un compilador que acepte una rama ternaria vacía si es falsa?
Jonathan Frech
1
@ OlivierDulac Puedes consultar. Por lo que sé, GCC tiene un ?:operador que es sólo equivalente aa ? a : b
Cromo
75
  • mainLista de argumentos de abuso para declarar una o más variables enteras:

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

    (respuesta al alfabeto en lenguajes de programación )

    Esta solución también abusa del hecho de que a(aka argc) comienza como 1, siempre que se llame al programa sin argumentos.

  • Use variables globales para inicializar cosas a cero:

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

    (¡respuesta a Anagram Code Golf! )

Joey Adams
fuente
62

El operador de coma se puede usar para ejecutar múltiples expresiones en un solo bloque evitando llaves:

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

}

Salidas: 1 2

Casey
fuente
No funciona si una de las declaraciones es break.
Maxim Mikhaylov
99
@MaxLawnboy porque breakes una declaración, y esta respuesta está hablando de expresiones.
NieDzejkob
59

Evite declaraciones catastróficas de tipo argumento-función

Si está declarando una función donde los cinco argumentos son ints, entonces la vida es buena. simplemente puedes escribir

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

Pero supongamos que dnecesita ser un char, o incluso un int*. ¡Entonces estás jodido! Si un parámetro está precedido por un tipo, todos deben ser:

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

¡Pero espera! Hay una forma de evitar esta desastrosa explosión de personajes inútiles. Dice así:

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

Esto incluso ahorra en una maindeclaración estándar si necesita utilizar los argumentos de la línea de comandos:

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

es dos bytes más corto que

main(int c,char**v){

Me sorprendió descubrir esto, ya que hasta ahora no lo he encontrado en PPCG.

Feersum
fuente
66
¿Por qué demonios funciona eso?
Nathaniel
30
Aparentemente, esto se llama estilo K&R y precede a ANSI C por una década.
Dennis
Tenga en cuenta que usar las funciones de K&R y las funciones más nuevas (por ejemplo, '99) juntas puede que no sea posible o no. Depende de tu compilador.
dmckee
55
@dmckee tiene razón. C99 no permite int implícito, por lo que debe usar -std=gnu99y ahora no es portátil. En clc-speak, ni siquiera está escribiendo el código "C" per se, sino "Gnu99-C". 'Por aquí ignoramos eso en su mayoría, pero es bueno mencionarlo si publicas código que es específico del compilador. A veces la gente realmente no descargar y ejecutar estos programas de la nuestra. :)
luser droog
@luserdroog: puede usar -std=c89para decirle a gcc o clang que compile su código de acuerdo con ese estándar anterior, que permite int implícito con solo una advertencia.
Peter Cordes
37

En lugar de> = y <= simplemente puede usar la división entera (/) cuando los valores comparados están por encima de cero, lo que ahorra un carácter. Por ejemplo:

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

Lo cual, por supuesto, todavía es encogible, usando por ejemplo solo> y ^ (una forma inteligente de evitar escribir && o || en algunos casos).

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

El truco de la división de enteros es, por ejemplo, útil para decidir si un número es menor que 100, ya que esto guarda un carácter:

a<100 vs 99/a

Esto también es bueno en los casos en que se necesita una mayor prioridad.

Fors
fuente
Puedes escribirputchar(c>31&c<127?c:46);
Jin X
37

Ciertos compiladores, como GCC, le permiten omitir básicas #includes, parámetro y tipos de devolución de main.

El siguiente es un programa válido C89 y C99 que compila (con advertencias) con GCC:

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

Observe que #includefalta el for stdio.h, falta el tipo de retorno para y mainfalta la declaración de tipo para i.

Casey
fuente
17
Técnicamente no es válido de acuerdo con los estándares, ya que main acepta cero o dos parámetros, no uno. No es que a nadie le importe el golf de código.
Konrad Borowski
Llamar printf()(o cualquier función variada) sin un prototipo provoca un comportamiento indefinido . GCC no compila el estándar C por defecto. Si invoca gcc en modo C89 ( gcc -ansi -pedantic) o modo C99 ( gcc -std=c99 -pedantic), recibirá bastantes quejas, al menos en el último caso.
Nisse Engström
@ NisseEngström: las convenciones de llamadas en implementaciones de C convencionales hacen que sea seguro llamar a funciones variadas sin prototipos. Entonces, la mayoría de las implementaciones de C definen el comportamiento.
Peter Cordes
29

El operador condicional ternario ?:menudo se puede utilizar como soporte en por simples if- elsedeclaraciones en un ahorro considerable.

A diferencia del equivalente de c ++, el operador no produce formalmente un valor l , pero algunos compiladores (especialmente gcc) le permitirán salirse con la suya, lo cual es una buena ventaja.

dmckee
fuente
Adición: si solo necesita un if, pero no otro, el ternario aún puede ser útil.
Casey
99
&&y ||también se puede usar: se if(x==3)f()convierte con su sugerencia x==3?f():0, y se puede mejorar aún más x==3&&f(). Pero tenga cuidado con la precedencia del operador: si f()se reemplaza con y=1, entonces la &&solución requiere un conjunto adicional de paréntesis.
ugoren
1
Nunca me había dado cuenta de que gcc ?:produce un valor l. ¿Puedo usar eso en el código de producción? lol
Jeff Burdges
44
@ugoren: x==3&&f()se puede seguir jugando al x^3||f()
golf
@fgrieu, sí, aunque no es exactamente el tema aquí ( esta respuesta lo sugiere).
ugoren
27

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

Los bits son buenos.

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

Pero con diferentes precedentes, y no cambies x como ++ y -. También puede usar esto en casos realmente específicos: ~ 9 es más corto que -10.

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

Eso es más esotérico, pero he tenido la ocasión de usarlo. Si no te importa el cortocircuito

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

También:

if(x>0 && y>0) x/y == x>=y   
Walpen
fuente
55
El último consejo ( (x/y) == (x>=y)) es realmente útil.
ugoren
24

Use lambdas (no portable)

En lugar de

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

o (solo gcc)

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

o (llvm con soporte de bloques)

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

prueba algo como

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

... donde la cadena entre comillas contiene las instrucciones del lenguaje máquina de su función "lambda" (conforme a todos los requisitos ABI de la plataforma).

Esto funciona en entornos en los que las constantes de cadena se marcan como ejecutables. Por defecto, esto es cierto en Linux y OSX pero no en Windows.

Una forma tonta de aprender a escribir sus propias funciones "lambda" es escribir la función en C, compilarla, inspeccionarla con algo parecido objdump -Dy copiar el código hexadecimal correspondiente en una cadena. Por ejemplo,

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

... cuando se compila gcc -Os -cpara un objetivo Linux x86_64 genera algo como

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

GNU CC goto:

Puede llamar a estas "funciones lambda" directamente, pero si el código al que llama no toma parámetros y no va a regresar, puede usar gotopara guardar algunos bytes. Entonces en lugar de

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

o (si su entorno no tiene glifos árabes)

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

Tratar

goto*&L"ﻫ";

o

goto*&L"\xfeeb";

En este ejemplo, eb fees lenguaje de máquina x86 para algo así for(;;);y es un ejemplo simple de algo que no toma parámetros y no va a volver :-)

Resulta que puede gotocodificar que vuelve a un padre que llama.

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

El ejemplo anterior (podría compilarse y ejecutarse en Linux con gcc -O) es sensible al diseño de la pila.

EDITAR: Dependiendo de su cadena de herramientas, es posible que deba usar el -zexecstackindicador de compilación.

Si no es evidente de inmediato, esta respuesta se escribió principalmente para los lols. No me hago responsable de jugar golf mejor o peor o de resultados psicológicos adversos al leer esto.

techo
fuente
2
Acabo de escribir un script para leer partes de una función C desde el estándar e imprimir una lambda C. Puede que valga la pena mencionarlo en su respuesta, podría ser agradable para usted verlo ya que me enseñó a hacer esto en primer lugar.
MD XF
23

Use cursores en lugar de punteros. Enganche el brk()al principio y úselo como puntero base .

char*m=brk();

Luego haga un #define para acceder a la memoria.

#define M [m]

Mse convierte en un postfix *aplicado a enteros. (El viejo truco de a [x] == x [a]).

¡Pero hay más! Entonces puede tener argumentos y retornos de puntero en funciones que son más cortas que las macros (especialmente si abrevia 'return'):

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

Para hacer un cursor desde un puntero, resta el puntero base, produciendo un ptrdiff_t, que se trunca en int, las pérdidas son su negocio.

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

Esta técnica se utiliza en mi respuesta para Escribir un intérprete para el cálculo lambda sin tipo .

luser droog
fuente
21

Definir parámetros en lugar de variables.

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

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

No necesita pasar el segundo parámetro.

Además, puede utilizar la precedencia del operador para guardar paréntesis.
Por ejemplo, (x+y)*2puede convertirse x+y<<1.

Ugoren
fuente
O simplemente x+y*2, ahorrando otro personaje más.
Braden Best
44
@ B1KMusic, x+y*2no es lo mismo, debido a la precedencia del operador.
ugoren
Bien, jajaja. Eso sería x + (y * 2). Estaba obsesionado con el x+y<<1ejemplo, asumiendo que estaba siendo evaluado como x+(y<<1), y sugerí el *2lugar. No sabía que las operaciones de desplazamiento de bits se evaluaran como, por ejemplo(x+y)<<2
Braden Best
20

Dado que por lo general EOF == -1, utilizar el bit a bit NO operador para comprobar si hay EOF: while(~(c=getchar()))o while(c=getchar()+1)y modificar el valor de c en todos los lugares

Lowjacker
fuente
1
No sé C lo suficientemente bien, pero ¿no while(1+c=getchar())funcionaría?
4ıʇǝɥʇuʎs
66
@ ɐɔıʇǝɥʇuʎs No. El operador de suma +tiene mayor precedencia que el operador de asignación =, por lo que 1+c=getchar()es equivalente a (1+c)=getchar(), que no se compila porque (1+c)no es un valor l.
ace_HongKongIndependence
19

El operador ternario ?:es inusual porque tiene dos piezas separadas. Debido a esto, proporciona un poco de escapatoria a las reglas de precedencia de operadores estándar. Esto puede ser útil para evitar paréntesis.

Tome el siguiente ejemplo:

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

El enfoque de golf habitual es reemplazar el ifcon &&, pero debido a la baja precedencia del operador de coma, necesita un par adicional de paréntesis:

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

Sin embargo, la sección central del operador ternario no necesita paréntesis:

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

Comentarios similares se aplican a los subíndices de matriz.

caja de pan
fuente
77
En este ejemplo, b-=a=bes aún más corto. El ?:truco sigue siendo útil, -=porque también tiene poca preferencia.
Ugoren
Buen punto; mi ejemplo fue innecesariamente complejo.
breadbox
Otro punto es que a veces uno quiere dar la vuelta al Estado: A x>0||(y=3), x>0?0:(y=3)no sirve para nada, pero x<1?y=3:0hace el trabajo.
ugoren
tanto clang como gcc permiten un verdadero caso vacío en el ternario. Si se omite, su valor es el valor de la condición. Por ejemplo,x>5?:y=1
Chris Uzdavinis
19

Cualquier parte de su código que se repita varias veces es un candidato para el reemplazo con el preprocesador.

#define R return

es un caso de uso muy común si su código involucra más de un par de funciones. Otras palabras clave bastante largas como while, double, switch, y caseson también candidatos; así como cualquier cosa que sea idomática en su código.

Generalmente reservo caracteres en mayúscula para este propósito.

dmckee
fuente
1
Un reemplazo más corto sería -DR=return. Tenga en cuenta que si incluye ciertos caracteres, puede ser necesario tener comillas simples o dobles alrededor de la definición -DP='puts("hello")'.
15

Si su programa lee o escribe en cada paso, intente siempre usar la función de lectura y escritura en lugar de getchar () y putchar () .

Ejemplo ( revertir stdin y colocar en stdout )

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

Ejercicio: use esta técnica para obtener una buena puntuación aquí .

Quijotesco
fuente
¿Qué quieres decir con en cada paso ?
Casey
Casey: Supongo que se refieren a si el programa está leyendo algo, opera en él y escribe la salida. De manera continua, por así decirlo. A diferencia de un enfoque en el que todas las entradas deben leerse y manejarse a la vez.
Joey
Joey tiene razón, quise decir lo mismo, lo siento, no revisé mi bandeja de entrada hasta hoy.
Quijotesco
8
Esa manipulación de la pila es hermosa.
Andrea Biondo
14

Bucles inversos

Si puedes, intenta reemplazar

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

con

for(int i=n;i--;){...}
techo
fuente
13

Si alguna vez necesita generar un solo carácter de nueva línea ( \n), no use putchar(10), use puts("").

ace_HongKongIndependence
fuente
12

Hacer uso de valores de retorno a cero cosas. Si llama a alguna función, y esa función devuelve cero en condiciones normales, puede colocarla en una ubicación donde se espera cero. Del mismo modo, si sabe que la función devolverá un valor distinto de cero, con la adición de una explosión. Después de todo, en cualquier caso, no manejas los errores correctamente en un código de golf, ¿verdad?

Ejemplos:

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

Asignar en lugar de devolver.

Esto no es realmente C estándar, pero funciona con todos los compiladores y CPU que conozco:

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

tiene el mismo efecto que:

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

Porque el primer argumento se almacena en el mismo registro de CPU que el valor de retorno.

Nota: Como se señaló en un comentario, este es un comportamiento indefinido y no se garantiza que funcione para todas las operaciones. Y cualquier optimización del compilador lo omitirá.

Macros X

Otra característica útil: X-Macros puede ayudarlo cuando tiene una lista de variables y necesita realizar alguna operación que involucre a todas ellas:

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

GB
fuente
3
Cité esto y me corrigieron. Esto simplemente no es cierto. Solo funcionará con multiplicaciones y divisiones y cuando las optimizaciones estén desactivadas. Esto se debe a que ambas operaciones ponen sus resultados en eax, que es el registro común para el retorno. Los parámetros se almacenan en la pila o ecx o edx. Inténtalo tú mismo.
Gaspa79
3
Tienes razón, es un comportamiento indefinido, también depende del compilador y de la arquitectura, generalmente verifico con gcc en x86 y armv7 antes de publicar cualquier respuesta usando este truco. Y, por supuesto, si habilita la optimización, cualquier compilador inteligente simplemente eliminaría la multiplicación innecesaria.
GB
3
He visto este trabajo con GCC pero no con otros
Albert Renshaw
1
@ Gaspa79: gcc con -O0siempre elige evaluar expresiones en el registro de valor de retorno. He visto x86, ARM y MIPS al menos (en gcc.godbolt.org ), y parece que gcc hace todo lo posible para hacerlo -O0. Pero recuerde que si usted toma ventaja de esto, el lenguaje de programación que está en decir gcc -O0, no C , y se debe etiquetar su respuesta en consecuencia, no como C . Falla en cualquier nivel de optimización que no sea el -O0modo de depuración, y no funciona con clang IIRC.
Peter Cordes
11
  1. Use en *alugar de a[0]para acceder al primer elemento de una matriz.

  2. Los operadores relacionales ( !=, >, etc.) dan 0o 1. Use esto con operadores aritméticos para dar diferentes compensaciones dependiendo de si la condición es verdadera o falsa: a[1+2*(i<3)]accedería a[1]si i >= 3y de a[3]otra manera.

es1024
fuente
11
a[i<3?3:1]son dos caracteres más cortos que a[1+2*(i<3)].
Reto Koradi
10

Puede consultar los archivos de IOCCC (concurso internacional de códigos C ofuscados).

Un truco notable es #definir macros cuya expansión tiene paréntesis / llaves desequilibradas, como

#define P printf(
Andreas Krey
fuente
16
Los paréntesis no coincidentes no tienen valor en sí mismos. El punto es definir la mayor cantidad posible del patrón repetitivo. Es posible que desee ir más allá, con #define P;printf(.
Ugoren
¿Cómo se acorta el conteo de bytes? Quizás dar un ejemplo?
Cyoce
2
@Cyoce Vea, por ejemplo, esta respuesta .
Jonathan Frech
8

for(int i=0;i<n;i++){a(i);b(i);} se puede acortar de varias maneras:

for(int i=0;i<n;){a(i);b(i++);} -1 para mover ++al último ien el bucle

for(int i=0;i<n;b(i++))a(i); -3 más para mover todas las declaraciones menos una a la parte superior y fuera del bucle principal, eliminando las llaves

MegaTom
fuente
Usar el operador de coma es otra forma de evitar llaves en algunos casos.
Peter Cordes
8

¡Vaya funcional!

Si puede reducir su problema a funciones simples con la misma firma y definidas como expresiones individuales, entonces puede hacerlo mejor #define r returny factorizar casi todo el estándar para definir una función.

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

El resultado del programa es su valor de estado devuelto al sistema operativo o al shell de control o IDE.

El uso le __VA_ARGS__permite utilizar el operador de coma para introducir puntos de secuencia en estas expresiones de función . Si esto no es necesario, la macro puede ser más corta.

#define D(f,b)f(x){return b;}
luser droog
fuente
7
  1. se usa scanf("%*d ");para leer la entrada ficticia. (en caso de que la entrada no tenga sentido en otro programa) es más corta que scanf("%d",&t);donde también necesita declarar la variable t.

  2. almacenar caracteres en la matriz int es mucho mejor que la matriz de caracteres. ejemplo.

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

Neeraj Gupta
fuente
2
En realidad, yo uso %*dno sólo en Golf, ya que también es útil en situaciones en las que uno podría, por ejemplo, quiera saltarse una nueva línea en scanf("%[^\n]%*c",str);:)
tomsmeding
6

Imprima un carácter y luego retorno de carro, en lugar de:

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

o

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

simplemente, declare c como int y:

puts(&c);
moala
fuente
99
Probablemente valga la pena señalar que esto depende de una arquitectura little-endian. Si c es un int grande-endiano, entonces solo obtendrá el retorno de carro. (Por otro lado, si c es un char, es posible que obtenga basura aleatoria después en lugar de un retorno de carro.)
breadbox
@breadbox sí, tienes toda la razón; Acabo de editar: el último extracto debe usar c como int (que con frecuencia es fácil de declarar como tal).
moala
¿ puts(&c)Realmente funciona? Eso no sería necesariamente nulo terminado.
Esolanging Fruit
1
@EsolangingFruit En little-endian con entradas de 32 bits, un int 0 ≤ c <256 se almacena como la secuencia de bytes c 0 0 0 . Cuando interpretamos la dirección de c como char *, vemos una cadena singleton: el carácter c , seguido de un byte nulo.
Dennis
6

¡El uso le asprintf()ahorra la asignación explícita y también mide la longitud de una cadena aka char*! Esto quizás no sea demasiado útil para el golf de código, pero facilita el trabajo diario con una matriz de caracteres. Hay algunos más buenos consejos en el siglo XXI .

Ejemplo de uso:

#define _GNU_SOURCE
#include <stdio.h>

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

import si usted tiene que

Como se señaló en la primera respuesta , algunos compiladores (en particular, GCC y clang) le permiten omitir #includes para las funciones estándar de la biblioteca.

Incluso si no puede simplemente eliminarlo #include, puede haber otras formas de evitarlo , pero eso no siempre es práctico o particularmente deportivo.

En los casos restantes, puede usar en #import<header file>lugar de #include<header file>guardar un byte. Esta es una extensión de GNU y se considera obsoleta, pero funciona al menos en gcc 4.8, gcc 5.1 y clang 3.7.

Dennis
fuente
6

Probar en cpow()lugar decos()

En lugar de

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

prueba algo como

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

Utiliza la fórmula de Euler , un pequeño análisis complejo y la observación de que asignar un complejo a un doble produce la parte real (cuidado con las llamadas a funciones variables y otras sutilezas).

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

Este tipo de truco se puede usar para reducir

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

dentro

double y=cpow(1i,x);

(1)x2=j2x2=jx

LATEX

techo
fuente
5

Aquí hay algunos consejos que he usado para mi ventaja. Los robé descaradamente a otros, así que dale crédito a todos menos a mí:

Combinar asignación con llamadas a funciones

En lugar de esto:

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

Hacer esto:

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

Inicializar múltiples variables juntas (cuando sea posible)

En lugar de esto:

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

Hacer esto:

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

Contraer valores cero / distintos de cero

Este es un buen truco que aprendí de alguien aquí (no recuerdo quién, lo siento). Cuando tiene un valor entero y necesita contraerlo a 1 o 0, puede !!hacerlo fácilmente. Esto a veces es ventajoso para otras alternativas como ?:.

Toma esta situación:

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

En su lugar, podría hacer esto:

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

Otro ejemplo:

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

Podría reescribirse como:

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

Conocer las igualdades lógicas básicas podría salvar un par de bytes. Por ejemplo, en lugar de if (!(a&&b)){}intentar usar la ley de DeMorgan if (!a||!b){}. Lo mismo se aplica a las funciones bit a bit: en lugar de ~(a|b)do ~a&~b.

tox123
fuente
Cf. Las leyes de De Morgan .
Jonathan Frech