web-dev-qa-db-fra.com

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

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?

134
smcameron

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

81
Evan Teran

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

68
fortran

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 .

50
John Carter

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

45
Jackson

en utilisant __FILE__ et __LINE__ pour le débogage

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

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};
31
Jasper Bekkers

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
28
Andrew Barrett

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.

23
Evan Teran

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.

17
Brian R. Bondy

Au lieu de

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

Utilisation

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

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;
14
EvilTeach

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
11
Steve Melnikoff

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;
}
8
Nils Pipenbrinck

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

8
Jamie

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]
7
Gilad Naor
#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.

6
gbarry

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.

6
hamiltop

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.

6
Dan Olson

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

5
Simon Walker

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.

3
Tim Post

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.

3
Yuval F

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

3
Karl
2
Alex B

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.

2
Viliam

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.

1
jdizzle

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.

1
epatel

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

1
JayG

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
     }
}
1
sanjoyd

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");
}
0
sakra

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
0
Anton K

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

Utilisation de l'opérateur ?:, sinon inutile, pour initialiser une variable const

const int bytesPerPixel = isAlpha() ? 4 : 3;

0
Martin Beckett
if(---------)  
printf("hello");  
else   
printf("hi");

Remplissez les blancs pour que ni bonjour ni salut n'apparaissent en sortie.
ans: fclose(stdout)

0
justgo

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

0
Jason