¿Cuál es tu truco de progtwigción C favorito?

Por ejemplo, recientemente encontré esto en el kernel de Linux:

 / * Forzar un error de comstackción si la condición es verdadera * /
 #define BUILD_BUG_ON (condición) ((void) sizeof (char [1 - 2 * !! (condition)]))

Entonces, en su código, si tiene alguna estructura que debe ser, digamos un tamaño de 8 bytes, tal vez debido a algunas restricciones de hardware, puede hacer:

 BUILD_BUG_ON ((sizeof (struct mystruct)% 8)! = 0);

y no se comstackrá a menos que el tamaño de la estructura mystruct sea un múltiplo de 8, y si es un múltiplo de 8, no se genera 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
 # define INIT (x, y) (x) = (y)
 #más
 #define GLOBAL extern
 # define INIT (x, y)
 #terminara si

 INTO GLOBAL (x, 0);
 GLOBAL int somefunc (int a, int b);

Con eso, el código que define x y somefunc hace:

 #define DEFINE_MYHEADER_GLOBALS
 #include "the_above_header_file.h"

mientras que el código que simplemente usa x y somefunc () hace:

 #include "the_above_header_file.h"

De este modo, obtiene un archivo de encabezado que declara tanto las instancias de prototipos globales como de funciones donde se necesitan, y las declaraciones externas correspondientes.

Entonces, ¿cuáles son tus trucos favoritos de progtwigción en C a lo largo de esas líneas?

C99 ofrece algunas cosas realmente geniales usando arreglos anónimos:

Eliminar variables sin sentido

 { int yes=1; setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); } 

se convierte en

 setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int)); 

Pasando una cantidad variable de argumentos

 void func(type* values) { while(*values) { x = *values++; /* do whatever with x */ } } func((type[]){val1,val2,val3,val4,0}); 

Listas enlazadas estáticas

 int main() { struct llist { int a; struct llist* next;}; #define cons(x,y) (struct llist[]){{x,y}} struct llist *list=cons(1, cons(2, cons(3, cons(4, NULL)))); struct llist *p = list; while(p != 0) { printf("%d\n", p->a); p = p->next; } } 

Cualquiera que sea, estoy seguro de muchas otras técnicas geniales que no he pensado.

Mientras leía el código fuente de Quake 2, se me ocurrió algo como esto:

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

(Más o menos, no tengo el código a mano para comprobarlo ahora).

Desde entonces, un nuevo mundo de uso creativo del preprocesador se abrió ante mis ojos. Ya no incluyo solo encabezados, sino partes enteras de código de vez en cuando (mejora mucho la reutilización) :-p

Gracias John Carmack! xD

Me gusta usar = {0}; Para inicializar estructuras sin necesidad de llamar a memset.

 struct something X = {0}; 

Esto inicializará a todos los miembros de la estructura (o matriz) a cero (pero no a cualquier octeto de relleno; use memset si también necesita poner a cero esos).

Pero debe tener en cuenta que hay algunos problemas con esto para estructuras grandes asignadas dinámicamente .

Si estamos hablando de trucos c, ¡mi favorito tiene que ser el dispositivo de Duff para el desenrollado de bucles! Solo estoy esperando la oportunidad correcta de venir conmigo para que realmente lo use enojado …

usando __FILE__ y __LINE__ para depurar

 #define WHERE fprintf(stderr,"[LOG]%s:%d\n",__FILE__,__LINE__); 

En c99

 typedef struct{ int value; int otherValue; } s; s test = {.value = 15, .otherValue = 16}; /* or */ int a[100] = {1,2,[50]=3,4,5,[23]=6,7}; 

Una vez que un compañero mío y yo redefinimos el retorno para encontrar un error de corrupción de stack complicado.

Algo como:

 #define return DoSomeStackCheckStuff, return 

Me gusta la “estructura hackear” por tener un objeto de tamaño dynamic. 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 una cadena de “objeto” como esta:

 struct X { int len; char str[1]; }; int n = strlen("hello world"); struct X *string = malloc(sizeof(struct X) + n); strcpy(string->str, "hello world"); string->len = n; 

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 “hola mundo”, más 1 (ya que str 1 está incluido en el tamaño de (X).

En general, es útil cuando se desea tener un “encabezado” justo antes de algunos datos de longitud variable en el mismo bloque.

Código orientado a objetos con C, mediante la emulación de clases.

Simplemente cree una estructura y un conjunto de funciones que lleven un puntero a esa estructura como primer parámetro.

En lugar de

 printf("counter=%d\n",counter); 

Utilizar

 #define print_dec(var) printf("%s=%d\n",#var,var); print_dec(counter); 

Usar un truco de macro estúpido para facilitar el mantenimiento de las definiciones de registro.

 #define COLUMNS(S,E) [(E) - (S) + 1] typedef struct { char studentNumber COLUMNS( 1, 9); char firstName COLUMNS(10, 30); char lastName COLUMNS(31, 51); } StudentRecord; 

Para crear una variable que sea de solo lectura en todos los módulos excepto en el que se declara en:

 // Header1.h: #ifndef SOURCE1_C extern const int MyVar; #endif 

 // Source1.c: #define SOURCE1_C #include Header1.h // MyVar isn't seen in the header int MyVar; // Declared in this file, and is writeable 

 // Source2.c #include Header1.h // MyVar is seen as a constant, declared elsewhere 

Los desplazamientos de bits solo se definen hasta una cantidad de desplazamiento de 31 (en un entero de 32 bits).

¿Qué debe hacer si desea tener un turno computado que también necesite trabajar con valores de turno más altos? Así es como lo hace Theora vide-codec:

 unsigned int shiftmystuff (unsigned int a, unsigned int v) { return (a>>(v>>1))>>((v+1)>>1); } 

O mucho más legible:

 unsigned int shiftmystuff (unsigned int a, unsigned int v) { unsigned int halfshift = v>>1; unsigned int otherhalf = (v+1)>>1; return (a >> halfshift) >> otherhalf; } 

Realizar la tarea como se muestra arriba es mucho más rápido que usar una twig como esta:

 unsigned int shiftmystuff (unsigned int a, unsigned int v) { if (v<=31) return a>>v; else return 0; } 

Declaración de matriz de puntero a funciones para implementar máquinas de estados finitos.

 int (* fsm[])(void) = { ... } 

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 integrado, a menudo asigno un ISR para apuntar a dicha tabla y revisarlo según sea necesario (fuera del ISR).

Otro buen “truco” del preprocesador es usar el carácter “#” para imprimir expresiones de depuración. Por ejemplo:

 #define MY_ASSERT(cond) \ do { \ if( !(cond) ) { \ printf("MY_ASSERT(%s) failed\n", #cond); \ exit(-1); \ } \ } while( 0 ) 

editar: el código siguiente solo funciona en C ++. Gracias a Smcameron y Evan Teran.

Sí, el tiempo de comstackción es siempre bueno. También se puede escribir como:

 #define COMPILE_ASSERT(cond)\ typedef char __compile_time_assert[ (cond) ? 0 : -1] 

Realmente no lo llamaría mi truco favorito, ya que nunca lo usé, pero la mención de Duff’s Device me recordó este artículo sobre la implementación de Coroutines en C. Siempre me da una risita, pero estoy seguro de que podría Ser útil algún tiempo.

 #if TESTMODE == 1 debug=1; while(0); // Get attention #endif 

El tiempo (0); no tiene ningún efecto en el progtwig, pero el comstackdor emitirá una advertencia sobre “esto no hace nada”, lo cual es suficiente para que vea la línea ofensiva y luego vea la razón real por la que quería llamar la atención.

Soy fan de xor hacks:

Intercambiar 2 punteros sin puntero tercer temp:

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

O me gusta mucho la lista de enlaces xor con un solo puntero. (http://en.wikipedia.org/wiki/XOR_linked_list)

Cada nodo en la lista enlazada es el Xor del nodo anterior y el siguiente nodo. Para avanzar, la dirección de los nodos se encuentra de la siguiente manera:

 LLNode * first = head; LLNode * second = first.linked_nodes; LLNode * third = second.linked_nodes ^ first; LLNode * fourth = third.linked_nodes ^ second; 

etc.

o atravesar hacia atrás:

 LLNode * last = tail; LLNode * second_to_last = last.linked_nodes; LLNode * third_to_last = second_to_last.linked_nodes ^ last; LLNode * fourth_to_last = third_to_last.linked_nodes ^ second_to_last; 

etc.

Aunque no es terriblemente útil (no se puede comenzar a atravesar desde un nodo arbitrario) me parece muy bueno.

Éste viene del libro ‘Cuerda suficiente para dispararte en el pie’:

En el encabezado declare.

 #ifndef RELEASE # define D(x) do { x; } while (0) #else # define D(x) #endif 

En su código coloque declaraciones de prueba, por ejemplo:

 D(printf("Test statement\n")); 

La ayuda do / while en caso de que el contenido de la macro se expanda a varias declaraciones.

La statement solo se imprimirá si no se utiliza el indicador ‘-D RELEASE’ para el comstackdor.

Usted puede por ejemplo. pasar la bandera a su makefile etc.

No estoy seguro de cómo funciona esto en Windows pero en * nix funciona bien

Rusty en realidad produjo un conjunto completo de condicionales de comstackción en ccan , revise el módulo de creación de aserciones:

 #include  #include  struct foo { char string[5]; int x; }; char *foo_string(struct foo *foo) { // This trick requires that the string be first in the structure BUILD_ASSERT(offsetof(struct foo, string) == 0); return (char *)foo; } 

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) mediante la mayoría de las funciones en línea, pero disfruto de macros inteligentes y útiles como las que usted describió.

Dos buenos libros de fonts para este tipo de cosas son La práctica de la progtwigción y la escritura de código sólido . Uno de ellos (no recuerdo cuál) dice: Prefiere enumerar a # definir donde puedes, porque el comstackdor controla la enumeración.

No es específico de C, pero siempre me ha gustado el operador XOR. Una cosa interesante que puede hacer es “cambiar sin un valor temporal”:

 int a = 1; int b = 2; printf("a = %d, b = %d\n", a, b); a ^= b; b ^= a; a ^= b; printf("a = %d, b = %d\n", a, b); 

Resultado:

a = 1, b = 2

a = 2, b = 1

Ver la pregunta “Características ocultas de C” .

Me gusta el concepto de container_of usado por ejemplo en listas. Básicamente, no necesita especificar los campos next y last para cada estructura que estará en la lista. En su lugar, agrega el encabezado de la estructura de lista a los elementos vinculados reales.

Eche un vistazo a include/linux/list.h para ver ejemplos de la vida real.

Creo que el uso de punteros de datos de usuario es bastante claro . Una moda perdiendo terreno en la actualidad. No es tanto una función de C, pero es bastante fácil de usar en C.

Utilizo X-Macros para permitir que el precomstackdor 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á.

Nuestro código base tiene un truco similar a

 #ifdef DEBUG #define my_malloc(amt) my_malloc_debug(amt, __FILE__, __LINE__) void * my_malloc_debug(int amt, char* file, int line) #else void * my_malloc(int amt) #endif { //remember file and line no. for this malloc in debug mode } 

que permite el seguimiento de las memory leaks en modo de depuración. Siempre pensé que esto era genial.

Diversión con macros:

 #define SOME_ENUMS(F) \ F(ZERO, zero) \ F(ONE, one) \ F(TWO, two) /* Now define the constant values. See how succinct this is. */ enum Constants { #define DEFINE_ENUM(A, B) A, SOME_ENUMS(DEFINE_ENUMS) #undef DEFINE_ENUM }; /* Now a function to return the name of an enum: */ const char *ToString(int c) { switch (c) { default: return NULL; /* Or whatever. */ #define CASE_MACRO(A, B) case A: return #b; SOME_ENUMS(CASE_MACRO) #undef CASE_MACRO } } 

Aquí hay un ejemplo de cómo hacer que el código C no tenga conocimiento 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 comstackdor / arco. Creo que es bastante bueno para abstraer un poco el código C, por lo que no llega a ser específico.

Añadiendo un ejemplo comstackble completo aquí.

 /* free.h */ #ifndef _FREE_H_ #define _FREE_H_ #include  #include  typedef unsigned char ubyte; typedef void (*F_ParameterlessFunction)() ; typedef void (*F_CommandFunction)(ubyte byte) ; void F_SetupLowerLayer ( F_ParameterlessFunction initRequest, F_CommandFunction sending_command, F_CommandFunction *receiving_command); #endif /* free.c */ static F_ParameterlessFunction Init_Lower_Layer = NULL; static F_CommandFunction Send_Command = NULL; static ubyte init = 0; void recieve_value(ubyte my_input) { if(init == 0) { Init_Lower_Layer(); init = 1; } printf("Receiving 0x%02x\n",my_input); Send_Command(++my_input); } void F_SetupLowerLayer ( F_ParameterlessFunction initRequest, F_CommandFunction sending_command, F_CommandFunction *receiving_command) { Init_Lower_Layer = initRequest; Send_Command = sending_command; *receiving_command = &recieve_value; } /* main.c */ int my_hw_do_init() { printf("Doing HW init\n"); return 0; } int my_hw_do_sending(ubyte send_this) { printf("doing HW sending 0x%02x\n",send_this); return 0; } F_CommandFunction my_hw_send_to_read = NULL; int main (void) { ubyte rx = 0x40; F_SetupLowerLayer(my_hw_do_init,my_hw_do_sending,&my_hw_send_to_read); my_hw_send_to_read(rx); getchar(); return 0; } 
 if(---------) printf("hello"); else printf("hi"); 

Llene los espacios en blanco para que ni hola ni hola aparezcan en la salida.
ans: fclose(stdout)