Por ejemplo, recientemente me encontré con esto en el kernel de Linux:
/ * Forzar un error de compilación si la condición es verdadera * / #define BUILD_BUG_ON (condición) ((nulo) sizeof (char [1 - 2 * !! (condición)]))
Entonces, en su código, si tiene alguna estructura que debe ser, digamos un múltiplo de 8 bytes de tamaño, tal vez debido a algunas restricciones de hardware, puede hacer:
BUILD_BUG_ON ((sizeof (struct mystruct)% 8)! = 0);
y no se compilará a menos que el tamaño de struct mystruct sea un múltiplo de 8, y si es un múltiplo de 8, no se generará ningún código de tiempo de ejecución.
Otro truco que conozco es del libro "Graphics Gems", que permite que un solo archivo de encabezado declare e inicialice variables en un módulo mientras que en otros módulos que usan ese módulo, simplemente los declara como externos.
#ifdef DEFINE_MYHEADER_GLOBALS #define GLOBAL #definir INIT (x, y) (x) = (y) #más #definir GLOBAL externo #definir INIT (x, y) #terminara si GLOBAL int INIT (x, 0); GLOBAL int somefunc (int a, int b);
Con eso, el código que define xy somefunc hace:
#define DEFINE_MYHEADER_GLOBALS #include "the_above_header_file.h"
mientras que el código que simplemente usa xy somefunc () hace:
#include "the_above_header_file.h"
Por lo tanto, obtiene un archivo de encabezado que declara ambas instancias de prototipos globales y de función donde se necesitan, y las correspondientes declaraciones externas.
Entonces, ¿cuáles son tus trucos de programación C favoritos en ese sentido?
BUILD_BUG_ON
macro, ¿qué tiene de malo usar#error
dentro y#if
?Respuestas:
C99 ofrece algunas cosas realmente geniales usando matrices anónimas:
Eliminar variables sin sentido
se convierte
Pasar una cantidad variable de argumentos
Listas enlazadas estáticas
Estoy seguro de que hay muchas otras técnicas geniales que no he pensado.
fuente
&(int){1}
, si desea dejar un poco más claro cuál es su intención aquí.Mientras leía el código fuente de Quake 2 se me ocurrió algo como esto:
(más o menos, no tengo el código a mano para verificarlo ahora).
Desde entonces, un nuevo mundo de uso creativo del preprocesador se abrió ante mis ojos. Ya no incluyo solo encabezados, sino fragmentos enteros de código de vez en cuando (mejora mucho la reutilización) :-p
Gracias John Carmack! xD
fuente
Me gusta usar
= {0};
para inicializar estructuras sin necesidad de llamar a memset.Esto inicializará todos los miembros de la estructura (o matriz) a cero (pero no los bytes de relleno; use memset si necesita ponerlos a cero también).
Pero debe tener en cuenta que hay algunos problemas con esto para estructuras grandes y dinámicamente asignadas .
fuente
const struct something zero_something = { 0 };
y luego puedo restablecer una variable sobre la marchastruct something X = zero_something;
o a medio camino a través de una rutina, puedo usar 'X = zero_something;'. La única objeción posible es que implica leer datos de alguna parte; en estos días, un 'memset ()' podría ser más rápido, pero me gusta la claridad de la asignación, y también es posible usar valores distintos de cero en el inicializador (y memset () seguido de ajustes para miembros individuales puede ser más lento que una simple copia).¡Si hablamos de trucos en C, mi favorito tiene que ser el Dispositivo de Duff para desenrollar el bucle! Solo estoy esperando la oportunidad adecuada para que lo use en realidad con ira ...
fuente
utilizando
__FILE__
y__LINE__
para depurarfuente
__FUNCTION__
es solo un alias para__func__
, y__func__
está en c99. Bastante práctico.__PRETTY_FUNCTION__
en C (GCC) es solo otro alias para__func__
, pero en C ++ obtendrá la firma de la función completa.En C99
fuente
Una vez que un compañero mío y yo redefinimos volver a encontrar un error de corrupción de pila complicado.
Algo como:
fuente
DoSomeStackCheckStuff
a las funciones que desea rastrear.#define return if((DoSomeStackCheckStuff) && 0) ; else return
... ¡tan loco como supongo!Me gusta el "truco de estructura" por tener un objeto de tamaño dinámico. Este sitio también lo explica bastante bien (aunque se refieren a la versión C99 donde puede escribir "str []" como el último miembro de una estructura). podrías hacer un "objeto" de cadena como este:
aquí, hemos asignado una estructura de tipo X en el montón que es del tamaño de un int (para len), más la longitud de "hello world", más 1 (desde str 1 está incluido en el tamaño de (X).
Generalmente es útil cuando desea tener un "encabezado" justo antes de algunos datos de longitud variable en el mismo bloque.
fuente
str[1]
(nostr[]
) el 1 byte de str está incluido en elsizeof(struct X)
. Esto incluye cualquier relleno entrelen
ystr
.str
. OK, cuando asignosizeof(struct X) + 10
Entonces esto hacestr
efectivo10 - sizeof(int)
(o más, ya que dijimos que hay relleno) grande. Esto se superponestr
y cualquier relleno posterior. La única forma en que tendría alguna diferencia es que si hubiera un miembro después de lostr
cual se rompe todo de todos modos, los miembros flexibles deben ser los últimos. Cualquier relleno al final solo posiblemente causará que se asigne demasiado. Proporcione un ejemplo específico de cómo podría salir mal.Código orientado a objetos con C, emulando clases.
Simplemente cree una estructura y un conjunto de funciones que toman un puntero a esa estructura como primer parámetro.
fuente
En vez de
Utilizar
fuente
Usar un estúpido truco macro para hacer que las definiciones de registros sean más fáciles de mantener.
fuente
Para crear una variable que es de solo lectura en todos los módulos, excepto en el que se declara:
fuente
Source2.c
, el compilador puede suponer que esoMyVar
no cambia, incluso a través de una llamada a la funciónSource1.c
. (Tenga en cuenta que esto, como una variable const real, difiere de un puntero a const. En este último caso, el objeto señalado aún podría modificarse a través de un puntero diferente.)Los cambios de bits solo se definen hasta una cantidad de desplazamiento de 31 (en un entero de 32 bits).
¿Qué hacer si desea tener un turno calculado que también necesita trabajar con valores de turno más altos? Así es como lo hace el códec de video Theora:
O mucho más legible:
Realizar la tarea de la manera que se muestra arriba es mucho más rápido que usar una rama como esta:
fuente
v
, mientras que elhalfshift
truco solo duplica el rango permitido a 63 en una arquitectura de 32 bits y 127 en una de 64 bits.Declarar conjuntos de punteros a funciones para implementar máquinas de estados finitos.
La ventaja más agradable es que es simple forzar a cada estímulo / estado a verificar todas las rutas de código.
En un sistema embebido, a menudo mapeo un ISR para apuntar a dicha tabla y revelarla según sea necesario (fuera del ISR).
fuente
Otro buen "truco" del preprocesador es usar el carácter "#" para imprimir expresiones de depuración. Por ejemplo:
editar: el siguiente código solo funciona en C ++. Gracias a smcameron y Evan Teran.
Sí, la afirmación del tiempo de compilación siempre es excelente. También se puede escribir como:
fuente
Realmente no lo llamaría un truco favorito, ya que nunca lo he usado, pero la mención del Dispositivo de Duff me recordó este artículo sobre la implementación de Coroutines en C. Siempre me da una sonrisa, pero estoy seguro de que podría Ser útil alguna vez.
fuente
static
variable, sino que asigno una estructura dinámicamente y paso un puntero a eso en la función de la rutina. Un montón de macros hacen que esto sea más apetecible. No es agradable, pero mejor que la versión asíncrona / de devolución de llamada que salta por todos lados. Sinswapcontext()
embargo , usaría hilos verdes (a través de * nixes) si pudiera.El tiempo (0); no tiene ningún efecto en el programa, pero el compilador emitirá una advertencia sobre "esto no hace nada", lo cual es suficiente para que mire la línea ofensiva y luego vea la verdadera razón por la que quería llamar la atención.
fuente
Soy fan de los hacks de xor:
Cambie 2 punteros sin el tercer puntero temporal:
O realmente me gusta la lista enlazada xor con un solo puntero. (http://en.wikipedia.org/wiki/XOR_linked_list)
Cada nodo en la lista vinculada es el Xor del nodo anterior y el siguiente nodo. Para avanzar, la dirección de los nodos se encuentra de la siguiente manera:
etc.
o para atravesar hacia atrás:
etc.
Si bien no es terriblemente útil (no puede comenzar a desplazarse desde un nodo arbitrario), creo que es muy bueno.
fuente
Este viene del libro 'Suficiente cuerda para dispararte en el pie':
En el encabezado declaramos
En su código, coloque las declaraciones de prueba, por ejemplo:
El do / while ayuda en caso de que el contenido de la macro se expanda a múltiples declaraciones.
La declaración solo se imprimirá si no se usa el indicador '-D RELEASE' para el compilador.
Entonces puedes, por ejemplo. pasar la bandera a su archivo MAKE, etc.
No estoy seguro de cómo funciona esto en Windows, pero en * nix funciona bien
fuente
#define D(x) do { } while(0)
lugar maneja ese caso (y se puede aplicar a la rama que también se insertax
para mayor consistencia)Rusty realmente produjo un conjunto completo de condicionales de compilación en ccan , consulte el módulo de afirmación de compilación:
Hay muchas otras macros útiles en el encabezado real, que son fáciles de colocar en su lugar.
Intento, con todas mis fuerzas, resistir la atracción del lado oscuro (y el abuso del preprocesador) apegándome principalmente a las funciones en línea, pero disfruto de macros inteligentes y útiles como las que describiste.
fuente
Dos buenos libros fuente para este tipo de cosas son La práctica de programar y escribir código sólido . Uno de ellos (no recuerdo cuál) dice: prefiera enum a #define donde pueda, porque el compilador verifica enum.
fuente
No es específico de C, pero siempre me ha gustado el operador XOR. Una cosa genial que puede hacer es "intercambiar sin un valor temporal":
Resultado:
fuente
Consulte la pregunta "Características ocultas de C" .
fuente
Me gusta el concepto de
container_of
utilizado, por ejemplo, en listas. Básicamente, no necesita especificarnext
ylast
campos para cada estructura que estará en la lista. En su lugar, agrega el encabezado de la estructura de la lista a los elementos vinculados reales.Echa un vistazo a
include/linux/list.h
ejemplos de la vida real.fuente
Creo que el uso de punteros de datos de usuario es bastante bueno. Una moda que pierde terreno hoy en día. No es tanto una característica de C, pero es bastante fácil de usar en C.
fuente
Utilizo X-Macros para permitir que el precompilador genere código. Son especialmente útiles para definir valores de error y cadenas de error asociadas en un solo lugar, pero pueden ir mucho más allá de eso.
fuente
Nuestra base de código tiene un truco similar a
que permite el seguimiento de pérdidas de memoria en modo de depuración. Siempre pensé que esto era genial.
fuente
Diversión con macros:
fuente
Aquí hay un ejemplo de cómo hacer que el código C sea completamente inconsciente de lo que realmente se usa de HW para ejecutar la aplicación. Main.c realiza la configuración y luego la capa libre se puede implementar en cualquier compilador / arco. Creo que es bastante bueno para abstraer un poco el código C, por lo que no puede ser demasiado específico.
Agregar un ejemplo compilable completo aquí.
fuente
Complete los espacios en blanco para que ni hola ni hola aparezcan en la salida.
ans:
fclose(stdout)
fuente
{}
botón de la barra de herramientas (lo he hecho por usted). El botón "Citar" no mantiene espacios en blanco ni aplica resaltado de sintaxis.