Par exemple, je suis récemment tombé sur cela dans le noyau Linux:
/* Forcer une erreur de compilation si la condition est vraie */ # Définir BUILD_BUG_ON (condition) ((void) sizeof (char [1 - 2 * !! (condition)]))
Donc, dans votre code, si vous avez une structure qui doit être, disons, un multiple de 8 octets, peut-être à cause de contraintes matérielles, vous pouvez faire:
BUILD_BUG_ON ((sizeof (struct mystruct)% 8)! = 0);
et il ne compilera que si la taille de struct mystruct est un multiple de 8, et s'il s'agit d'un multiple de 8, aucun code d'exécution n'est généré.
Une autre astuce que je connais est celle du livre "Graphics Gems" qui permet à un fichier d’en-tête unique de déclarer et d’initialiser des variables dans un module, tandis que dans les autres modules utilisant ce module, les déclare simplement en tant qu’externes.
# ifdef DEFINE_MYHEADER_GLOBALS # définir GLOBAL # définir INIT (x, y) (x) = (y) # autre # définir GLOBAL extern # Définit INIT (x, y) # Endif GLOBAL int INIT (x, 0); GLOBAL int somefunc ( int a, int b);
Avec cela, le code qui définit x et somefunc fait:
# définir DEFINE_MYHEADER_GLOBALS # include "the_above_header_file.h"
tandis que le code qui utilise simplement x et somefunc () fait:
# inclut "the_above_header_file.h"
Vous obtenez donc un fichier d’en-tête qui déclare les instances de globales et les prototypes de fonctions là où ils sont nécessaires, ainsi que les déclarations externes correspondantes.
Alors, quelles sont vos astuces de programmation C préférées dans ce sens?
C99 propose des contenus vraiment sympas utilisant des tableaux anonymes:
Suppression des variables inutiles
{
int yes=1;
setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
}
devient
setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));
Passer une quantité variable d'arguments
void func(type* values) {
while(*values) {
x = *values++;
/* do whatever with x */
}
}
func((type[]){val1,val2,val3,val4,0});
listes de liens statiques
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;
}
}
Je suis sûr que de nombreuses autres techniques intéressantes auxquelles je n’ai pas pensé.
En lisant le code source de Quake 2, je suis arrivé à quelque chose comme ceci:
double normals[][] = {
#include "normals.txt"
};
(plus ou moins, je n'ai pas le code pour le vérifier maintenant).
Depuis lors, un nouveau monde d'utilisation créative du pré-processeur s'est ouvert sous mes yeux. Je n'inclus plus seulement des en-têtes, mais des morceaux de code entiers de temps en temps (cela améliore beaucoup la réutilisabilité) :-p
Merci John Carmack! xD
J'aime utiliser = {0};
pour initialiser des structures sans avoir à appeler memset.
struct something X = {0};
Cela initialisera tous les membres de la structure (ou du tableau) à zéro (mais pas les octets de remplissage - utilisez memset si vous devez également les remettre à zéro).
Mais vous devez savoir qu’il existe quelques problèmes avec cela pour les grandes structures allouées dynamiquement .
Si nous parlons de trucs c, mon préféré doit être Duff's Device pour le déroulement de la boucle! J'attends juste la bonne occasion de m'engager à l'utiliser réellement dans la colère ...
en utilisant __FILE__
et __LINE__
pour le débogage
#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};
Une fois, un de mes compagnons et moi avons redéfini le retour pour trouver un bug de corruption de pile délicat.
Quelque chose comme:
#define return DoSomeStackCheckStuff, return
J'aime le "struct hack" pour avoir un objet de taille dynamique. Ce site l'explique assez bien aussi (bien qu'ils se rapportent à la version C99 où vous pouvez écrire "str []" en tant que dernier membre d'une structure). vous pouvez faire une chaîne "objet" comme ceci:
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;
ici, nous avons alloué une structure de type X sur le tas, de la taille d'un int (pour len), plus la longueur de "hello world", plus 1 (puisque str 1 est inclus dans le sizeof (X).
C'est généralement utile lorsque vous voulez avoir un "en-tête" juste avant des données de longueur variable dans le même bloc.
Code orienté objet avec C, en émulant des classes.
Créez simplement une structure et un ensemble de fonctions qui prennent un pointeur sur cette structure en tant que premier paramètre.
Au lieu de
printf("counter=%d\n",counter);
Utilisation
#define print_dec(var) printf("%s=%d\n",#var,var);
print_dec(counter);
Utiliser une astuce macro stupide pour rendre les définitions d’enregistrement plus faciles à gérer.
#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;
Pour créer une variable en lecture seule dans tous les modules sauf celui déclaré dans:
// 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
Les décalages de bits ne sont définis que jusqu'à un nombre de décalages de 31 (sur un entier de 32 bits).
Que faites-vous si vous voulez un décalage calculé qui doit également fonctionner avec des valeurs de décalage plus élevées? Voici comment le codec vidéo Theora le fait:
unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
return (a>>(v>>1))>>((v+1)>>1);
}
Ou beaucoup plus lisible:
unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
unsigned int halfshift = v>>1;
unsigned int otherhalf = (v+1)>>1;
return (a >> halfshift) >> otherhalf;
}
Effectuer la tâche comme indiqué ci-dessus est bien plus rapide que d’utiliser une branche comme celle-ci:
unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
if (v<=31)
return a>>v;
else
return 0;
}
Déclaration d'un tableau de pointeur vers des fonctions pour l'implémentation de machines à états finis.
int (* fsm[])(void) = { ... }
L'avantage le plus agréable est qu'il est simple de forcer chaque stimulus/état à vérifier tous les chemins de code.
Dans un système embarqué, je mappe souvent un ISR pour qu'il pointe vers une telle table et le révise au besoin (en dehors de l'ISR).
Une autre astuce de Nice pré-processeur consiste à utiliser le caractère "#" pour imprimer des expressions de débogage. Par exemple:
#define MY_ASSERT(cond) \
do { \
if( !(cond) ) { \
printf("MY_ASSERT(%s) failed\n", #cond); \
exit(-1); \
} \
} while( 0 )
edit: le code ci-dessous ne fonctionne que sur C++. Merci à smcameron et à Evan Teran.
Oui, l'assertion du temps de compilation est toujours excellente. Il peut aussi être écrit comme:
#define COMPILE_ASSERT(cond)\
typedef char __compile_time_assert[ (cond) ? 0 : -1]
#if TESTMODE == 1
debug=1;
while(0); // Get attention
#endif
Le tout (0); n'a aucun effet sur le programme, mais le compilateur émettra un avertissement à propos de "cela ne fait rien", ce qui est suffisant pour me permettre d'aller voir la ligne incriminée et de voir ensuite la vraie raison pour laquelle je voulais attirer l'attention.
Je suis fan de xor hacks:
Permutez 2 pointeurs sans troisième pointeur temp:
int * a;
int * b;
a ^= b;
b ^= a;
a ^= b;
Ou j'aime beaucoup la liste des liens xor avec un seul pointeur. (http://en.wikipedia.org/wiki/XOR_linked_list)
Chaque nœud de la liste liée est le Xor du nœud précédent et du nœud suivant. Pour avancer, les adresses des nœuds se retrouvent de la manière suivante:
LLNode * first = head;
LLNode * second = first.linked_nodes;
LLNode * third = second.linked_nodes ^ first;
LLNode * fourth = third.linked_nodes ^ second;
etc.
ou à reculer:
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.
Bien que ce ne soit pas très utile (vous ne pouvez pas commencer à traverser à partir d'un nœud arbitraire), je le trouve très cool.
Je ne dirais pas que c'est un tour préféré, puisque je ne l'ai jamais utilisé, mais la mention de Duff's Device me rappelait cet article sur la mise en œuvre de Coroutines en C. Cela me fait toujours rire, mais je suis sûr que cela pourrait être utile quelque temps.
Celui-ci vient du livre 'Assez de corde pour te tirer une balle dans le pied':
Dans l'en-tête déclarer
#ifndef RELEASE
# define D(x) do { x; } while (0)
#else
# define D(x)
#endif
Dans votre code, placez les instructions de test, par exemple:
D(printf("Test statement\n"));
Do/While aide si le contenu de la macro est étendu à plusieurs instructions.
L'instruction ne sera imprimée que si l'indicateur '-D RELEASE' pour le compilateur n'est pas utilisé.
Vous pouvez alors par exemple. passe le drapeau à ton makefile etc.
Vous ne savez pas comment cela fonctionne dans Windows mais dans * nix cela fonctionne bien
Rusty a en fait produit tout un ensemble de conditions de construction dans ccan , consultez le module d'assertion de construction:
#include <stddef.h>
#include <ccan/build_assert/build_assert.h>
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;
}
L'en-tête lui-même contient de nombreuses autres macros utiles, faciles à mettre en place.
J'essaie, de toutes mes forces, de résister à l'attirance du côté obscur (et aux abus du préprocesseur) en collant principalement aux fonctions en ligne, mais j'apprécie des macros utiles et astucieuses, comme celles que vous avez décrites.
Deux bons ouvrages pour ce genre de choses sont La pratique de la programmation et Écriture de code solide . L’un d’eux (je ne me souviens plus lequel) dit: Préférer enum à #define où vous le pouvez, car enum est vérifié par le compilateur.
Ce n'est pas spécifique à C, mais j'ai toujours aimé l'opérateur XOR. Une bonne chose à faire est de "permuter sans valeur temporaire":
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);
Résultat:
a = 1, b = 2
a = 2, b = 1
Voir "Fonctions cachées de C" question.
J'aime le concept de container_of
utilisé par exemple dans les listes. En gros, il n'est pas nécessaire de spécifier les champs next
et last
pour chaque structure qui figurera dans la liste. Au lieu de cela, vous ajoutez l'en-tête de structure de liste aux éléments liés réels.
Jetez un coup d'œil sur include/linux/list.h
pour des exemples concrets.
Notre base de code a un truc semblable à
#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
}
ce qui permet le suivi des fuites de mémoire en mode débogage. J'ai toujours pensé que c'était cool.
Je pense que l'utilisation de serdata pointeurs est très chouette. Une mode en perte de vitesse aujourd'hui. Ce n'est pas vraiment une fonctionnalité C mais il est assez facile à utiliser en C.
J'utilise X-Macros pour permettre au pré-compilateur de générer du code. Ils sont particulièrement utiles pour définir les valeurs d'erreur et les chaînes d'erreur associées à un endroit donné, mais ils peuvent aller bien au-delà.
Amusez-vous avec les 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
}
}
En C99, vous pouvez directement incorporer des URL dans le code source dans des fonctions. Exemple:
#include <stdio.h>
int main(int argc, char** argv) {
http://stackoverflow.com/
printf("Hello World");
}
J'adore les opérateurs if-else et while (0) vides.
Par exemple:
#define CMD1(X) do { foo(x); bar(x); } while (0)
#define CMD2(X) if (1) { foo(x); bar(x); } else
Voici un exemple pour rendre le code C complètement ignorant de ce qui est réellement utilisé par HW pour exécuter l'application. Main.c effectue la configuration, puis la couche libre peut être mise en œuvre sur n’importe quel compilateur/Arch. Je pense que c’est assez bien pour résumer un peu le code C, donc ça n’arrive pas à être trop spécifique.
Ajouter un exemple compilable complet ici.
/* free.h */
#ifndef _FREE_H_
#define _FREE_H_
#include <stdio.h>
#include <string.h>
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;
}
Utilisation de l'opérateur ?:, sinon inutile, pour initialiser une variable const
const int bytesPerPixel = isAlpha() ? 4 : 3;
if(---------)
printf("hello");
else
printf("hi");
Remplissez les blancs pour que ni bonjour ni salut n'apparaissent en sortie.
ans: fclose(stdout)
J'ai toujours aimé les astuces du préprocesseur stupide pour créer des types de conteneur génériques:
/* list.h */
#ifndef CONTAINER_TYPE
#define CONTAINER_TYPE VALUE_TYPE ## List
#endif
typedef struct CONTAINER_TYPE {
CONTAINER_TYPE *next;
VALUE_TYPE v;
} CONTAINER_TYPE;
/* Possibly Lots of functions for manipulating Lists
*/
#undef VALUE_TYPE
#undef CONTAINER_TYPE
Ensuite, vous pouvez faire par exemple:
#define VALUE_TYPE int
#include "list.h"
typedef struct myFancyStructure *myFancyStructureP;
#define VALUE_TYPE myFancyStructureP
#define CONTAINER_TYPE mfs
#include "list.h"
Et n'écrivez plus jamais une liste chaînée. Si VALUE_TYPE doit toujours être un pointeur, il s'agit d'une surcharge, puisqu'un vide * fonctionnerait aussi bien. Mais il existe souvent de très petites structures pour lesquelles le surcoût de l'indirection n'a pas de sens. Vous bénéficiez également d’une vérification de type (c’est-à-dire que vous ne souhaitez peut-être pas concaténer une liste chaînée de chaînes avec une liste chaînée de doublons, même si les deux fonctionnent dans une liste chaînée void *).