Par exemple, je suis récemment tombé sur ceci dans le noyau Linux:
/ * Forcer une erreur de compilation si la condition est vraie * / #define 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 certaines contraintes matérielles, vous pouvez faire:
BUILD_BUG_ON ((sizeof (struct mystruct)% 8)! = 0);
et il ne se 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é du tout.
Une autre astuce que je connais vient du livre "Graphics Gems" qui permet à un seul fichier d'en-tête à la fois de déclarer et d'initialiser des variables dans un module tandis que dans d'autres modules utilisant ce module, il suffit de les déclarer comme externes.
#ifdef DEFINE_MYHEADER_GLOBALS #define GLOBAL #define INIT (x, y) (x) = (y) #autre #define GLOBAL extern #define INIT (x, y) #fin si GLOBAL int INIT (x, 0); GLOBAL int somefunc (int a, int b);
Avec cela, le code qui définit x et somefunc fait:
#define DEFINE_MYHEADER_GLOBALS #include "the_above_header_file.h"
tandis que le code qui utilise simplement x et somefunc () fait:
#include "the_above_header_file.h"
Vous obtenez donc un fichier d'en-tête qui déclare à la fois les instances de globals et les prototypes de fonction 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?
BUILD_BUG_ON
macro, quel est le problème avec l'utilisation de l'#error
intérieur et#if
?Réponses:
C99 propose des trucs vraiment sympas en utilisant des tableaux anonymes:
Supprimer les variables inutiles
devient
Passer une quantité variable d'arguments
Listes liées statiques
Je suis sûr que beaucoup d'autres techniques intéressantes auxquelles je n'ai pas pensé.
la source
&(int){1}
: si vous voulez clarifier un peu votre intention ici.En lisant le code source de Quake 2, j'ai trouvé quelque chose comme ceci:
(plus ou moins, je n'ai pas le code à portée de main pour le vérifier maintenant).
Depuis, un nouveau monde d'utilisation créative du préprocesseur s'est ouvert sous mes yeux. Je n'inclus plus que des en-têtes, mais des morceaux entiers de code de temps en temps (cela améliore beaucoup la réutilisabilité) :-p
Merci John Carmack! xD
la source
J'aime utiliser
= {0};
pour initialiser des structures sans avoir besoin d'appeler memset.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 être conscient que cela pose certains problèmes pour les grandes structures allouées dynamiquement .
la source
const struct something zero_something = { 0 };
et ensuite je peux réinitialiser une variable à la volée avecstruct something X = zero_something;
ou à mi-chemin d'une routine que je peux utiliser 'X = zero_something;'. La seule objection possible est qu'il s'agit de lire des données quelque part; ces jours-ci, un `` memset () '' pourrait être plus rapide - mais j'aime la clarté de l'affectation, et il est également possible d'utiliser des valeurs autres que zéro dans l'initialiseur aussi (et memset () suivis par des ajustements au membre individuel peut être plus lent qu’une simple copie).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 opportunité pour que je l'utilise avec colère ...
la source
utilisation
__FILE__
et__LINE__
pour le débogagela source
__FUNCTION__
est juste un alias pour__func__
, et__func__
est en c99. Très pratique.__PRETTY_FUNCTION__
en C (GCC) est juste un autre alias pour__func__
, mais en C ++, il vous donnera la signature de la fonction complète.En C99
la source
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:
la source
DoSomeStackCheckStuff
fonctions que vous souhaitez tracer.#define return if((DoSomeStackCheckStuff) && 0) ; else return
... tout aussi fou je suppose!J'aime le "struct hack" pour avoir un objet de taille dynamique. Ce site l' explique assez bien aussi (bien qu'ils se réfèrent à la version C99 où vous pouvez écrire "str []" comme dernier membre d'une structure). vous pouvez créer une chaîne "objet" comme ceci:
ici, nous avons alloué une structure de type X sur le tas qui a la taille d'un int (pour len), plus la longueur de "hello world", plus 1 (puisque str 1 est inclus dans sizeof (X).
Il 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.
la source
str[1]
(passtr[]
) le 1 octet de str est inclus dans lesizeof(struct X)
. Cela inclut tout remplissage entrelen
etstr
.str
. OK, quand j'allouesizeof(struct X) + 10
alors cela faitstr
effectivement10 - sizeof(int)
(ou plus, puisque nous avons dit qu'il y a du rembourrage) gros. Cela se superposestr
et tout rembourrage après lui. La seule façon dont cela aurait une différence, c'est que s'il y avait un membre aprèsstr
quoi tout casse de toute façon, les membres flexibles doivent être les derniers. Tout remplissage à la fin entraînera probablement trop d'allocation. Veuillez donner un exemple précis de la façon dont cela pourrait mal tourner.Code orienté objet avec C, en émulant des classes.
Créez simplement une structure et un ensemble de fonctions qui prennent un pointeur vers cette structure comme premier paramètre.
la source
Au lieu de
Utilisation
la source
Utiliser une astuce de macro stupide pour rendre les définitions d'enregistrement plus faciles à maintenir.
la source
Pour créer une variable qui est en lecture seule dans tous les modules sauf celui dans lequel elle est déclarée:
la source
Source2.c
, le compilateur peut supposer queMyVar
cela ne change pas, même lors d'un appel de fonction àSource1.c
. (Notez que cela, en tant que variable const réelle, diffère d'un pointeur vers const. Dans ce dernier cas, l'objet pointé peut encore être modifié via un pointeur différent.)Les décalages de bits ne sont définis que jusqu'à une valeur de décalage de 31 (sur un entier de 32 bits).
Que faites-vous si vous voulez avoir un quart de travail calculé qui doit également fonctionner avec des valeurs de quart plus élevées? Voici comment le vide-codec Theora le fait:
Ou bien plus lisible:
Effectuer la tâche de la manière indiquée ci-dessus est beaucoup plus rapide que d'utiliser une branche comme celle-ci:
la source
v
, alors que l'halfshift
astuce ne fait que doubler la plage autorisée à 63 sur une architecture 32 bits, et 127 sur une architecture 64 bits.Déclarer des tableaux de pointeurs vers des fonctions pour implémenter des machines à états finis.
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 mapperai souvent un ISR pour pointer vers une telle table et le réviserai si nécessaire (en dehors des ISR).
la source
Un autre "truc" du préprocesseur est d'utiliser le caractère "#" pour imprimer les expressions de débogage. Par exemple:
edit: le code ci-dessous ne fonctionne que sur C ++. Merci à smcameron et Evan Teran.
Oui, l'affirmation du temps de compilation est toujours excellente. Il peut également s'écrire:
la source
Je n'appellerais pas vraiment cela une astuce préférée, car je ne l'ai jamais utilisé, mais la mention de Duff's Device m'a rappelé 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.
la source
static
variable, mais alloue plutôt une structure de manière dynamique et passe un pointeur vers cela dans la fonction de coroutine. Un tas de macros rend cela plus acceptable. Ce n'est pas agréable mais meilleur que la version async / callback qui saute partout. J'utiliserais des fils verts (viaswapcontext()
sur * nixes) si je pouvais bien.Le while (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 faire aller regarder la ligne incriminée et voir ensuite la vraie raison pour laquelle je voulais attirer l'attention sur elle.
la source
Je suis fan des hacks xor:
Échangez 2 pointeurs sans troisième pointeur temporaire:
Ou j'aime vraiment la liste chaînée 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, l'adresse des nœuds se trouve de la manière suivante:
etc.
ou pour reculer:
etc.
Bien que cela ne soit pas très utile (vous ne pouvez pas commencer à traverser à partir d'un nœud arbitraire), je trouve que c'est très cool.
la source
Celui-ci vient du livre 'Assez de corde pour vous tirer une balle dans le pied':
Dans l'en-tête déclarer
Dans vos instructions de test de lieu de code, par exemple:
Le do / while aide au cas où le contenu de la macro se développerait en plusieurs instructions.
L'instruction ne sera imprimée que si l'indicateur «-D RELEASE» du compilateur n'est pas utilisé.
Vous pouvez alors par exemple. passez le drapeau à votre makefile etc.
Je ne sais pas comment cela fonctionne dans Windows mais dans * nix cela fonctionne bien
la source
#define D(x) do { } while(0)
place gère ce cas (et peut être appliqué à la branche qui s'insèrex
également pour la cohérence)Rusty a en fait produit tout un ensemble de conditions de construction dans ccan , consultez le module d' assertion de construction:
Il y a beaucoup d'autres macros utiles dans l'en-tête réel, qui sont faciles à mettre en place.
J'essaie, de toutes mes forces, de résister à l'attraction du côté obscur (et à l'abus des préprocesseurs) en m'en tenant principalement aux fonctions en ligne, mais j'aime les macros intelligentes et utiles comme celles que vous avez décrites.
la source
Deux bons livres sources pour ce genre de choses sont The Practice of Programming and Writing Solid Code . L'un d'eux (je ne me souviens pas lequel) dit: Préférez enum à #define là où vous le pouvez, car enum est vérifié par le compilateur.
la source
Pas spécifique à C, mais j'ai toujours aimé l'opérateur XOR. Une chose intéressante à faire est de "permuter sans valeur de température":
Résultat:
la source
Voir la question «Fonctionnalités cachées de C» .
la source
J'aime le concept de
container_of
utilisé par exemple dans les listes. En gros, vous n'avez pas besoin de spécifiernext
etlast
champs pour chaque structure qui sera dans la liste. Au lieu de cela, vous ajoutez l'en-tête de la structure de liste aux éléments liés réels.Jetez un œil
include/linux/list.h
aux exemples réels.la source
Je pense que l'utilisation des pointeurs userdata est assez soignée. Une mode qui perd du terrain de nos jours. Ce n'est pas tellement une fonctionnalité C mais c'est assez facile à utiliser en C.
la source
J'utilise X-Macros pour laisser le pré-compilateur 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 en un seul endroit, mais ils peuvent aller bien au-delà.
la source
Notre base de code a une astuce similaire à
qui permet le suivi des fuites de mémoire en mode débogage. J'ai toujours pensé que c'était cool.
la source
Amusez-vous avec les macros:
la source
Voici un exemple comment rendre le code C complètement inconscient de ce qui est réellement utilisé de HW pour exécuter l'application. Le main.c fait l'installation et ensuite la couche libre peut être implémentée sur n'importe quel compilateur / arch. Je pense que c'est assez sympa pour faire abstraction un peu du code C, donc ça ne peut pas être trop spécifique.
Ajout d'un exemple complet compilable ici.
la source
Remplissez les espaces pour que ni bonjour ni salut n'apparaissent dans la sortie.
ans:
fclose(stdout)
la source
{}
bouton de la barre d'outils (je l'ai fait pour vous). Le bouton "Citer" ne garde pas les espaces et n'applique pas la coloration syntaxique.