programmation - pointeur c




Quel est votre truc de programmation C préféré? (20)

Par exemple, je suis récemment tombé sur ceci dans le noyau Linux:

/* Force a compilation error if condition is true */
#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 contraintes matérielles, vous pouvez faire:

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

et il ne compilera pas à moins que la taille de struct mystruct ne soit 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 vient du livre "Graphics Gems" qui permet à un seul fichier d'en-tête 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 externs.

#ifdef DEFINE_MYHEADER_GLOBALS
#define GLOBAL
#define INIT(x, y) (x) = (y)
#else
#define GLOBAL extern
#define 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:

#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 ainsi un fichier d'en-tête qui déclare à la fois les instances des globals et des prototypes de fonctions, ainsi que les déclarations externes correspondantes.

Alors, quelles sont vos astuces de programmation C préférées dans ce sens?


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

Au lieu de

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

Utilisation

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

Celui-ci vient du livre 'Assez de corde pour vous tirer dans le pied':

Dans l'en-tête déclare

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

Dans vos instructions de test de code, par exemple:

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

Le do / while aide dans le cas où le contenu de la macro se développe en 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. passez le drapeau à votre makefile etc.

Je ne sais pas comment cela fonctionne dans les fenêtres mais dans * nix cela fonctionne bien


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.



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

J'adore utiliser = {0}; initialiser des structures sans avoir besoin d'appeler memset.

struct something X = {0};

Cela va initialiser tous les membres de la structure (ou tableau) à zéro (mais pas tous les octets de remplissage - utilisez memset si vous avez également besoin de zéro).

Mais vous devez savoir qu'il existe des problèmes avec de grandes structures allouées dynamiquement .


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 []" en tant que dernier membre d'une structure). vous pourriez 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 qui est la taille d'un int (pour len), plus la longueur de "hello world", plus 1 (puisque str 1 est inclus dans le sizeof (X).

Il est généralement utile lorsque vous voulez avoir un "en-tête" juste avant certaines données de longueur variable dans le même bloc.


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


Je ne dirais pas que c'est un truc préféré, puisque je ne l'ai jamais utilisé, mais la mention de Duff's Device m'a rappelé cet article sur l'implémentation de Coroutines en C. Ça me donne toujours un petit rire, mais je suis sûr qu'il pourrait sois utile quelque temps.


Je suis un fan de xor hacks:

Echangez 2 pointeurs sans le troisième pointeur de température:

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

Ou j'aime vraiment la liste liée xor avec un seul pointeur. (http://en.wikipedia.org/wiki/XOR_linked_list)

Chaque noeud de la liste liée est le Xor du noeud précédent et le noeud suivant. Pour traverser vers l'avant, l'adresse des noeuds 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 parcourir en arrière:

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 pas très utile (vous ne pouvez pas commencer à traverser à partir d'un noeud arbitraire), je trouve que c'est très cool.


Les décalages de bits ne sont définis que jusqu'à un décalage de 31 (sur un entier de 32 bits).

Que faites-vous si vous voulez avoir un décalage calculé qui doit également fonctionner avec des valeurs de décalage plus élevées? Voici comment le vidéodisque 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 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;
}

Pas spécifique à C, mais j'ai toujours aimé l'opérateur XOR. Une chose sympa qu'il peut faire est "échanger sans valeur temp":

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


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

Si nous parlons des tours de c, mon préféré doit être le périphérique de Duff pour le déroulement de la boucle! J'attends juste la bonne opportunité pour venir l'utiliser dans la colère ...


Un autre "astuce" 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 géniale. Il peut aussi être écrit comme:

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

Utiliser une astuce macro stupide pour rendre les définitions d'enregistrements 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;

Voici un exemple de la façon dont le code C ne sait absolument pas ce qui est réellement utilisé 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 intéressant pour l'abstraction du code C, donc ça ne va 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;
}

en utilisant __FILE__ et __LINE__ pour le débogage

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

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

Le temps (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 que j'aille regarder la ligne incriminée et ensuite voir la vraie raison pour laquelle je voulais attirer l'attention.





c