Problème de mémoire

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour à tous, voici un petit programme:

 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
#include <iostream>
using namespace std;

int     main(void)
{
    int     n;
    int     *tab;
    int     *tab2;

    cout << "Nb d'element du tableau ? ";
    cin >> n;
    tab = new int[n];
    for (int i = 0; i < n; i++)
    {
        cout << "elm " << i << ": ";
        cin >> tab[i];
    }
    tab2 = new int[n];
    for (int i = 0; i < n; i++)
        tab2[i] = tab[i] * tab[i];
    delete[] tab;
    for (int i = 0; i < n; i++)
        cout << tab2[i] << " ";
    cout << endl;
    delete[] tab2;
    return (0);
}

Ce programme fonctionne correctement, mais quand je lance valgrind j'ai le droit à ça: total heap usage: 5 allocs, 4 frees, 74,768 bytes allocated Je ne comprend pas. D'abord il y a deux new, donc 2 allocations seulement ? Et deux delete, donc 2 free ? Si quelqu'un peut m'expliquer, merci d'avance.

+0 -0

Bonsoir, Plusieurs choses concernant ton code :

  • ne prédéclare pas tes variables (lis ceci). Tu peux faire directement : int *tab = new int[n]; par exemple.

  • n'utilise pas using namespace std;, c'est une mauvaise habitude, utilise plutôt, si tu n'as pas envie d'écrire à chaque fois std::, using std::cout; par exemple.

  • n'utilise pas de pointeurs nus en c++ excepté si tu as une très bonne raison raison pour le faire. Laisse le RAII gérer la mémoire pour toi et utilise les pointeurs intelligents (regarde du côté de std::make_unique, std::make_shared)

  • les tableaux à la C du type int tab[n] sont dépréciés en c++. On a à la place std::array depuis le c++11

Concernant ta question, je pense que cela vient du fais que déclares au début tab et tab2 sans les définir. Le compilateur est donc libre d'en faire ce qu'il veut : il a sans doute alloué deux espaces en mémoire qu'il a libéré avant que toi tu n'alloues de l'espace pour tes tableaux. Concernant la 5e allocation, elle vient sans doute de ton int n. En effet, il lui faut bien un espace en mémoire pour stocker ta variable n. Enfin, c'est ce que je pense mais je peux me tromper. D'ailleurs, en y repensant, c'est étonnant que ton code compile puisque n n'est pas constant… Si tu utilises une variable pour donner le nombre d'éléments d'un tableau, cette variable doit être constante.

Édité par Typhlos

+0 -0
Auteur du sujet

Pour les variables déclarés au début de la fonction c'est une habitude prise à l'école 42. Pour le reste c'est en fait l'exercice 42 du livre d'exercices C++ de Claude Delannoy, donc je n'ai pas encore vu tout ça, mais je sais que les tableaux à la C sont dépréciés le compilateur me mettant un jolie warning. Par contre je ne suis pas du tout convaincu par tes explications. :euh:

Édité par matthieuj

+0 -0

Lu'!

Pour le reste c'est en fait l'exercice 42 du livre d'exercices C++ de Claude Delannoy

matthieuj

Ce bouquin est une bouse, tu peux éventuellement t'en servir pour caler un meuble mais guère plus.

Quelques notes sur le code :

  • l'utilisation "using namespace std" est une mauvaise pratique. On préférera soit nommer pleinement les éléments : std::cout par exemple, soit importer sélectivement des noms dans la portée voulue : using std::cout par exemple.
  • pour les pré-déclarations de variables, voir ici : où dois-je déclarer mes variables locales ?
  • int main(void) n'est pas une signature correcte pour la fonction main. Voir ici : signatures main.
  • vire tes parenthèses sur le return, elles n'ont absolument aucun sens et elles finiront par te jouer des tours dans des situations particulières,
  • comme l'a dit @Typhlos tes new sont à bannir, ici on utilisera std::make_unique<T[]> ou encore std::vector .

Pour être plus précis sur ce dernier point C++ est un langage à exceptions, y gérer manuellement la mémoire est hors de question à moins d'avoir une excellente raison de le faire et les compétences pour le faire. Exemple concret, formellement ton code fuit :

1
2
3
4
5
tab = new int[n];
for (int i = 0; i < n; i++){
  cout << "elm " << i << ": ";
  cin >> tab[i];
}

La documentation nous dit que les opérateur << ou >> peuvent jeter une exception. Si c'est le cas, le flot d'exécution est rompu et ton code n'arrivera jamais à l'instruction delete[] tab;. Pouf fuite de mémoire.

La gestion des ressources en C++ doit scrupuleusement respecter le RAII (voir cet article).

Concernant ton problème, Valgrind doit donner plus d'infos que cela si tu regardes les logs qu'il te transmet de plus près. Je soupçonne allocations effectuées par les appels aux opérateurs.

Et oublie ce bouquin. Si tu veux un livre correct sur le C++ pour les débutants, prends plutôt le C++ Primer 5th edition de Lippmann (en anglais).

Édité par Ksass`Peuk

First : Always RTFM - "Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein

+7 -0

Malgré que je plussoie Ksass`Peuk, je n'ai pas d'erreur sur ton code avec Valgrind o_O

EDIT: En cas d'erreur entrée utilisateur, valgrind réagit, car en effet il manque l'initialisation des tableaux

 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
#include <iostream>
using namespace std;

int     main()
{
    int     n = 0;
    int     *tab = NULL;
    int     *tab2 = NULL;

    cout << "Nb d'element du tableau ? ";
    cin >> n;
    tab = new int[n](); // Initialisation
    for (int i = 0; i < n; i++)
    {
        cout << "elm " << i << ": ";
        cin >> tab[i];
    }
    tab2 = new int[n](); // Initialisation
    for (int i = 0; i < n; i++)
        tab2[i] = tab[i] * tab[i];
    delete[] tab;
    for (int i = 0; i < n; i++)
        cout << tab2[i] << " ";
    cout << endl;
    delete[] tab2;
    return (0);
}

Que va-t-il se passer si l'utilisateur envoie un nombre d'élément inférieur à 0 ? ;)

Édité par fred1599

+0 -0
Auteur du sujet

Merci pour vos réponses.

L'utilisateur n'envoie pas de nombre inférieur à zéro parce que l'utilisateur c'est moi et ce qui m'intéresse dans cet exemple c'est de découvrir new et delete. Le problème vient de cin et cout, mais je n'en sais pas plus.

La signature du main, les variables déclarés en blocs au début, les parenthèses autour du return, ce sont des habitudes prises en langage C à l'école, il faut que je corrige tout ça ^^.

Pour ce qui est du livre, je vais travailler avec C++ Primer. Mais j'ai la flemme de me plonger encore une fois dans un livre de plusieurs centaines de pages. J'ai préféré trouver un 'bon' bouquin d'exercices progressif et chercher dans les livres et sur internet à compléter les notions que je ne connais pas bien. Donc si vous avez des séries d'exercices par notions ou de mini-projets, je suis preneur. Tout ce que j'ai trouvé c'est ça: https://openclassrooms.com/forum/sujet/exercices-venez-vous-entrainer-95469 (vous me direz c'est déjà pas mal).

+0 -0

La signature du main, les variables déclarés en blocs au début, les parenthèses autour du return, ce sont des habitudes prises en langage C à l'école.

[…] J'ai préféré trouver un 'bon' bouquin d'exercices progressif et chercher dans les livres et sur internet à compléter les notions que je ne connais pas bien.

matthieuj

Il ne faut pas voir C++ comme un complément à C. Ce n'est pas le cas. Du tout. Si tu veux apprendre à développer correctement en C++, oublie complètement le C dans un premier temps. Sinon tu vas juste finir par faire du C en C++ ce qui est faux la majorité du temps.

Tout ce que j'ai trouvé c'est ça: https://openclassrooms.com/forum/sujet/exercices-venez-vous-entrainer-95469 (vous me direz c'est déjà pas mal).

matthieuj

Les exercices sont corrects. Les solutions je n'en suis pas sûr. Il faudra que je regarde à l'occasion pour voir si les conseils et codes fournis sont effectivement corrects. En tout cas n'hésite pas à poser des questions sur ce forum ou celui d'OC pour qu'on commente tes solutions. On est pire que le compilateur.

First : Always RTFM - "Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein

+4 -0

+1 à "oublie new et delete" car ce n'est pas comme ça que le C++ doit s'utiliser – et s'il y a une chose que nous apprend l'expertise, c'est qu'il est impossible de garantir le 0-fuite quand on manipule la mémoire à la main. C'est réservé au très bas niveau pour écrire des capsules RAII, et sur une appli normale, on n'en fait pas car on utilise justement les capsules RAII déjà existantes.

Apprends à développer correctement en C++ avant de regarder les sujets très avancés.

+1 pour le livre à éviter.

Pour les exos du sdzoc, j'avais pondu quelques corrections. Mais pour des exos, un github ou un wiki quelque part serait possiblement mieux.

Édité par lmghs

+0 -0

Cette réponse a aidé l'auteur du sujet

Comme tout est dit, je vais simplement me pencher sur le problème d'origine: pourquoi valgrind voit une fuite mémoire ?

Pour commencer, valgrind propose de mettre les options –leak-check=full puis –show-leak-kinds=all pour tracer les fuites.

Si c'est comme chez moi, l'erreur vient de _dl_init qui s'occupe du chargement des bibliothèques. Le programme pourrait ne rien faire que valgrind indique toujours une fuite. Personnellement je l'ignore et la supprime des traces (option --suppressions=fichier et --gen-suppressions=all pour la génération du fichier). Mais c'est hélas toujours pris en compte dans le compteur d'allocation :/

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte