IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Apprendre à tester ses programmes - Besoin de conseils


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Invité
    Invité(e)
    Par défaut Apprendre à tester ses programmes - Besoin de conseils
    Bonjour,

    Je suis débutant dans la programmation et aurais besoin d'un coup de main. Dans le cadre de ma formation, on nous demande de coder une soixantaine de fonctions en C, souvent extraites de la librairie standard du langage.
    J'ai terminé cette partie du boulot mais fais face à un problème. Je voudrais écrire moi-même un programme de tests pour toutes les fonctions. Je peux trouver en ligne des programmes qui font le job, mais voudrais plutôt être capable d'écrire le mien. Je me dis que dans mon futur boulot, il n'y aura personne pour tester à ma place, alors autant prendre le temps d'acquérir de bons réflexes dans le domaines dès aujourd'hui, sur des travaux peu conséquents.

    Du coup, auriez-vous des conseils pour orienter ce travail? J'imagine que je pourrais écrire des tests simples qui comparent les résultats que je devrais avoir avec ceux que j'ai, mais il y a peut-être des procédures un peu plus solides et éprouvées qui me donneraient les bons réflexes à adopter pour tester des projets plus gros. Quand je regarde le code source des programmes testeurs déjà créés, la structure est vraiment intéressante et assez riche, c'est un résultat auquel j'aimerais parvenir.

    J'espère que cette demande n'est pas trop vague.
    Merci d'avance pour les éventuelles réponses et bonne journée

  2. #2
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Ce que tu évoques - la validation d'une brique constitutive du programme sur un jeu d'entrée limité - s'appelle test unitaire. Son emploi au cours du cycle de développement est une composante de l'intégration continue.

    Citation Envoyé par MadiPoupou Voir le message
    J'imagine que je pourrais écrire des tests simples qui comparent les résultats que je devrais avoir avec ceux que j'ai, mais il y a peut-être des procédures un peu plus solides et éprouvées qui me donneraient les bons réflexes à adopter pour tester des projets plus gros.
    Ben voilà : c'est aussi simple que cela ! Le gros du travail consiste en réalité à intégrer la compilation et l'exécution automatique de ces tests dans la procédure de génération de l'exécutable principal.

    Code square.h : Sélectionner tout - Visualiser dans une fenêtre à part
    int square(int x);
    Code test/square.c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <assert.h>
     
    #include "../square.h"
     
    int main() {
        assert(square(3) == 9);
        assert(square(7) == 49);
        assert(square(10) == 100);
        return 0;
    }


    Il existe d'innombrables outils plus ou moins sophistiqués pour encadrer l'écriture et la gestion de ces tests, tu en trouveras quelques uns là-bas : https://en.wikipedia.org/wiki/List_o...g_frameworks#C . Check, par exemple, est un candidat sérieux pour toute taille de projet.

  3. #3
    Invité
    Invité(e)
    Par défaut
    Merci pour ta réponse. Je crois qu'en fait j'avais cherché la complication là où elle n'était pas.
    Je vais essayer de travailler sur les test unitaires.

    Merci beaucoup et bon weekend!

  4. #4
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 770
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 770
    Par défaut
    La réponse de Matt_Houston est correcte ... mais cela demande beaucoup de veille technologique pour se renseigner/ tester/ ...

    Un truc rapide est simplement de créer un 2ième projet (en ligne de commandes) pour juste tester ton code : cela aura l'avantage d'aller plus vite et sûrement de commencer à te faire réfléchir :
    • Est-ce qu'il faut ajouter à mes classes des opérateurs pour les tests ?
    • Comment créer les arguments à passer en paramètres ? Avec quelles valeurs ?
    • Comment tester une classe virtuelle ?
    • Comment tester une boite noire ?
    • ...

    Par contre, il ne sera pas automatisé : ce sera à toi de le lancer et de regarder les success/ fail

    Un truc important : les tests permettent à la fois de tester le bon comportement (surtout avec les valeurs particulières) mais également toutes les erreurs possibles : ton nombre de tests peut vite grandir/ exploser

    Un exemple pour tester la fonction extract_input : on va coder une procédure (<- fonction sans argument) et sans retour test_extract_input, qui va chaîner les tests (*) avec un compteur, et que l'on va appeler dans le main.
    Attention, cela peut faire du code très long, moche et avec une maintenance douteuse. C'est du code fait au copié/ collé donc attention aussi.
    À l'inverse tu auras un texte personnalisé pour chaque test (<- qui prend en compte les 80 colonnes de la ligne de commandes) : donc facilement compréhensible pour repérer le problème (ou facilement trouvable dans cette masse de code pour se remémorer le test)
    Un dernier point, les include sont sauvages : comme ce projet test est dans ton projet, mais dans un/des dossier(s) spécifique(s), il ne faut pas avoir peur des chemins un peu tortueux

    * : Cela semble évident , mais 1 test c'est juste un appel à la fonction suivi des tests nécessaires pour tester le(s) résultat(s). Ensuite pour d'autres tests (tests de performances par exemple), cela peut être différent.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    #include "../main/project_XXX/cmd_line_parser.h"
     
    #include "../main/project_XXX/common.h"
     
    #include <string>
     
     
    void test_extract_input(void) {
    	unsigned char raw_data[16];
    	unsigned short data_len, count = 1;
    	unsigned char alias_len;
    	bool is_an_alias;
    	INPUT_ERROR error;
     
    	alias_len = 20;
     
    	if ( extract_input("alia()", 10, alias_len, raw_data, data_len, is_an_alias, error) ) {
    		std::printf("test %03d func - invalid func:               Error\n", count);
    	} else {
    		if ((data_len == 0) && (error == I_ERR_INVALID_FUNC)) {
    			std::printf("test %03d func - invalid func:               Success\n", count);
    		} else {
    			std::printf("test %03d func - invalid func:               Error - Invalid output\n", count);
    		}
     
    	}
     
    	++count;
     
    	if ( extract_input("ascii_st()", 10, alias_len, raw_data, data_len, is_an_alias, error) ) {
    		std::printf("test %03d func - invalid func:               Error\n", count);
    	} else {
    		if ((data_len == 0) && (error == I_ERR_INVALID_FUNC)) {
    			std::printf("test %03d func - invalid func:               Success\n", count);
    		} else {
    			std::printf("test %03d func - invalid func:               Error - Invalid output\n", count);
    		}
     
    	}
     
    	++count;
     
    	if ( extract_input("dec_lis()", 10, alias_len, raw_data, data_len, is_an_alias, error) ) {
    		std::printf("test %03d func - invalid func:               Error\n", count);
    	} else {
    		if ((data_len == 0) && (error == I_ERR_INVALID_FUNC)) {
    			std::printf("test %03d func - invalid func:               Success\n", count);
    		} else {
    			std::printf("test %03d func - invalid func:               Error - Invalid output\n", count);
    		}
     
    	}
     
    	++count;
     
    // ...
     
    /****************************************************************************/
    /**************************   alias && ascii_str   **************************/
    /****************************************************************************/
     
    	if ( extract_input("ascii_str(test)", 10, alias_len, raw_data, data_len, is_an_alias, error) ) {
    		if (!is_an_alias && (data_len == 4) && (std::strncmp((const char*) raw_data, "test", 4) == 0) &&
    			(error == I_ERR_NONE)) {
     
    			std::printf("test %03d func ascii_str - valid:            Success\n", count);
    		} else {
    			std::printf("test %03d func ascii_str - valid:            Error - Invalid output\n", count);
    		}
    	} else {
    		std::printf("test %03d func ascii_str - valid:            Error\n", count);
    	}
     
    	++count;
     
    // ...
     
    	{
    		char str[] = "ascii_str( )";
    		unsigned char c;
    		bool is_ok = true;
     
    		c = 0x01;
     
    		while(is_ok && (c <= 0x7F)) {
    			str[10] = c;
     
    			if ( extract_input(str, 2, alias_len, raw_data, data_len, is_an_alias, error) ) {
    				if (!is_an_alias && (data_len == 1) && (raw_data[0] == c) &&
    					(error == I_ERR_NONE)) {
    				} else {
    					is_ok = false;
    				}
    			} else { 
    				is_ok = false;
    			}
     
    			++c;
     
    			if (c == ' ') { ++c; }
    		}
     
    		if (is_ok) {
    			std::printf("test %03d func ascii_str - valid chars:      Success\n", count);
    		} else {
    			std::printf("test %03d func ascii_str - valid chars:      Error\n", count);
    		}
    	}
     
    // ...
    }
    Un autre exemple avec des macros spécifiques qui lorsque c'est bien fait, allége le code et la visibilité (par contre le code de la macro est souvent beurk )

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    #include "../main/project_XXX/cmd_line_parser.h"
     
    #include <string>
     
     
    #define TEST_INVALID(PARAM, ERROR_ID) \
    	(PARAM.selected_switch == SWITCH_NONE) && (PARAM.list_values.size() == 0) && \
    	(PARAM.param_error == ERROR_ID) && (!PARAM.has_no_error) && \
    	(!PARAM.has_AID == 0) && (!PARAM.has_key == 0) && (PARAM.index_filename == 0) && \
    	(PARAM.has_port == false) &&  (PARAM.port == 0)
     
     
    #define TEST_PARAM(PARAM, SWITCH_ID, AID, APK1) \
    	(PARAM.selected_switch == SWITCH_ID) && (PARAM.list_values.size() == 2) && \
    	(PARAM.param_error == P_ERR_NONE) && PARAM.has_no_error && \
    	(PARAM.index_AID > 0) && (PARAM.index_AID <= 2) && (PARAM.list_values[(size_t) (PARAM.index_AID - 1)] == AID) && \
    	(PARAM.index_APK1 > 0) && (PARAM.index_APK1 <= 2) && \
    	(PARAM.list_values[(size_t) (PARAM.index_APK1 - 1)] == APK1) && \
    	(PARAM.index_filename == 0) && \
    	(PARAM.has_port == false) && (PARAM.port == 0)
     
     
    #define TEST_PARAM_FILENAME(PARAM, SWITCH_ID, FILENAME) \
    	(PARAM.selected_switch == SWITCH_ID) && (PARAM.list_values.size() == 1) && \
    	(PARAM.index_AID == 0) && (PARAM.index_APK1 == 0) && (PARAM.index_filename == 1) && \
    	(PARAM.list_values[(size_t) (PARAM.index_filename - 1)] == FILENAME) && \
    	(PARAM.has_port == false) && (PARAM.port == 0)
     
     
    #define TEST_PARAM_WITH_PORT(PARAM, PORT, SWITCH_ID, AID, APK1) \
    	(PARAM.selected_switch == SWITCH_ID) && (PARAM.list_values.size() == 2) && \
    	(PARAM.param_error == P_ERR_NONE) && PARAM.has_no_error && \
    	(PARAM.index_AID > 0) && (PARAM.index_AID <= 2) && (PARAM.list_values[(size_t) (PARAM.index_AID - 1)] == AID) && \
    	(PARAM.index_APK1 > 0) && (PARAM.index_APK1 <= 2) && \
    	(PARAM.list_values[(size_t) (PARAM.index_APK1 - 1)] == APK1) && \
    	(PARAM.index_filename == 0) && \
    	PARAM.has_port &&  (PARAM.port == PORT)
     
     
    #define TEST_params1_WITH_PORT(PARAM, PORT) \
    	(PARAM.selected_switch == SWITCH_GET_CARD_TYPE) && (PARAM.list_values.size() == 0) && \
    	(PARAM.param_error == P_ERR_NONE) && PARAM.has_no_error && \
    	(PARAM.index_AID == 0) && (PARAM.index_APK1 == 0) && (PARAM.index_filename == 0) && \
    	PARAM.has_port &&  (PARAM.port == PORT)
     
     
    #define TEST_params2_WITH_PORT(PARAM, PORT, FILENAME) \
    	(PARAM.selected_switch == SWITCH_DEBUG) && (PARAM.list_values.size() == 1) && \
    	(PARAM.param_error == P_ERR_NONE) && PARAM.has_no_error && \
    	(PARAM.index_AID == 0) && (PARAM.index_APK1 == 0) && (PARAM.index_filename == 1) && \
    	(PARAM.list_values[(size_t) (PARAM.index_filename - 1)] == FILENAME) && \
    	PARAM.has_port &&  (PARAM.port == PORT)
     
     
    #define TEST_VALID(PARAM, SWITCH_ID) \
    	(PARAM.selected_switch == SWITCH_ID) && (PARAM.list_values.size() == 0) && \
    	(PARAM.param_error == P_ERR_NONE) && PARAM.has_no_error && \
    	(PARAM.index_AID == 0) && (PARAM.index_APK1 == 0) && (PARAM.index_filename == 0) && \
    	(PARAM.has_port == false) &&  (PARAM.port == 0)
     
     
    void test_parser(void)
    {/*
    	Command_Feeder feeder(0, NULL);
     
    	Input_Params params1;
    	Input_Params params2;
    	Input_Params params3;
    	Input_Params params4;
    	Input_Params params5;
    	Input_Params params6;
    	Input_Params params7;
     
    	unsigned char c = 1;
     
    	{
    		char* argv1[4] = {"XXX", "-card_agent_get_id",  "0E7500", "12345678901234567890123456789012"};
    		char* argv2[4] = {"XXX", "-card_agent_get_i",   "0E7500", "12345678901234567890123456789012"};
    		char* argv3[4] = {"XXX", "-card_agent_get_id1", "0E7500", "12345678901234567890123456789012"};
     
    		parse(4, argv1, params1, false); // <- param.clear();
    		parse(4, argv2, params2, false); // <- param.clear();
    		parse(4, argv3, params3, false); // <- param.clear();
     
    		std::printf("Test %2u.1 - card_agent_get_id: %s\n", c, ((TEST_PARAM(params1, SWITCH_CARD_AGENT_GET_ID, argv1[2], argv1[3]))? "Success": "Error"));
    		std::printf("Test %2u.2 - card_agent_get_id: %s\n", c, (( TEST_INVALID(params2, P_ERR_INVALID_COMMAND) )? "Success": "Error"));
    		std::printf("Test %2u.3 - card_agent_get_id: %s\n", c, (( TEST_INVALID(params3, P_ERR_INVALID_COMMAND) )? "Success": "Error"));
    	}
     
    	++c;
     
    	{
    		char* argv1[2] = {"XXX", "-card_prog_read"};
    		char* argv2[2] = {"XXX", "-card_prog_rea"};
    		char* argv3[2] = {"XXX", "-card_prog_read1"};
     
    		parse(2, argv1, params1, false); // <- param.clear();
    		parse(2, argv2, params2, false); // <- param.clear();
    		parse(2, argv3, params3, false); // <- param.clear();
     
    		std::printf("Test %2u.1 - card_prog_read:    %s\n", c, (( TEST_VALID(params1, SWITCH_CARD_PROG_READ) )? "Success": "Error"));
    		std::printf("Test %2u.2 - card_prog_read:    %s\n", c, (( TEST_INVALID(params2, P_ERR_INVALID_COMMAND) )? "Success": "Error"));
    		std::printf("Test %2u.3 - card_prog_read:    %s\n", c, (( TEST_INVALID(params3, P_ERR_INVALID_COMMAND) )? "Success": "Error"));
    	}
     
    	++c;
     
    // ...
    }

    Toi tu peux faire un fichier test pour 1 fonction (soit une 60taine )

    Et dans ton main tu peux utiliser les arguments de la ligne de commandes pour lancer tous les tests, une série de tests, ou ....
    Par contre , je ne peux pas trop te montrer de code parce que j'ai recodé en C++ très simplement la bibliothèque getopt.


    Édit 1: Pour certains tests, peut-être que le C++ est plus approprié pour, par exemple, manipuler des chaînes de caractères. Donc même si ton projet principal est en pur C, rien ne t'empêche de faire un projet test en C++ : cela fonctionne tout également.
    Je vois que GoogleTests chronomètre les tests : pour des tests de performance cela est obligatoire effectivement .
    En C/ C++ cela va dépendre de ton système d'exploitation. Donc j'utilise la fonction getRealTime de David Robert Nadeau (<- lien) : à voir si actuellement il n'y a pas mieux.

    Édit 2: Le problème de la méthode à la main c'est que tu peux faire des tests qui peuvent être difficiles à mettre en place ou nécessite beaucoup de code avec une bibliothèque/ cadriciel : donc attention au fait de dire "faire les tests à la main dans un premier temps" si tu utilises une méthode plus robuste.
    Un exemple : Comment tester que ton algo génère un identifiant unique ?
    Simple [mais très lent]: tu crées une base de données avec SQlite, avec 1 seule table et avec une colonne UNIQUE, et dans une boucle tu appelles ton algo et tu l'insères dans ta base de données en gérant les erreurs d'insertion.

  5. #5
    Invité
    Invité(e)
    Par défaut
    Ouaw
    Merci beaucoup pour toutes tes infos. Au final, j'avais plutôt penché pour ton option en créant des tests "à la main", pour une partie de mes fonctions. J'ai la chance d'avoir une programme externe de test assez robuste, alors je me contente de tester les fonctions qui ont un fonctionnement très "différent". Par exemple, je crée une mécanique de test d'une fonction qui renvoie une string, une pour une fonction qui renvoie un int, etc. J'ai encore du boulot, mais je suis arrivé à un résultat correct pour le moment.
    Je me contente d'utiliser du C parce que je ne connais rien en C++. Je suis vraiment en train d'approfondir les basiques du C, je vais patienter avant de me pencher sur autre chose.
    En revanche, je garde ton message au chaud parce que certaines notions qui m'échappent un peu pour le moment me seront probablement utiles par la suite.

    Et je crois comprendre le problème de cette méthodologie de test. Mais elle semble tout de même assez appropriée au travail que je dois réaliser (en gros, un bon vieux tas de fonctions qui n'ont pratiquement aucun lien les unes avec les autres, ou très peu).

    Je profite du post pour poser une question. Je dois tester des fonctions qui écrivent sur la sortie standard (en utilisant l'appel système write()). La méthode que j'ai choisie est de rediriger la sortie standard vers un file descriptor différent, un fichier perso, puis de comparer le contenu de ce fichier avec ce que j'attendais. Est-ce une bonne méthode?
    Et lorsque l'on souhaite tester une fonction qui efface de la mémoire. Comment peut-on vérifier qu'elle fait bien son travail? Parce qu'en plus, cette dernière est supposée mettre à NULL le pointeur qui servait à identifier la zone mémoire... Je peux vérifier si ce dernier est bien NULL, mais la zone mémoire...

    Merci beaucoup pour vos réponses! Bon début de semaine

  6. #6
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 770
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 770
    Par défaut
    Citation Envoyé par MadiPoupou Voir le message
    Je profite du post pour poser une question. Je dois tester des fonctions qui écrivent sur la sortie standard (en utilisant l'appel système write()). La méthode que j'ai choisie est de rediriger la sortie standard vers un file descriptor différent, un fichier perso, puis de comparer le contenu de ce fichier avec ce que j'attendais. Est-ce une bonne méthode?
    Il n'y a pas beaucoup d'infos Mais comme tu maîtrises l'entrée tu connais la sortie.
    Donc passer par un ficher me semble superflu : tu n'appelles pas write et tu retournes carrément ce que tu dois écrire et donc tester une bonne chaîne de caractères.
    Mais cela nécessite de séparer l'écriture de l'affichage : à voir si c'est intéressant.


    Citation Envoyé par MadiPoupou Voir le message
    Et lorsque l'on souhaite tester une fonction qui efface de la mémoire. Comment peut-on vérifier qu'elle fait bien son travail? Parce qu'en plus, cette dernière est supposée mettre à NULL le pointeur qui servait à identifier la zone mémoire... Je peux vérifier si ce dernier est bien NULL, mais la zone mémoire...
    En réalité, malloc, free, delete, et autres sont des opérateurs que tu peux surcharger ... mais c'est un truc de rexoor de la mort
    Il faut chercher une petite bibliothèque qui traque les fuites mémoire pour un exemple

    Édit 1 : Évidemment surcharger les opérateurs ne garanti pas que la mémoire soit libérée mais au moins qu'il n'y a pas de fuite de mémoire. Ensuite, en C, il y a une méthode pour avoir la quantité de mémoire utilisée (mais elle n'est pas portable il me semble)

    Pour l'EDI Visual Studio, tu as un petit plugin Visual Leak Detector. Pour les autres EDIs, il faut chercher.
    Sous Linux, l'outil c'est Valgrind.

    Édit : En C, on fait comme cela:
    On va redéfinir les noms des opérateurs avec un define en utilisant une fonction avec la même signature.
    Et cette fonction de remplacement, va appeler le bon opérateur (*) mais va en plus soit afficher des traces soit compter les allocations/ désallocations soit autre chose
    Évidement, des variables globales vont être utilisées : donc attention
    * : surcharger ces opérateurs permet aussi de les remplacer par, par exemple, une machine virtuelle

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #define  malloc(size) 	    	   	xmalloc (size, __FILE__, __LINE__)
    #define  calloc(elements, size)  	xcalloc (elements, size, __FILE__, __LINE__)
     
    /** replacement of malloc */
    void * xmalloc(unsigned int size,
      const char * file, unsigned int line) {
      void * ptr = malloc(size);
     
      if (ptr != NULL) {
        add_mem_info(ptr, size, file, line);
      }
     
      return ptr;
    }
     
     
    /** replacement of calloc : using linked list to store allocations */
    void * xcalloc(unsigned int elements, unsigned int size,
      const char * file, unsigned int line) {
      unsigned total_size;
      void * ptr = calloc(elements, size);
     
      if (ptr != NULL) {
        total_size = elements * size;
        add_mem_info(ptr, total_size, file, line);
      }
     
      return ptr;
    }

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Apprendre à déboguer ses programmes
    Par LittleWhite dans le forum Débuter
    Réponses: 0
    Dernier message: 11/03/2015, 21h28
  2. Besoin de conseils pour un programme
    Par snips67 dans le forum VB 6 et antérieur
    Réponses: 1
    Dernier message: 23/05/2009, 11h59
  3. Besoin de conseils sur un programme
    Par Lynx91 dans le forum C
    Réponses: 11
    Dernier message: 12/09/2007, 13h36
  4. besoin de conseil pour apprendre l'infographie
    Par maltutoma dans le forum Etudes
    Réponses: 9
    Dernier message: 11/09/2007, 11h41
  5. besoin de conseil création programme (débutante)
    Par kwakly dans le forum WinDev
    Réponses: 1
    Dernier message: 16/05/2007, 13h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo