web-dev-qa-db-fra.com

Caractéristiques cachées de C

Je sais qu'il existe un standard derrière toutes les implémentations du compilateur C, il ne devrait donc pas y avoir de fonctionnalités cachées. Malgré cela, je suis sûr que tous les développeurs C ont des astuces cachées/secrètes qu’ils utilisent tout le temps.

141
bernardn

Pointeurs de fonction. Vous pouvez utiliser une table de pointeurs de fonction pour implémenter, par exemple, des interpréteurs rapides de code à thread indirect (FORTH) ou des répartiteurs de code octet, ou pour simuler des méthodes virtuelles analogues à des objets orientés objet.

Il existe ensuite des gemmes cachées dans la bibliothèque standard, telles que qsort (), bsearch (), strpbrk (), strcspn () [ces deux dernières étant utiles pour la mise en œuvre d'un remplacement de strtok ()].

Un défaut de fonctionnalité de C est que le débordement arithmétique signé est un comportement indéfini (UB). Ainsi, chaque fois que vous voyez une expression telle que x + y, les deux étant signées ints, elle peut potentiellement déborder et causer UB.

62
zvrba

Il s’agit plutôt d’une astuce du compilateur GCC, mais vous pouvez donner des indications d’indication de branche au compilateur (courant dans le noyau Linux).

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

voir: http://kerneltrap.org/node/4705

Ce que j'aime dans cette fonction, c'est que cela ajoute également une certaine expressivité à certaines fonctions.

void foo(int arg)
{
     if (unlikely(arg == 0)) {
           do_this();
           return;
     }
     do_that();
     ...
}
116
tonylo
int8_t
int16_t
int32_t
uint8_t
uint16_t
uint32_t

Celles-ci sont un élément facultatif de la norme, mais il doit s'agir d'une fonctionnalité cachée, car les utilisateurs le redéfinissent constamment. Une base de code sur laquelle j'ai travaillé (et le fais toujours pour l'instant) comporte plusieurs redéfinitions, toutes avec des identifiants différents. La plupart du temps, c'est avec des macros de préprocesseur:

#define INT16 short
#define INT32  long

Etc. Cela me donne envie de me tirer les cheveux. Il suffit d'utiliser les types entiers standard effrayants!

77
Ben Collins

L'opérateur par virgule n'est pas largement utilisé. On peut certes en abuser, mais cela peut aussi être très utile. Cette utilisation est la plus courante:

for (int i=0; i<10; i++, doSomethingElse())
{
  /* whatever */
}

Mais vous pouvez utiliser cet opérateur n'importe où. Observer:

int j = (printf("Assigning variable j\n"), getValueFromSomewhere());

Chaque instruction est évaluée, mais la valeur de l'expression sera celle de la dernière instruction évaluée.

73
Ben Collins

structure d'initialisation à zéro

struct mystruct a = {0};

cela mettra à zéro tous les éléments de structure.

63
mike511

Constantes multi-caractères:

int x = 'ABCD';

Ceci définit x sur 0x41424344 (ou 0x44434241, selon l'architecture).

EDIT: Cette technique n'est pas portable, surtout si vous sérialisez l'int. Cependant, il peut être extrêmement utile de créer des énumérations auto-documentées. par exemple.

enum state {
    stopped = 'STOP',
    running = 'RUN!',
    waiting = 'WAIT',
};

Cela est beaucoup plus simple si vous envisagez un vidage brut de la mémoire et que vous devez déterminer la valeur d’une énumération sans avoir à la chercher.

52
Ferruccio

Je n'ai jamais utilisé champs de bits mais ils sonnent bien pour des contenus ultra-bas.

struct cat {
    unsigned int legs:3;  // 3 bits for legs (0-4 fit in 3 bits)
    unsigned int lives:4; // 4 bits for lives (0-9 fit in 4 bits)
    // ...
};

cat make_cat()
{
    cat kitty;
    kitty.legs = 4;
    kitty.lives = 9;
    return kitty;
}

Cela signifie que sizeof(cat) peut être aussi petit que sizeof(char).


Commentaires incorporés par Aaron et leppie , merci les gars.

44
Motti

Entrelacement de structures telles que Duff's Device :

strncpy(to, from, count)
char *to, *from;
int count;
{
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}
37
ComSubVie

C a une norme mais tous les compilateurs C ne sont pas totalement compatibles (je n’ai pas encore vu de compilateur C99 totalement compatible!).

Cela dit, les astuces que je préfère sont celles qui ne sont pas évidentes et portables sur toutes les plateformes, car elles reposent sur la sémantique C. Ils concernent généralement les macros ou les calculs arithmétiques.

Par exemple, permuter deux entiers non signés sans utiliser de variable temporaire:

...
a ^= b ; b ^= a; a ^=b;
...

ou "extension de C" pour représenter des machines à états finis comme:

FSM {
  STATE(x) {
    ...
    NEXTSTATE(y);
  }

  STATE(y) {
    ...
    if (x == 0) 
      NEXTSTATE(y);
    else 
      NEXTSTATE(x);
  }
}

cela peut être réalisé avec les macros suivantes:

#define FSM
#define STATE(x)      s_##x :
#define NEXTSTATE(x)  goto s_##x

En général, cependant, je n'aime pas les astuces astucieuses, mais rend le code inutilement compliqué à lire (exemple d'échange) et j'aime celles qui rendent le code plus clair et traduisant directement l'intention (comme dans l'exemple de FSM). .

37
Remo.D

J'aime beaucoup les initialiseurs désignés, ajoutés en C99 (et supportés depuis longtemps dans gcc):

#define FOO 16
#define BAR 3

myStructType_t myStuff[] = {
    [FOO] = { foo1, foo2, foo3 },
    [BAR] = { bar1, bar2, bar3 },
    ...

L'initialisation de la matrice n'est plus dépendante de la position. Si vous modifiez les valeurs de FOO ou de BAR, l'initialisation du tableau correspondra automatiquement à leur nouvelle valeur.

33
DGentry

C99 a une impressionnante initialisation de structure quelconque.

struct foo{
  int x;
  int y;
  char* name;
};

void main(){
  struct foo f = { .y = 23, .name = "awesome", .x = -38 };
}
</ code>
28
Jason

les structures et les tableaux anonymes sont ceux que je préfère. (cf. http://www.run.montefiore.ulg.ac.be/~martin/resources/kung-f00.html )

setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));

ou

void myFunction(type* values) {
    while(*values) x=*values++;
}
myFunction((type[]){val1,val2,val3,val4,0});

il peut même être utilisé pour instancier des listes chaînées ...

27
PypeBros

la fonctionnalité (cachée) qui m'a "choqué" quand j'ai vu la première fois est sur printf. Cette fonctionnalité vous permet d'utiliser des variables pour le formatage des spécificateurs de format eux-mêmes. cherchez le code, vous verrez mieux:

#include <stdio.h>

int main() {
    int a = 3;
    float b = 6.412355;
    printf("%.*f\n",a,b);
    return 0;
}

le caractère * réalise cet effet.

24
kolistivra

gcc a un certain nombre d'extensions du langage C que j'apprécie, que l'on peut trouver ici . Certains de mes favoris sont attributs de fonction . Un exemple extrêmement utile est l'attribut format. Cela peut être utilisé si vous définissez une fonction personnalisée prenant une chaîne de format printf. Si vous activez cet attribut de fonction, gcc vérifiera vos arguments pour vous assurer que la chaîne de formatage et les arguments correspondent, et générera des avertissements ou des erreurs le cas échéant.

int my_printf (void *my_object, const char *my_format, ...)
            __attribute__ ((format (printf, 2, 3)));
24
Russell Bryant

Eh bien ... Je pense que l’un des points forts du langage C est sa portabilité et son standard. Aussi, chaque fois que je découvre un "truc caché" dans la mise en oeuvre que j’utilise actuellement, j’essaie de ne pas l’utiliser, car j’essaie de garder mon Code C en standard et portable que possible.

24

Assertions à la compilation, comme déjà discuté ici .

//--- size of static_assertion array is negative if condition is not met
#define STATIC_ASSERT(condition) \
    typedef struct { \
        char static_assertion[condition ? 1 : -1]; \
    } static_assertion_t

//--- ensure structure fits in 
STATIC_ASSERT(sizeof(mystruct_t) <= 4096);
19
philant

Concaténation de chaîne constante

J'ai été assez surpris de ne pas le voir déjà dans les réponses, car tous les compilateurs que je connais le soutiennent, mais beaucoup de programmeurs semblent l'ignorer. Parfois, c'est vraiment pratique et pas seulement lors de l'écriture de macros.

Cas d'utilisation que j'ai dans mon code actuel: J'ai un #define PATH "/some/path/" dans un fichier de configuration (en réalité, il est défini par le fichier makefile). Maintenant, je veux construire le chemin complet, y compris les noms de fichiers, pour ouvrir des ressources. Cela va juste à:

fd = open(PATH "/file", flags);

Au lieu de l'horrible, mais très commun:

char buffer[256];
snprintf(buffer, 256, "%s/file", PATH);
fd = open(buffer, flags);

Notez que la solution horrible commune est:

  • trois fois plus longtemps
  • beaucoup moins facile à lire
  • beaucoup plus lent
  • moins puissant avec une limite de taille de tampon arbitraire (mais vous devrez utiliser un code encore plus long pour éviter cela sans une contaténation constante des chaînes).
  • utiliser plus d'espace de pile
16
kriss

Eh bien, je ne l'ai jamais utilisée, et je ne suis pas sûre de l'avoir jamais recommandée à qui que ce soit, mais je pense que cette question serait incomplète sans une mention du copropriété de Simon Tatham . astuce de routine.

15
Mark Baker

Lors de l'initialisation de tableaux ou d'énums, vous pouvez mettre une virgule après le dernier élément de la liste d'initialisation. par exemple:

int x[] = { 1, 2, 3, };

enum foo { bar, baz, boom, };

Cela a été fait de sorte que si vous générez du code automatiquement, vous n'avez pas à vous soucier d'éliminer la dernière virgule.

12
Ferruccio

L'affectation des structures est cool. Beaucoup de gens ne semblent pas se rendre compte que les struct sont aussi des valeurs, et peuvent être assignés, il n’est pas nécessaire d’utiliser memcpy(), quand une simple affectation fait l'affaire.

Par exemple, considérons une bibliothèque graphique 2D imaginaire, elle pourrait définir un type pour représenter une coordonnée d'écran (entière):

typedef struct {
   int x;
   int y;
} Point;

Maintenant, vous faites des choses qui peuvent sembler "mauvaises", comme écrire une fonction qui crée un point initialisé à partir d'arguments de fonction et le retourne, comme suit:

Point point_new(int x, int y)
{
  Point p;
  p.x = x;
  p.y = y;
  return p;
}

C'est sûr tant que (bien sûr) la valeur de retour est copiée par valeur en utilisant l'affectation de la structure:

Point Origin;
Origin = point_new(0, 0);

De cette façon, vous pouvez écrire du code assez propre et orienté objet, le tout en C standard.

12
unwind

Indexation vectorielle étrange:

int v[100]; int index = 10; 
/* v[index] it's the same thing as index[v] */
10
INS

Lorsque vous utilisez sscanf, vous pouvez utiliser% n pour savoir où vous devriez continuer à lire:

sscanf ( string, "%d%n", &number, &length );
string += length;

Apparemment, vous ne pouvez pas ajouter une autre réponse, je vais donc en inclure une seconde ici, vous pouvez utiliser "&&" et "||" comme conditionnels:

#include <stdio.h>
#include <stdlib.h>

int main()
{
   1 || puts("Hello\n");
   0 || puts("Hi\n");
   1 && puts("ROFL\n");
   0 && puts("LOL\n");

   exit( 0 );
}

Ce code produira:

Bonjour 
 ROFL
9
onemasse

Les compilateurs C implémentent l’une des normes. Cependant, avoir une norme ne signifie pas que tous les aspects du langage sont définis. Le périphérique de Duff , par exemple, est une fonction "cachée" favorite qui est devenue si populaire que les compilateurs modernes disposent d’un code de reconnaissance spécial pour s’assurer que les techniques d’optimisation ne gênent pas l’effet souhaité de ce motif souvent utilisé.

En général, les fonctionnalités cachées ou les astuces de langage sont déconseillés, car vous utilisez le rasoir Edge, quelle que soit la norme C utilisée par le compilateur. Beaucoup de ces astuces ne fonctionnent pas d'un compilateur à un autre, et souvent ce type de fonctionnalité échouera d'une version d'une suite de compilateur d'un fabricant donné à une autre version.

Les différentes astuces qui ont cassé le code C comprennent:

  1. S'appuyant sur la façon dont le compilateur établit les structures en mémoire.
  2. Hypothèses sur finalité des entiers/floats.
  3. Hypothèses sur les ABI de fonction.
  4. Hypothèses sur la direction dans laquelle les cadres de pile grandissent.
  5. Hypothèses relatives à l'ordre d'exécution dans les instructions.
  6. Hypothèses relatives à l'ordre d'exécution des instructions dans les arguments de fonction.
  7. Hypothèses sur la taille en bits ou la précision des types court, int, long, float et double.

D'autres problèmes et problèmes surviennent lorsque les programmeurs émettent des hypothèses sur des modèles d'exécution qui sont tous spécifiés dans la plupart des normes C en tant que comportement "dépendant du compilateur".

9
Kevin S.

Gcc (c) possède certaines fonctionnalités amusantes que vous pouvez activer, telles que les déclarations de fonctions imbriquées et la forme a?: B de l'opérateur?: Qui renvoie a si a n'est pas faux.

8
Alex Brown

Vérification des hypothèses au moment de la compilation à l'aide d'énums: Exemple stupide, mais peut être très utile pour les bibliothèques avec des constantes configurables au moment de la compilation.

#define D 1
#define DD 2

enum CompileTimeCheck
{
    MAKE_SURE_DD_IS_TWICE_D = 1/(2*(D) == (DD)),
    MAKE_SURE_DD_IS_POW2    = 1/((((DD) - 1) & (DD)) == 0)
};
8
S.C. Madsen

J'ai découvert récemment 0 bitfields.

struct {
  int    a:3;
  int    b:2;
  int     :0;
  int    c:4;
  int    d:3;
};

qui donnera une mise en page de

000aaabb 0ccccddd

au lieu de sans le: 0;

0000aaab bccccddd

Le champ largeur 0 indique que les champs de bits suivants doivent être définis sur la prochaine entité atomique (char)

8
Patrick Schlüter

Ma fonctionnalité "cachée" préférée de C est l'utilisation de% n dans printf pour écrire dans la pile. Normalement, printf extrait les valeurs de paramètre de la pile en fonction de la chaîne de format, mais% n peut les réécrire.

Consultez la section 3.4.2 ici . Peut conduire à de nombreuses vulnérabilités.

8
Sridhar Iyer

utiliser INT (3) pour définir le point d'arrêt du code est mon préféré

8
Dror Helper

Macros d'arguments variables de style C99, aka

#define ERR(name, fmt, ...)   fprintf(stderr, "ERROR " #name ": " fmt "\n", \
                                  __VAR_ARGS__)

qui serait utilisé comme

ERR(errCantOpen, "File %s cannot be opened", filename);

Ici, j'utilise également l'opérateur stringize et la concaténation de constante de chaîne, d'autres fonctionnalités que j'aime beaucoup.

7
Ben Combee

Les variables automatiques de taille variable sont également utiles dans certains cas. Ceux-ci ont été ajoutés dans nC99 et ont été pris en charge dans gcc pendant longtemps.

void foo(uint32_t extraPadding) {
    uint8_t commBuffer[sizeof(myProtocol_t) + extraPadding];

Vous vous retrouvez avec un tampon sur la pile avec de la place pour l'en-tête de protocole de taille fixe plus les données de taille variable. Vous pouvez obtenir le même effet avec alloca (), mais cette syntaxe est plus compacte.

Vous devez vous assurer que extraPadding est une valeur raisonnable avant d'appeler cette routine, sinon vous ferez sauter la pile. Avant de faire appel à malloc ou à une autre technique d’allocation de mémoire, vous devez vérifier les arguments de manière rationnelle. Ce n’est donc pas vraiment inhabituel.

6
DGentry

J'ai aimé les structures de tailles variables que vous pourriez faire:

typedef struct {
    unsigned int size;
    char buffer[1];
} tSizedBuffer;

tSizedBuffer *buff = (tSizedBuffer*)(malloc(sizeof(tSizedBuffer) + 99));

// can now refer to buff->buffer[0..99].

Également la macro offsetof qui est maintenant en ANSI C mais était un brin de magie la première fois que je l'ai vue. Il utilise essentiellement l'opérateur adresse-sur (&) pour une refonte de pointeur null en tant que variable de structure.

5
paxdiablo
5
Steve Webb

Lambda (fonctions anonymes, par exemple) dans GCC:

#define lambda(return_type, function_body) \
    ({ return_type fn function_body fn })

Ceci peut être utilisé comme:

lambda (int, (int x, int y) { return x > y; })(1, 2)

Qui est étendu à:

({ int fn (int x, int y) { return x > y } fn; })(1, 2)
5
Joe D

Pour effacer le tampon d'entrée, vous ne pouvez pas utiliser fflush(stdin). La manière correcte est la suivante: scanf("%*[^\n]%*c") Ceci éliminera tout du tampon d'entrée.

4
Tomas Senart

Je ne l'ai découvert qu'après 15 ans de programmation en C:

struct SomeStruct
{
   unsigned a : 5;
   unsigned b : 1;
   unsigned c : 7;
};

Bitfields! Le nombre après les deux-points est le nombre de bits requis par le membre, les membres étant regroupés dans le type spécifié. Par conséquent, le texte ci-dessus ressemblerait à ce qui suit si unsigned est 16 bits:

xxxc cccc ccba aaaa

Skizz

3
Skizz

Conversion de types en utilisant des typecasts inhabituels. Bien que ce ne soit pas une fonctionnalité cachée, c'est assez délicat.

Exemple:

Si vous avez besoin de savoir comment le compilateur stocke float, essayez ceci:

uint32_t Int;
float flt = 10.5; // say

Int = *(uint32_t *)&flt;

printf ("Float 10.5 is stored internally as %8X\n", Int);

ou

float flt = 10.5; // say

printf ("Float 10.5 is stored internally as %8X\n", *(uint32_t *)&flt);

Notez l'utilisation intelligente des typecasts. Conversion de l'adresse de la variable (ici & flt) en type souhaité (ici (uint32_t *)) et extraction de son contenu (application '*').

Cela fonctionne aussi de l'autre côté de l'expression:

*(float *)&Int = flt;

Cela pourrait également être accompli en utilisant l'union:

typedef union
{
  uint32_t Int;
  float    flt;

} FloatInt_type;
3
yogeesh

Les premières versions de gcc ont tenté de lancer un jeu chaque fois qu’il rencontrait "#pragma" dans le code source. Voir aussi ici .

3
Sec

On m'a montré cela dans un peu de code une fois, et j'ai demandé ce que ça faisait:


hexDigit = "0123456789abcdef"[someNybble];

Un autre favori est:


unsigned char bar[100];
unsigned char *foo = bar;
unsigned char blah = 42[foo];
3
Andrew Edgecombe

Lorsque vous comparez une variable à un littéral, il est préférable de mettre le littéral à l'opérateur à gauche de l'opérateur ==, Afin de vous assurer que le compilateur génère une erreur lorsque vous utilisez l'assignation par erreur. opérateur à la place.

if (0 == count) {
    ...
}

Cela peut paraître étrange au premier abord, mais cela pourrait vous épargner des maux de tête (comme si vous tapiez if (count = 0) par erreur).

2
Vicky Chijwani

Steve Webb a souligné le __LINE__ et __FILE__ macros. Cela me rappelle à quel point, dans mon travail précédent, je les avais piratés pour avoir une journalisation en mémoire.

Je travaillais sur un périphérique où aucun port n'était disponible pour transmettre les informations de journalisation d'un périphérique à un PC utilisé pour le débogage. On pouvait utiliser des points d'arrêt pour arrêter et connaître l'état du programme à l'aide du débogueur, mais il n'y avait aucune information sur la trace du système.

Étant donné que tous les appels aux journaux de débogage constituaient en réalité une seule macro globale, nous avons modifié cette macro pour transférer le nom de fichier et le numéro de ligne dans un tableau global. Ce tableau contenait une série de noms de fichiers et de numéros de lignes indiquant les appels de débogage qui ont été appelés, donnant ainsi une bonne idée de la trace de l'exécution (pas le message de journal réel cependant). Vous pouvez suspendre l'exécution par le débogueur, vider ces octets dans un fichier local, puis mapper ces informations sur la base de code à l'aide de scripts. Cela a été rendu possible parce que nous avions des directives de codage strictes, nous pouvions donc apporter des modifications au mécanisme de journalisation dans un seul fichier.

2
thequark

intptr_t pour déclarer des variables de type pointeur. C99 spécifique et déclaré dans stdint.h

2
user445161

Ce n'est pas vraiment une fonctionnalité cachée, mais cela me semblait être du vaudou, la première fois que j'ai vu quelque chose comme ceci:


void callback(const char *msg, void *data)
{
    // do something with msg, e.g.
    printf("%s\n", msg);

    return;
    data = NULL;
}

La raison de cette construction est que, si vous le compilez avec -Wextra et sans la ligne "data = NULL;", gcc émettra un avertissement concernant les paramètres inutilisés. Mais avec cette ligne inutile, vous ne recevez pas d'avertissement.

EDIT: Je sais qu’il existe d’autres (meilleurs) moyens de prévenir ces avertissements. Cela me semblait étrange, la première fois que j'ai vu ça.

2
quinmars

La taille des pointeurs de fonction n'est pas standard. Du moins pas dans le livre de K & R. Même si on parle de la taille des autres types de pointeurs, mais (je pense) sizeof d'un pointeur de fonction est un comportement indéfini.

Aussi sizeof est un opérateur de compilation, je vois beaucoup de gens qui demandent si sizeof est une fonction ou un opérateur dans les forums en ligne.

Une erreur que j'ai vue est la suivante (exemple simplifié):

int j;
int i;
j = sizeof(i++)

l'incrément sur i ne serait pas exécuté car sizeof était évalué au moment de la compilation. Le programmeur avait l'intention de pirater les deux opérations, d'incrémenter i et de calculer sizeof en une seule déclaration.

La priorité de l'opérateur en C régit l'ordre d'association et non l'ordre d'évaluation. Par exemple, si vous avez trois fonctions f, g, h retournant chacune un int, et leur expression est la suivante:

f() + g() * h()

La norme C ne donne pas de règle sur l'ordre d'évaluation de ces fonctions. Le résultat de g et h serait multiplié avant d'ajouter le résultat de f. Cela peut entraîner des erreurs si les fonctions partagent un état et que le calcul dépend de l'ordre d'évaluation de ces fonctions. Cela peut entraîner des problèmes de portabilité.

1
thequark

Extrait :

Dans cette page, vous trouverez une liste de questions/énigmes intéressantes concernant la programmation en C. Ces programmes sont ceux que j’ai reçus par courrier électronique de la part de mes amis, quelques-uns que j’ai lus dans certains livres, quelques-uns sur Internet, et quelques-unes de mes expériences de codage en C.

http://www.gowrikumar.com/c/index.html

1
Özgür

enregistrer les variables

J'avais l'habitude de déclarer certaines variables avec le mot-clé register pour accélérer les choses. Cela indiquerait au compilateur C d'utiliser un registre de CPU comme stockage local. Ce n'est probablement plus nécessaire puisque les compilateurs modernes le font automatiquement.

1
Mark Stock

Supposons que vous ayez une structure avec des membres du même type:

struct Point {
    float x;
    float y;
    float z;
};

Vous pouvez en convertir des occurrences en un pointeur flottant et utiliser des index de tableau:

Point a;
int sum = 0, i = 0;
for( ; i < 3; i++)
    sum += ((float*)a)[i];

Assez élémentaire, mais utile pour écrire du code concis.

1
aeflash

Le souvent oublié %n _ spécificateur dans printf chaîne de format peut être assez pratique parfois. % n renvoie la position actuelle du curseur imaginaire utilisé lorsque printf formate sa sortie.

int pos1, pos2;
 char *string_of_unknown_length = "we don't care about the length of this";

  printf("Write text of unknown %n(%s)%n text\n", &pos1, string_of_unknown_length, &pos2);
  printf("%*s\\%*s/\n", pos1, " ", pos2-pos1-2, " ");
  printf("%*s", pos1+1, " ");
  for(int i=pos1+1; i<pos2-1; i++)
    putc('-', stdout);
  putc('\n', stdout);

aura la sortie suivante

Write text of unknown (we don't care about the length of this) text
                      \                                      /
                       --------------------------------------

Accordé un peu artificiel, mais peut avoir des utilisations pour faire de jolis rapports.

0
Patrick Schlüter

Utilisez NaN pour les calculs chaînés/le retour d'erreur:

// # include <stdint.h>
static uint64_t iNaN = 0xFFF8000000000000;
const double NaN = * (double *) & iNaN; // calme NaN

Une fonction interne peut renvoyer NaN en tant qu'indicateur d'erreur: elle peut être utilisée en toute sécurité dans tout calcul et le résultat sera toujours NaN.

remarque: tester NaN est délicat, puisque NaN! = NaN ... utilisez isnan (x) ou lancez le vôtre.
x! = x est mathématiquement correct si x est NaN, mais a tendance à être optimisé par certains compilateurs

0
Adrian Sietsma

J'aime l'opérateur typeof (). Cela fonctionne comme sizeof () en ce sens qu’il est résolu au moment de la compilation. Au lieu de renvoyer le nombre d'octets, il renvoie le type. Ceci est utile lorsque vous devez déclarer une variable du même type que toute autre variable, quel que soit son type.

typeof(foo) copy_of_foo; //declare bar to be a variable of the same type as foo
copy_of_foo = foo; //now copy_of_foo has a backup of foo, for any type

Cela pourrait être juste une extension de gcc, je ne suis pas sûr.

0
Eyal

Je viens de lire ceci article . Il possède du C et plusieurs autres langues "fonctions cachées".

0
Rigo Vides

Pourquoi ne pas utiliser while (0) dans un commutateur pour pouvoir utiliser des instructions continue telles que break :-)

void sw(int s)
{
    switch (s) while (0) {
    case 0:
        printf("zero\n");
        continue;
    case 1:
        printf("one\n");
        continue;
    default:
        printf("something else\n");
        continue;
    }
}
0
Colin King