Quelle est votre astuce de programmation C préférée? [fermé]

134

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?

smcameron
la source
9
Cela ressemble plus à des astuces de préprocesseur C.
jmucchiello
À propos de BUILD_BUG_ONmacro, quel est le problème avec l'utilisation de l' #errorintérieur et #if?
Ricardo

Réponses:

80

C99 propose des trucs vraiment sympas en utilisant des tableaux anonymes:

Supprimer les 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 liées 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 beaucoup d'autres techniques intéressantes auxquelles je n'ai pas pensé.

Evan Teran
la source
2
Je pense que votre premier exemple peut également s'écrire &(int){1} : si vous voulez clarifier un peu votre intention ici.
Lily Ballard
67

En lisant le code source de Quake 2, j'ai trouvé quelque chose comme ceci:

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

(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

fortran
la source
13
Vous ne pouvez pas dire carmack dans un thread d'optimisation sans mentionner le sqrt inverse rapide qui était dans la source du tremblement de terre. en.wikipedia.org/wiki/Fast_inverse_square_root
pg1989
D'où a-t-il obtenu 0x5f3759df en premier lieu?
RSH1 du
2
@RoryHarvey: D'après ce que j'ai pu trouver en le recherchant, il semble que c'était purement empirique. Certaines études (je ne me souviens pas où je les ai vus) ont démontré que c'était presque optimal, mais pas complètement optimal. De même, il semble que pour 64 bits, la valeur a été découverte, plutôt que le calcul.
Matthieu M.
50

J'aime utiliser = {0}; pour initialiser des structures sans avoir besoin d'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 être conscient que cela pose certains problèmes pour les grandes structures allouées dynamiquement .

John Carter
la source
Pas nécessaire pour les variables globales, d'ailleurs.
strager
5
Non nécessaire pour les variables statiques . Les variables globales peuvent être mises à zéro, mais ce n'est pas une exigence.
Jamie
4
J'étends parfois ceci à: const struct something zero_something = { 0 };et ensuite je peux réinitialiser une variable à la volée avec struct 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).
Jonathan Leffler
45

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 ...

Jackson
la source
4
Je l'ai utilisé une fois pour produire un gain de performance mesurable, mais ces jours-ci, il n'est pas utile sur beaucoup de matériel. Toujours profil!
Dan Olson
6
Ouais, le genre de personnes qui ne comprennent pas le contexte dans lequel l'appareil de Duff a été créé: la "lisibilité du code" est inutile si le code n'est pas assez rapide pour fonctionner. Probablement aucune des personnes qui vous ont critiqué n'a jamais eu à coder en temps réel difficile.
Rob K
1
+1, j'ai en fait eu besoin d'utiliser l'appareil de Duff plusieurs fois. La première fois, c'était une boucle qui copiait simplement des trucs et effectuait une petite transformation en cours de route. C'était beaucoup, beaucoup plus rapide qu'un simple memcpy () dans cette architecture.
Makis
3
La colère viendra de vos collègues et successeurs qui doivent maintenir votre code après vous.
Jonathan Leffler
1
Comme je l'ai dit, j'attends toujours la bonne opportunité - mais personne ne m'a encore assez ennuyé. J'écris en C depuis environ 25 ans maintenant, je pense que je suis tombé sur l'appareil de Duff au début des années 90 et je n'ai pas encore eu à l'utiliser. Comme d'autres l'ont commenté, ce genre d'astuce est de moins en moins utile maintenant que les compilateurs s'améliorent dans ce type d'optimisation.
Jackson
42

utilisation __FILE__et __LINE__pour le débogage

#define WHERE fprintf(stderr,"[LOG]%s:%d\n",__FILE__,__LINE__);
Pierre
la source
6
Sur certains compilateurs, vous obtenez également FUNCTION .
JBRWilkinson
11
__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.
sklnd
FILE montre le chemin complet du nom de fichier donc j'utilise basename ( FILE )
Jeegar Patel
31

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};
Jasper Bekkers
la source
28

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
Andrew Barrett
la source
4
J'espère que cela a été # défini dans le corps de la fonction et # non défini à la fin!
strager
Je n'aime pas beaucoup ça - la première chose qui me vient à l'esprit est que DoSomeStackCheckStuff bousille la mémoire à cause d'un bug et quiconque lit le code n'est pas au courant de la redéfinition de return et se demande ce qui se passe.
gilligan
8
@strager Mais cela le rendrait fondamentalement inutile. Le but est d'ajouter un suivi à chaque appel de fonction. Sinon, vous ajouteriez simplement un appel aux DoSomeStackCheckStufffonctions que vous souhaitez tracer.
Clueless
1
@gilligan Je ne pense pas que ce soit le type de chose que vous laissez activé tout le temps; cela semble assez pratique pour un travail de débogage ponctuel.
sunetos
ça marche vraiment? :) J'aurais écrit #define return if((DoSomeStackCheckStuff) && 0) ; else return... tout aussi fou je suppose!
Paolo Bonzini
22

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:

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 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.

Evan Teran
la source
Personnellement, je trouve plus facile de me contenter de malloc () et de réallouer () moi-même et d'utiliser strlen () chaque fois que j'ai besoin de trouver la longueur, mais si vous avez besoin d'un programme qui ne connaît jamais la longueur de la chaîne et devra probablement en trouver beaucoup fois, c'est probablement la meilleure route.
Chris Lutz
4
"... la version C99 où vous pouvez écrire" str [] "" J'ai vu des tableaux de taille nulle dans un tel contexte, comme str [0]; assez souvent. Je pense que c'est C99. Je sais que les compilateurs plus anciens se plaignent des tableaux de taille nulle.
smcameron
3
J'aime aussi celui-ci, cependant, vous devriez utiliser quelque chose comme malloc (offsetof (X, str) + numbytes) sinon les choses iront mal à cause de problèmes de remplissage et d'alignement. Par exemple, sizeof (struct X) pourrait être 8, pas 5.
Fozi
3
@Fozi: En fait, je ne pense pas que ce serait un problème. Puisque cette version a str[1](pas str[]) le 1 octet de str est inclus dans le sizeof(struct X). Cela inclut tout remplissage entre lenet str.
Evan Teran
2
@Rusky: Comment cela aurait-il un effet négatif sur quoi que ce soit? Supposons qu'il y ait "padding" après str. OK, quand j'alloue sizeof(struct X) + 10alors cela fait streffectivement 10 - sizeof(int)(ou plus, puisque nous avons dit qu'il y a du rembourrage) gros. Cela se superpose str 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ès strquoi 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.
Evan Teran
17

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.

Brian R. Bondy
la source
2
Existe-t-il encore quelque chose qui traduit le C ++ en C, comme le faisait cfront?
MarkJ
11
Ce n'est guère une orientation objet. Pour OO avec héritage, vous devrez ajouter une sorte de table de fonction virtuelle à votre structure d'objet, qui peut être surchargée par des "sous-classes". Il y a beaucoup de frameworks de style "C avec classes" à moitié cuits à cet effet, mais je recommande de rester en dehors.
exDM69
Il fallait le dire. +1 pour ça.
Amit S
3
@ exDM69, l'orientation objet est autant une manière de penser un problème qu'un paradigme de codage; vous pouvez le faire avec succès sans héritage. J'ai fait cela sur quelques projets avant de passer à plein régime en C ++.
Mark Ransom
16

Au lieu de

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

Utilisation

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

Utiliser une astuce de macro stupide pour rendre les définitions d'enregistrement plus faciles à maintenir.

#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;
EvilTeach
la source
11

Pour créer une variable qui est en lecture seule dans tous les modules sauf celui dans lequel elle est déclarée:

// 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
Steve Melnikoff
la source
Cela semble dangereux. Ce sont des déclarations et des définitions qui ne correspondent pas. Lors de la compilation Source2.c, le compilateur peut supposer que MyVarcela 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.)
jilles
1
Cela ne produit pas de variable en lecture seule dans certaines unités de compilation. Cela produit un comportement indéfini (voir p. 6.2.7.2 de l'ISO 9899 et également p. 6.7.3.5).
Ales Hakl
8

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:

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

Ou bien 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 de la manière indiquée ci-dessus est beaucoup 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;
}
Nils Pipenbrinck
la source
... et gcc en fait en ligne :) +1
Tim Post
2
Sur ma machine, gcc-4.3.2 se débarrasse de la branche dans la seconde en utilisant une instruction cmov (déplacement conditionnel)
Adam Rosenfield
3
"beaucoup plus rapide que d'utiliser une branche": la différence étant que la branche est correcte pour toutes les valeurs de v, alors que l' halfshiftastuce ne fait que doubler la plage autorisée à 63 sur une architecture 32 bits, et 127 sur une architecture 64 bits.
Pascal Cuoq
8

Déclarer des tableaux de pointeurs vers des fonctions pour implémenter des 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 mapperai souvent un ISR pour pointer vers une telle table et le réviserai si nécessaire (en dehors des ISR).

Jamie
la source
Une technique que j'aime avec ceci est, si vous avez une fonction qui nécessite une initialisation, vous initialisez le pointeur avec un appel à la routine d'initialisation. Lorsque cela s'exécute, la dernière chose qu'il fait est de remplacer le pointeur par un pointeur vers la fonction réelle, puis d'appeler cette fonction. De cette façon, l'initialiseur est automatiquement appelé la première fois que la fonction est appelée, et la fonction réelle est appelée à chaque fois.
TMN
7

Un autre "truc" du préprocesseur est d'utiliser le caractère "#" pour imprimer les 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'affirmation du temps de compilation est toujours excellente. Il peut également s'écrire:

#define COMPILE_ASSERT(cond)\
     typedef char __compile_time_assert[ (cond) ? 0 : -1]
Gilad Naor
la source
La macro COMPILE_ASSERT ne peut cependant pas être utilisée deux fois, car elle pollue l'espace de noms avec un typedef, et la 2ème utilisation obtient: erreur: redéfinition de typedef '__compile_time_assert'
smcameron
Avez-vous réellement essayé cela? Vous pouvez "typedef foo;" autant de fois que vous le souhaitez. C'est comme ça que vous faites des prédéclarations. Je l'utilise depuis 2,5 ans maintenant sur plusieurs compilateurs, à la fois gcc, VC et un compilateur pour un environnement embarqué, et je n'ai jamais rencontré de difficultés.
Gilad Naor
Je déteste le préprocesseur C ... :(
hasen
1
Oui, je l'ai essayé. J'ai coupé et collé le message d'erreur du compilateur, qui était gcc.
smcameron
1
@Gilad: il est légal en c ++ d'avoir des typedefs redondants, mais pas en c.
Evan Teran
6

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.

Dan Olson
la source
J'ai en fait utilisé cette technique dans la pratique pour rendre le code pilotant une séquence d'E / S asynchrones dépendantes vaguement lisibles par l'homme. La principale différence est que je ne stocke pas l'état de la coroutine dans une staticvariable, 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 (via swapcontext()sur * nixes) si je pouvais bien.
pmdj
6
#if TESTMODE == 1    
    debug=1;
    while(0);     // Get attention
#endif

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.

gbarry
la source
9
ne pourriez-vous pas utiliser #warning à la place?
Stefano Borini
Apparemment, je pourrais. Ce n'est pas complètement standard, mais cela a fonctionné dans les compilateurs que j'utilise. Fait intéressant, le compilateur intégré a traduit une #define, contrairement à gcc.
gbarry
6

Je suis fan des hacks xor:

Échangez 2 pointeurs sans troisième pointeur temporaire:

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

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:

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

etc.

ou pour 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 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.

hamiltop
la source
5

Celui-ci vient du livre 'Assez de corde pour vous 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 vos instructions de test de lieu de code, par exemple:

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

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

Simon Walker
la source
Vous voudrez peut-être étendre D (x) à {} lorsque RELEASE est défini, afin qu'il joue bien avec les instructions if. Sinon "if (a) D (x);" s'étendra à "si (a)" seulement lorsque vous aurez défini RELEASE. Cela vous donnera quelques bugs agréables dans la version verison
MarkJ
3
@MarkJ: NON. La façon dont il est, "if (a) D (x);" se développe en "si (a);" ce qui est parfaitement bien. Si vous aviez D (x) étendu à {}, alors "if (a) if (b) D (x); else foo ();" se développerait INCORRECTEMENT en "if (a) if (b) {}; else foo ();", faisant correspondre le "else foo ()" avec le second if au lieu du premier if.
Adam Rosenfield
Pour être honnête, j'utilise principalement cette macro pour tester les instructions d'impression, ou si j'avais une instruction conditionnelle, je la mettrais toutes par exemple. D (si (a) foo (););
Simon Walker
1
@AdamRosenfield: Utiliser à la #define D(x) do { } while(0)place gère ce cas (et peut être appliqué à la branche qui s'insère xégalement pour la cohérence)
rpetrich
3

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;
}

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.

Tim Post
la source
Ouais, je suis récemment tombé sur ccan, et j'envisageais de contribuer au code, mais je n'ai pas encore fait le tour de la "voie ccan". Merci pour le lien cependant, plus de motivation pour examiner ccan, ce qui, j'espère vraiment, suscitera un certain intérêt.
smcameron
Eh bien, je ne serais pas trop préoccupé par la «voie ccan» jusqu'à ce qu'elle soit plus établie ... pour le moment, ccan-lint est proposé en tant que projet GSOC. C'est un petit groupe plutôt sympathique ... et un endroit idéal pour vider des extraits :)
Tim Post
BTW, j'ai remarqué que BuILD_ASSERT de Rusty est juste comme la macro du noyau Linux (pas surprenant) mais qu'il manque l'un des "nots" (ou bangs, ou!) Et en remarquant que, je pense que mon exemple d'utilisation de la macro que j'ai postée est Pas correcte. Doit avoir été: "BUILD_BUG_ON ((sizeof (struct mystruct)% 8))"
smcameron
3

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.

Yuval F
la source
1
AFAIK, dans C89 / 90, il n'y a PAS de vérification de type pour les énumérations. les énumérations sont simplement des #defines plus pratiques.
cschol
Bas de page 39, 2e ED K&R. Il y a au moins la possibilité de vérifier.
Jonathan Watmough
3

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":

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

Karl
la source
a = 1; b = 2; a = a + b; b = ab; a = ab; donne le même résultat aussi
Grambot
Cela permutera également a et b: a ^ = b ^ = a ^ = b;
vikhyat
@TheCapn: l'ajout pourrait cependant déborder.
Michael Foukarakis
2

J'aime le concept de container_ofutilisé par exemple dans les listes. En gros, vous n'avez pas besoin de spécifier nextetlast 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.haux exemples réels.

Viliam
la source
1

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.

epatel
la source
1
J'aurais aimé comprendre ce que vous vouliez dire ici. Pouvez-vous expliquer plus? Qu'est-ce qu'un pointeur de données utilisateur?
Zan Lynx
c'est principalement pour les rappels. Il s'agit de certaines données que vous aimeriez vous donner à chaque fois que le rappel est déclenché. Particulièrement utile pour passer un C ++ ce pointeur vers un rappel afin que vous puissiez lier un objet à un événement.
Evan Teran
Ah oui. Merci. J'utilise beaucoup cela, mais je ne l'ai jamais appelé ainsi.
Zan Lynx
1

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à.

JayG
la source
1

Notre base de code a une astuce similaire à

#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
}

qui permet le suivi des fuites de mémoire en mode débogage. J'ai toujours pensé que c'était cool.

jdizzle
la source
1

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
     }
}
sanjoyd
la source
0

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.

/* 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;
}
eaanon01
la source
4
Vous souhaitez élaborer, peut-être expliquer une utilisation pratique?
Leonardo Herrera
Comme un exemple si je dois écrire un programme de test en utilisant une interface som HW qui génère des interruptions à la fin. Ensuite, ce module peut être configuré pour exécuter une fonction en dehors de la portée normale en tant que gestionnaire de signal / d'interruption.
eaanon01
0
if(---------)  
printf("hello");  
else   
printf("hi");

Remplissez les espaces pour que ni bonjour ni salut n'apparaissent dans la sortie.
ans:fclose(stdout)

justgo
la source
vous pouvez formater le code avec le {}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.
Álvaro González