Découvrez quelle solution de test API est arrivée en tête dans le rapport GigaOm Radar. Obtenez votre rapport d'analyse gratuit >>

Découvrez quelle solution de test API est arrivée en tête dans le rapport GigaOm Radar. Obtenez votre rapport d'analyse gratuit >>
Aller à la section
Bien que les pointeurs vous permettent d’utiliser votre créativité pour résoudre un problème particulier, ils présentent plusieurs limitations qui sont parmi les plus difficiles à gérer pour les programmeurs. Lisez la suite pour savoir comment utiliser Insure++ pour identifier automatiquement les problèmes liés aux pointeurs.
Aller à la section
Aller à la section
Les pointeurs sont à la fois la force et le talon d’Achille de programmation en C et C++. Tandis que les pointeurs vous permettent d’être très créatif et flexible dans la manière dont vous abordez la résolution d’un problème particulier. Cependant, il est facile d’introduire involontairement des défauts dans votre code. Les problèmes avec les pointeurs sont parmi les plus difficiles rencontrés par les programmeurs C. Il y a une génération, les techniques de force brute, comme l'insertion d'instructions d'impression dans le code, constituaient souvent la meilleure stratégie pour tenter de détecter ces problèmes.
Aujourd'hui, les outils de détection d'erreurs de mémoire comme Assurer ++ peut détecter automatiquement les problèmes liés au pointeur lors de l'exécution du code, ce qui permet d'économiser beaucoup de temps et d'éviter des maux de tête. Insure++ détecte des problèmes dans les catégories suivantes :
Insure++ utilise un analyseur de code de pointe, ainsi que des centaines d'heuristiques, pour analyser le code de l'application, au cours duquel il signale plusieurs violations statiques possibles. Lors de l'analyse du code, il écrit un nouveau fichier de code source avec les instruments appropriés insérés dans les points problématiques, tels que le déréférencement du pointeur, la sortie de la portée, etc. Le fichier source résultant est automatiquement compilé et tous les fichiers de code objet résultants sont liés dans un nouveau programme exécutable.
Dans de nombreux cas, lorsque vous essayez de résoudre des problèmes de gestion de la mémoire et de déterminer si une variable est corrompue, vous aurez besoin de connaître l'adresse de cette variable et découvrirez probablement qu'il ne s'agit pas seulement de cette variable, mais aussi d'autres. L'utilisation d'un débogueur est souvent considérée comme une bonne pratique lorsqu'il s'agit d'identifier les problèmes dans votre code, notamment pour comprendre les valeurs des variables et les adresses mémoire.
Les débogueurs fournissent un ensemble d'outils puissants pour inspecter et manipuler l'exécution de votre programme. Cependant, il est important de noter que l’utilisation d’un débogueur n’est pas incompatible avec d’autres techniques de débogage. Les instructions d'impression et l'inspection manuelle peuvent toujours s'avérer des outils précieux, en particulier dans les situations où l'utilisation d'un débogueur peut s'avérer peu pratique.
Une approche simple que j'utilise depuis de nombreuses années consiste simplement à utiliser l'adresse de l'opérateur « & » dans une instruction printf. Prenons l'exemple de code suivant.
int myVariable = 77; printf("Address of myVariable is: %p\n", (void *)&myVariable);
L'expression &myVariable renvoie l'adresse de myVariable. Le spécificateur de format %p est utilisé avec printf pour imprimer l'adresse au format pointeur. Le cast (void*) est utilisé pour correspondre au spécificateur de format %p. N'oubliez pas que l'adresse mémoire réelle peut varier à chaque exécution du programme.
Vous trouverez ci-dessous le code d'un programme « Hello, World » qui utilise l'allocation dynamique de mémoire.
/* * File: hello.c */ #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { char *string, *string_so_far; int i, length; length = 0; for(i=0; i<argc; i++) { length += strlen(argv[i])+1; string = malloc(length+1); /* * Copy the string built so far. */ if(string_so_far != (char *)0) strcpy(string, string_so_far); else *string = '\0'; strcat(string, argv[i]); if(i < argc-1) strcat(string, " "); string_so_far = string; } printf("You entered: %s\n", string_so_far); return (0); }
L'idée de base de ce programme est que nous gardons une trace de la taille actuelle de la chaîne dans la longueur variable. Au fur et à mesure que chaque nouvel argument est traité, nous ajoutons sa longueur à la variable de longueur et allouons un bloc de mémoire de la nouvelle taille. Notez que le code prend soin d'inclure le caractère NULL final lors du calcul de la longueur de la chaîne (ligne 14) ainsi que de l'espace entre les chaînes. Ces deux erreurs sont faciles à commettre. C'est un exercice intéressant de voir à quelle vitesse vous pouvez trouver une telle erreur avec un outil de détection d'erreurs de mémoire comme Parasoft Insure++.
Le code copie l'argument dans le tampon ou l'ajoute, selon qu'il s'agit ou non du premier passage autour de la boucle. Enfin, le pointeur string_so_far pointe vers la nouvelle chaîne plus longue.
Si vous compilez et exécutez ce programme sous Insure ++, vous verrez des erreurs de «pointeur non initialisé» signalées pour le code «strcpy (string, string_so_far)». C'est parce que la variable string_so_far n'a été définie sur rien avant le premier voyage à travers la boucle d'arguments. Dans un petit exemple de code comme celui-ci, un tel problème est évident, mais même si l'erreur est enterrée dans un tas de centaines de milliers de lignes de code et beaucoup plus subtile que l'erreur ci-dessus, Insure ++ le trouvera à chaque fois.
Insure++ signale tous les problèmes détectés. Les rapports Insure++ contiennent des informations détaillées telles que le type de bogue, le fichier source et le numéro de ligne, le contenu réel de la ligne de code source et les expressions à l'origine du problème, avec des rapports comprenant :
La couverture des tests est essentielle pour assurer la sécurité des pointeurs en C et C++. L'analyse statique et l'analyse dynamique jouent un rôle important.
Lorsqu'il s'agit de travailler avec des pointeurs en C ou C++, la couverture des tests est importante car ils introduisent des risques. Le risque peut se présenter de manière subtile et parfois très difficile à détecter, ce qui entraîne des coûts de main-d'œuvre élevés.
Il est essentiel d’utiliser un ensemble complet de méthodes de test pour couvrir une liste de problèmes et de scénarios de pointeurs. Par exemple, vous voudrez utilisez un outil comme Insure++ pour aider les développeurs à détecter les erreurs de programmation erratiques et d'accès à la mémoire, telles que la corruption du tas, les threads malveillants, les fuites de mémoire, les tableaux hors limites et les pointeurs invalides.
Un moyen puissant de commencer est de en utilisant l'analyse statique ou la détection des erreurs de compilation, qui détecte les problèmes potentiels liés aux pointeurs pendant la phase de compilation. Des problèmes tels que la perte de précision du pointeur, la non-concordance dans la spécification de format ou le type d'argument, les variables inutilisées, le code mort, la division par zéro, etc.
Tests d'analyse dynamique est une autre approche puissante et complémentaire de l’analyse statique. La détection des erreurs d'exécution permet d'identifier la mémoire de tas et de pile corrompue, ainsi que tous les types de fuites de mémoire, d'allocation de mémoire, d'erreurs ou de discordances libres et d'erreurs de tableau hors limites.
Pour garantir que vous avez testé tout le code, l'analyse de couverture de code permet d'identifier visuellement quelles sections de code ont été exécutées et lesquelles ne l'ont pas été. Laisser du code non testé dans votre application peut vous coûter des nuits blanches ou venir vous mordre plus tard.