Je sais qu'il existe une norme 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.
c
hidden-features
bernardn
la source
la source
Réponses:
Pointeurs de fonction. Vous pouvez utiliser une table de pointeurs de fonction pour implémenter, par exemple, des interpréteurs de code à thread indirect rapide (FORTH) ou des répartiteurs de code d'octet, ou pour simuler des méthodes virtuelles de type OO.
Ensuite, il y a des gemmes cachées dans la bibliothèque standard, telles que qsort (), bsearch (), strpbrk (), strcspn () [les deux derniers étant utiles pour implémenter un remplacement strtok ()].
Un défaut 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, tous deux signés ints, cela peut potentiellement déborder et provoquer UB.
la source
Plus une astuce du compilateur GCC, mais vous pouvez donner des indices d'indication de branche au compilateur (commun dans le noyau Linux)
voir: http://kerneltrap.org/node/4705
Ce que j'aime à ce sujet, c'est que cela ajoute également une certaine expressivité à certaines fonctions.
la source
Il s'agit d'un élément facultatif dans la norme, mais il doit s'agir d'une fonctionnalité masquée, car les gens les redéfinissent constamment. Une base de code sur laquelle j'ai travaillé (et je le fais encore, pour l'instant) a plusieurs redéfinitions, toutes avec des identifiants différents. La plupart du temps, c'est avec des macros de préprocesseur:
Etc. Cela me donne envie de m'arracher les cheveux. Utilisez simplement les typedefs entiers standards!
la source
L'opérateur virgule n'est pas largement utilisé. Il peut certes être abusé, mais il peut aussi être très utile. Cette utilisation est la plus courante:
Mais vous pouvez utiliser cet opérateur n'importe où. Observer:
Chaque instruction est évaluée, mais la valeur de l'expression sera celle de la dernière instruction évaluée.
la source
initialisation de la structure à zéro
cela mettra à zéro tous les éléments de structure.
la source
memset
/calloc
do "tous les octets zéro" (c'est-à-dire les zéros physiques), qui n'est en effet pas défini pour tous les types.{ 0 }
est garanti de tout intilaize avec des valeurs zéro logiques appropriées . Les pointeurs, par exemple, sont garantis pour obtenir leurs valeurs nulles appropriées, même si la valeur nulle sur la plate-forme donnée est0xBAADFOOD
.memset
fait (avec0
comme deuxième argument). Vous obtenez un zéro logique lorsque vous initialisez / affectez0
(ou{ 0 }
) à l'objet dans le code source. Ces deux types de zéros ne produisent pas nécessairement le même résultat. Comme dans l'exemple avec pointeur. Lorsque vous faitesmemset
sur un pointeur, vous obtenez un0x0000
pointeur. Mais lorsque vous affectez0
à un pointeur, vous obtenez une valeur de pointeur nulle , ce qui au niveau physique peut être0xBAADF00D
ou autre chose.double
,. Habituellement, il est implémenté conformément à la norme IEEE-754, dans laquelle le zéro logique et le zéro physique sont identiques. Mais IEEE-754 n'est pas requis par la langue. Il se peut donc que lorsque vous faitesdouble d = 0;
(zéro logique), physiquement certains bits de la mémoire occupés pard
ne soient pas zéro.Constantes multi-caractères:
Cela définit
x
à0x41424344
(ou0x44434241
, 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
Cela rend les choses beaucoup plus simples si vous regardez un vidage de la mémoire brute et que vous devez déterminer la valeur d'une énumération sans avoir à la rechercher.
la source
Je n'ai jamais utilisé de champs de bits, mais ils sonnent bien pour les trucs à très bas niveau.
Cela signifie que cela
sizeof(cat)
peut être aussi petit quesizeof(char)
.Commentaires incorporés par Aaron et leppie , merci les gars.
la source
C a une norme mais tous les compilateurs C ne sont pas entièrement compatibles (je n'ai pas encore vu de compilateur C99 entièrement conforme!).
Cela dit, les astuces que je préfère sont celles qui ne sont pas évidentes et portables sur toutes les plates-formes car elles reposent sur la sémantique C. Il s'agit généralement de macros ou d'arithmétique binaire.
Par exemple: permuter deux entiers non signés sans utiliser de variable temporaire:
ou "étendre C" pour représenter des machines à états finis comme:
cela peut être réalisé avec les macros suivantes:
En général, cependant, je n'aime pas les astuces qui sont intelligentes mais rendent le code inutilement compliqué à lire (comme l'exemple d'échange) et j'aime celles qui rendent le code plus clair et transmettent directement l'intention (comme l'exemple FSM) .
la source
Structures entrelacées comme le dispositif de Duff :
la source
J'aime beaucoup les initialiseurs désignés, ajoutés dans C99 (et pris en charge dans gcc depuis longtemps):
L'initialisation du tableau ne dépend plus de la position. Si vous modifiez les valeurs de FOO ou BAR, l'initialisation du tableau correspondra automatiquement à leur nouvelle valeur.
la source
C99 a une super initialisation de structure dans n'importe quel ordre.
la source
les structures et tableaux anonymes sont mes préférés. (cf. http://www.run.montefiore.ulg.ac.be/~martin/resources/kung-f00.html )
ou
il peut même être utilisé pour instancier des listes chaînées ...
la source
gcc a un certain nombre d'extensions au langage C que j'apprécie, qui peuvent être trouvées ici . Certains de mes favoris sont des attributs de fonction . Un exemple extrêmement utile est l'attribut format. Cela peut être utilisé si vous définissez une fonction personnalisée qui prend une chaîne de format 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 correspondent et générera des avertissements ou des erreurs selon le cas.
la source
la fonction (cachée) qui m'a "choqué" quand j'ai vu pour la première fois concerne printf. cette fonctionnalité vous permet d'utiliser des variables pour formater les spécificateurs de format eux-mêmes. cherchez le code, vous verrez mieux:
le caractère * réalise cet effet.
la source
Eh 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'essaye de ne pas l'utiliser car j'essaye de garder mon Code C aussi standard et portable que possible.
la source
Assertions au moment de la compilation, comme déjà discuté ici .
la source
Concaténation de chaînes 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 supportent, mais de nombreux 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 fait, il est réglé par le makefile). Maintenant, je veux créer le chemin complet avec les noms de fichiers pour ouvrir les ressources. Cela va simplement à:Au lieu de l'horrible, mais très commun:
Notez que la solution horrible commune est:
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 de l'astuce de co-routine de Simon Tatham .
la source
Lors de l'initialisation de tableaux ou d'énumérations, vous pouvez mettre une virgule après le dernier élément de la liste d'initialisation. par exemple:
Cela a été fait pour que si vous générez du code automatiquement, vous n'ayez pas à vous soucier d'éliminer la dernière virgule.
la source
L'affectation des structures est cool. Beaucoup de gens ne semblent pas se rendre compte que les structures sont aussi des valeurs et peuvent être attribuées, il n'est pas nécessaire d'utiliser
memcpy()
, lorsqu'une simple affectation fait l'affaire.Par exemple, considérons une bibliothèque de graphiques 2D imaginaires, elle pourrait définir un type pour représenter une coordonnée d'écran (entière):
Maintenant, vous faites des choses qui peuvent sembler "incorrectes", comme écrire une fonction qui crée un point initialisé à partir des arguments de fonction, et le renvoie, comme ceci:
C'est sûr, tant (bien sûr) que la valeur de retour est copiée par valeur à l'aide de l'affectation de structure:
De cette façon, vous pouvez écrire du code assez propre et orienté objet, le tout en C.
la source
Indexation vectorielle étrange:
la source
Les compilateurs C implémentent l'une des nombreuses normes. Cependant, avoir une norme ne signifie pas que tous les aspects de la langue sont définis. L'appareil de Duff , par exemple, est une fonctionnalité `` cachée '' préférée qui est devenue si populaire que les compilateurs modernes ont un code de reconnaissance à usage spécial pour s'assurer que les techniques d'optimisation ne sabotent pas l'effet souhaité de ce modèle souvent utilisé.
En général, les fonctionnalités cachées ou les astuces de langage sont déconseillées car vous exécutez à la limite du (des) standard (s) C utilisé par votre compilateur. Beaucoup de ces astuces ne fonctionnent pas d'un compilateur à un autre, et souvent ces types de fonctionnalités échoueront d'une version d'une suite de compilateurs d'un fabricant donné à une autre version.
Diverses astuces qui ont cassé le code C incluent:
D'autres problèmes et problèmes qui surviennent 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 un comportement «dépendant du compilateur».
la source
Lorsque vous utilisez sscanf, vous pouvez utiliser% n pour savoir où vous devez continuer à lire:
Apparemment, vous ne pouvez pas ajouter une autre réponse, donc je vais en inclure une deuxième ici, vous pouvez utiliser "&&" et "||" comme conditionnels:
Ce code affichera:
la source
utiliser INT (3) pour définir le point d'arrêt au code est mon préféré de tous les temps
la source
Ma fonction "cachée" préférée de C est l'utilisation de% n dans printf pour réécrire dans la pile. Normalement, printf extrait les valeurs des paramètres 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 désagréables.
la source
Vérification des hypothèses au moment de la compilation à l'aide d'énumérations: exemple stupide, mais peut être vraiment utile pour les bibliothèques avec des constantes configurables au moment de la compilation.
la source
#define CompilerAssert(exp) extern char _CompilerAssert[(exp)?1:-1]
)Gcc (c) a quelques 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.
la source
J'ai découvert récemment 0 bitfields.
qui donnera une mise en page de
au lieu de sans le: 0;
Le champ de largeur 0 indique que les champs de bits suivants doivent être définis sur l'entité atomique suivante (
char
)la source
Macros d'argument de variable de style C99, aka
qui serait utilisé comme
Ici, j'utilise également l'opérateur stringize et la concatentation constante de chaîne, d'autres fonctionnalités que j'aime beaucoup.
la source
Les variables automatiques de taille variable sont également utiles dans certains cas. Celles-ci ont été ajoutées sur nC99 et sont prises en charge dans gcc depuis longtemps.
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 finissez par faire exploser la pile. Vous devrez vérifier les arguments avant d'appeler malloc ou toute autre technique d'allocation de mémoire, donc ce n'est pas vraiment inhabituel.
la source