Caractéristiques cachées de C

je sais qu'il y a un standard derrière toutes les implémentations de compilateurs C, donc il ne devrait pas y avoir de fonctionnalités cachées. Malgré cela, je suis sûr que tous les développeurs C ont des trucs cachés/secrets qu'ils utilisent tout le temps.

141
demandé sur bernardn 2008-09-25 13:02:06
la source

30 ответов

pointeurs de fonction. Vous pouvez utiliser une table de pointeurs de fonction pour mettre en œuvre, par exemple, des interpréteurs de code rapides à filetage indirect (FORTH) ou des répartiteurs de byte-code, ou pour simuler des méthodes virtuelles de type OO.

puis il y a des gemmes cachées dans la bibliothèque standard, telles que qsort(),bsearch(), strpbrk(), strcspn() [ces deux dernières étant utiles pour implémenter un remplacement de strtok ()].

Un misfeature C est que signée de dépassement de capacité arithmétique est comportement non défini (UB). Ainsi, chaque fois que vous voyez une expression telle que x+y, les deux étant signés ints, elle pourrait potentiellement déborder et causer UB.

62
répondu zvrba 2008-09-25 14:29:18
la source

plus une astuce du compilateur GCC, mais vous pouvez donner des indications de branche au compilateur (commun 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 c'est qu'il ajoute aussi un peu d'expressivité à certaines fonctions.

void foo(int arg)
{
     if (unlikely(arg == 0)) {
           do_this();
           return;
     }
     do_that();
     ...
}
117
répondu tonylo 2008-09-25 18:08:29
la source
int8_t
int16_t
int32_t
uint8_t
uint16_t
uint32_t

il s'agit d'un élément optionnel dans la norme, mais il doit être une caractéristique cachée, parce que les gens sont constamment en train de les redéfinir. Une base de code sur laquelle j'ai travaillé (et sur laquelle je travaille toujours, pour le moment) a plusieurs redéfinitions, toutes avec des identificateurs différents. La plupart du temps, il est avec préprocesseur macros:

#define INT16 short
#define INT32  long

et ainsi de suite. Ça me donne envie de tirer mes cheveux. il suffit d'utiliser les Putain de typographies integer standard!

78
répondu Ben Collins 2008-09-25 18:31:30
la source

l'opérateur virgule n'est pas très 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ù. Observe:

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
répondu Ben Collins 2008-09-25 18:37:22
la source

structure d'initialisation à zéro

struct mystruct a = {0};

cela va mettre à zéro tous les éléments de la structure.

63
répondu mike511 2008-10-01 04:34:59
la source

constantes à plusieurs caractères:

int x = 'ABCD';

cet ensemble de x à 0x41424344 (ou 0x44434241 , selon l'architecture).

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

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

c'est beaucoup plus simple si vous regardez un dump mémoire brute et avez besoin de déterminer la valeur de l'enum sans avoir à le chercher.

52
répondu Ferruccio 2011-09-11 00:27:28
la source

Je n'ai jamais utilisé bit fields mais ils sonnent cool pour des trucs ultra-bas niveau.

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


Constituée commentaires par Aaron et leppie , merci les gars.

44
répondu Motti 2017-05-23 15:17:47
la source

C a une norme mais tous les compilateurs C ne sont pas entièrement conformes (Je n'ai pas encore vu de compilateur C99 entièrement conforme!).

cela dit, les trucs que je préfère sont ceux qui ne sont pas évidents et portables à travers les plateformes car ils dépendent de la sémantique. Ils sont généralement sur les macros ou Bit arithmétique.

par exemple: échanger deux entiers non signés sans utiliser de variable temporaire:

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

ou "extension de C" à représentent des machines d'état finis comme:

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

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

qui peut être atteint 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 trucs qui sont intelligents mais rendent le code inutilement compliqué à lire (comme l'exemple de swap) et j'aime ceux qui rendent le code plus clair et véhiculent directement l'intention (comme L'exemple de FSM).

37
répondu Remo.D 2008-09-25 13:20:57
la source

Entrelacs comme dispositif de Duff :

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
répondu ComSubVie 2010-10-14 11:28:10
la source

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

#define FOO 16
#define BAR 3

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

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

33
répondu DGentry 2008-09-25 18:15:22
la source

C99 a quelques impressionnant n'importe quel ordre initialisation de structure.

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

void main(){
  struct foo f = { .y = 23, .name = "awesome", .x = -38 };
}

28
répondu 2 revs, 2 users 83%Jason 2012-01-19 19:20:06
la source

anonyme structures et des tableaux est mon préféré. (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 les listes chaînées...

27
répondu PypeBros 2008-09-25 14:16:48
la source

gcc a un certain nombre d'extensions à la langue C que j'apprécie, qui peut être trouvé ici . Certains de mes favoris sont attributs de fonction . Un exemple extrêmement utile est l'attribut format. Ceci peut être utilisé si vous définissez une fonction personnalisée qui prend un format de printf. Si vous activez cet attribut de fonction, gcc vérifiera vos arguments pour s'assurer que votre chaîne de format et vos arguments concordent et générera avertissements ou erreurs, selon le cas.

int my_printf (void *my_object, const char *my_format, ...)
            __attribute__ ((format (printf, 2, 3)));
24
répondu Russell Bryant 2008-09-29 04:27:06
la source

la caractéristique (cachée) qui m'a" choqué " quand j'ai vu pour la première fois est au sujet de printf. cette fonctionnalité vous permet d'utiliser des variables pour formater les spécificateurs de format eux-mêmes. recherchez 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
répondu kolistivra 2009-06-22 15:10:14
la source

bien... Je pense que l'un des points forts du langage C est sa portabilité et sa standardité, donc chaque fois que je trouve un "truc caché" dans l'implémentation que j'utilise actuellement, j'essaie de ne pas l'utiliser parce que j'essaie de garder mon code C aussi standard et portable que possible.

24
répondu Giacomo Degli Esposti 2009-11-03 18:00:04
la source

au moment de la Compilation des assertions 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
répondu philant 2017-05-23 15:09:44
la source

concaténation à chaîne constante

j'ai été assez surpris de ne pas le voir déjà dans les réponses, comme tous les compilateurs que je connais le soutiennent, mais beaucoup de programmeurs semble l'ignorer. Parfois, c'est très pratique et pas seulement quand on écrit des macros.

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

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 commune horrible est:

  • trois fois plus long
  • beaucoup moins facile à lire
  • beaucoup plus lent
  • moins de powerfull à lui réglé à une limite de taille de tampon arbitraire (mais vous devriez utiliser encore plus longtemps code pour éviter cela sans la contaténation de chaînes constantes).
  • utiliser plus d'espace de pile
16
répondu kriss 2010-05-08 12:22:58
la source

Eh bien, je ne l'ai jamais utilisé, et je ne sais pas si je le recommanderais à qui que ce soit, mais je pense que cette question serait incomplète sans une mention du truc de co-routine de Simon Tatham.

15
répondu Mark Baker 2008-10-17 12:54:17
la source

lors de l'initialisation des tableaux ou des enums, vous pouvez placer une virgule après le dernier élément de la liste initialiseur. e.g:

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
répondu Ferruccio 2009-06-11 15:14:49
la source

L'assignation de la structure est cool. Beaucoup de gens ne semblent pas réaliser que les structures sont des valeurs aussi, et peuvent être attribuées autour, il n'y a pas besoin d'utiliser memcpy() , quand une simple tâche fait l'affaire.

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

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

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

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

ceci est sûr, aussi longtemps (bien sûr) que la valeur de retour est copiée par valeur en utilisant l'assignation de struct:

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

de cette façon, vous pouvez écrire un code ish tout à fait propre et orienté objet, tout en standard C.

12
répondu unwind 2010-10-25 12:32:49
la source

Étrange vecteur d'indexation:

int v[100]; int index = 10; 
/* v[index] it's the same thing as index[v] */
10
répondu INS 2008-09-25 13:30:28
la source

C Compilateurs mettre en œuvre une de plusieurs normes. Cependant, une norme ne signifie pas que tous les aspects de la langue sont définis. dispositif de Duff , par exemple, est un favori "caché" caractéristique qui est devenu si populaire que les compilateurs modernes ont un code de reconnaissance à des fins spéciales pour assurer que les techniques d'optimisation ne sabotent pas l'effet désiré de ce modèle souvent utilisé.

en général des traits cachés ou des trucs de langue sont découragés car vous courez sur le bord du rasoir de n'importe quel standard C(s) que votre compilateur utilise. Beaucoup de ces astuces ne fonctionnent pas d'un compilateur à l'autre, et souvent ces types de fonctionnalités échouent d'une version d'une suite de compilateurs par un fabricant donné à une autre version.

divers trucs qui ont cassé le code C comprennent:

  1. S'appuyant sur la façon dont le compilateur affiche les structures en mémoire.
  2. hypothèses sur boutisme d'entiers/flotteurs.
  3. Hypothèses sur la fonction ABIs.
  4. Hypothèses sur l'orientation de la pile d'images de grandir.
  5. Hypothèses au sujet de l'ordre d'exécution à l'intérieur des états.
  6. suppositions sur l'ordre d'exécution des énoncés dans les arguments de fonction.
  7. hypothèses sur la taille des bits ou la précision des types court, int, long, float et double.

autres problèmes et questions qui se posent chaque fois que les programmeurs font des hypothèses sur les modèles d'exécution qui sont tous spécifiés dans la plupart des normes C comme comportement "dépendant du compilateur".

9
répondu Kevin S. 2008-09-25 13:26:19
la source

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

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

apparemment, vous ne pouvez pas ajouter une autre réponse, donc je vais inclure une deuxième 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 affichera:

Hi
ROFL
9
répondu onemasse 2011-05-26 06:20:38
la source

utilisant INT (3) pour définir le point d'arrêt au code est mon préféré de tous les temps

8
répondu Dror Helper 2008-09-25 13:06:39
la source

ma caractéristique "cachée" préférée de C, est l'utilisation de %n dans printf pour écrire de nouveau à la pile. Normalement printf affiche les valeurs des paramètres de la pile sur la base de la chaîne de format, mais %n peut les écrire en retour.

consultez la section 3.4.2 ici . Peut conduire à beaucoup de mauvaises vulnérabilités.

8
répondu Sridhar Iyer 2008-09-27 00:18:15
la source

au moment de la Compilation hypothèse-vérification à l'aide des enums: Exemple stupide, mais peut être vraiment utile pour les bibliothèques avec des constantes configurables lors 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
répondu S.C. Madsen 2009-11-11 16:42:11
la source

Gcc( c) a quelques fonctionnalités amusantes que vous pouvez activer, telles que les déclarations de fonction imbriquées, et le a?:b forme de la ?: opérateur, qui renvoie un si a n'est pas faux.

8
répondu Alex Brown 2011-05-26 06:32:16
la source

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: 0;

0000aaab bccccddd

le champ 0 indique que les champs de bits suivants doivent être positionnés sur la prochaine entité atomique ( char )

8
répondu tristopia 2011-09-11 00:58:31
la source

C99-style variable argument macros, alias

#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 aussi l'opérateur stringize et la concatentation constante string, d'autres fonctionnalités que j'aime vraiment.

7
répondu Ben Combee 2008-10-22 22:00:24
la source

Variable taille variables automatiques sont également utiles dans certains cas. Ceux-ci ont été ajoutés en 1999 et ont été pris en charge dans gcc depuis 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 l'extraPadding est une valeur raisonnable avant d'appeler cette routine, ou vous finir par faire sauter la pile. Vous devriez vérifier les arguments avant d'appeler malloc ou n'importe quelle autre technique d'allocation de mémoire, donc ce n'est pas vraiment inhabituel.

6
répondu DGentry 2008-09-25 18:23:20
la source

Autres questions sur c hidden-features