大小为 4 valgrind 的读取无效

Posted

技术标签:

【中文标题】大小为 4 valgrind 的读取无效【英文标题】:Invalid read of size 4 valgrind 【发布时间】:2020-05-11 17:13:48 【问题描述】:

我用 valgrind 运行我的代码并得到一个“无效读取大小 4”的错误。这是我得到的:

==15103==    at 0x10F74C: NoeudHydro<noeud*>::GetNumero() const (NoeudHydro.h:97)
==15103==    by 0x10ECE1: std::ostream& operator<< <noeud*>(std::ostream&, NoeudHydro<noeud*>) (NoeudHydro.h:191)
==15103==    by 0x10E15A: main (main.cpp:71)
==15103==  Address 0x5b88450 is 0 bytes inside a block of size 240 free'd
==15103==    at 0x4C3123B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==15103==    by 0x110943: __gnu_cxx::new_allocator<NoeudHydro<noeud*> >::deallocate(NoeudHydro<noeud*>*, unsigned long) (new_allocator.h:125)
==15103==    by 0x1105C1: std::allocator_traits<std::allocator<NoeudHydro<noeud*> > >::deallocate(std::allocator<NoeudHydro<noeud*> >&, NoeudHydro<noeud*>*, unsigned long) (alloc_traits.h:462)
==15103==    by 0x10FBE7: std::_Vector_base<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >::_M_deallocate(NoeudHydro<noeud*>*, unsigned long) (stl_vector.h:180)
==15103==    by 0x112102: void std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >::_M_realloc_insert<NoeudHydro<noeud*> >(__gnu_cxx::__normal_iterator<NoeudHydro<noeud*>*, std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > > >, NoeudHydro<noeud*>&&) (vector.tcc:448)
==15103==    by 0x111E47: void std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >::emplace_back<NoeudHydro<noeud*> >(NoeudHydro<noeud*>&&) (vector.tcc:105)
==15103==    by 0x111CCF: std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >::push_back(NoeudHydro<noeud*>&&) (stl_vector.h:954)
==15103==    by 0x111AE6: AjoutNoeudHydro(std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >&, noeud*, char, int, int*) (passerelleRT.cpp:48)
==15103==    by 0x1119EC: ParcoursAxe(axe*, std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >&, int*, int) (passerelleRT.cpp:36)
==15103==    by 0x10E0E1: main (main.cpp:68)
==15103==  Block was alloc'd at
==15103==    at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==15103==    by 0x1109D3: __gnu_cxx::new_allocator<NoeudHydro<noeud*> >::allocate(unsigned long, void const*) (new_allocator.h:111)
==15103==    by 0x110680: std::allocator_traits<std::allocator<NoeudHydro<noeud*> > >::allocate(std::allocator<NoeudHydro<noeud*> >&, unsigned long) (alloc_traits.h:436)
==15103==    by 0x10FE55: std::_Vector_base<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >::_M_allocate(unsigned long) (stl_vector.h:172)
==15103==    by 0x111FC5: void std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >::_M_realloc_insert<NoeudHydro<noeud*> >(__gnu_cxx::__normal_iterator<NoeudHydro<noeud*>*, std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > > >, NoeudHydro<noeud*>&&) (vector.tcc:406)
==15103==    by 0x111E47: void std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >::emplace_back<NoeudHydro<noeud*> >(NoeudHydro<noeud*>&&) (vector.tcc:105)
==15103==    by 0x111CCF: std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >::push_back(NoeudHydro<noeud*>&&) (stl_vector.h:954)
==15103==    by 0x111AE6: AjoutNoeudHydro(std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >&, noeud*, char, int, int*) (passerelleRT.cpp:48)
==15103==    by 0x1119EC: ParcoursAxe(axe*, std::vector<NoeudHydro<noeud*>, std::allocator<NoeudHydro<noeud*> > >&, int*, int) (passerelleRT.cpp:36)
==15103==    by 0x10E0E1: main (main.cpp:68)

这是我的代码:

#include <iostream>
#include "NoeudHydro.h"
#include "passerelleRT.h"
using namespace std;

bool EstNoeudReit(PTNoeud Nd)
    PTNoeud Ndsuivant=Nd->suiv;
    return SontConfondus(Nd,Ndsuivant);


bool SontConfondus(PTNoeud Nd1, PTNoeud Nd2)
    return Nd1->pos[0]==Nd2->pos[0] && Nd1->pos[1]==Nd2->pos[1] && Nd1->pos[2]==Nd2->pos[2];


void ParcoursAxe(PTAxe AxeCourant,vector< NoeudHydro<PTNoeud> >& SysHydro, int TabEqui[],int temps)
    PTNoeud NdSRCourant=AxeCourant->premNoeud;
    NdSRCourant=NdSRCourant->NdDistSurAxeFils;

    while(NdSRCourant!=NULL)
        if(NdSRCourant->NdDistSurAxeFils!=NULL) //Si c'est un Nd Ramif/Reit
            if(EstNoeudReit(NdSRCourant))
                AjoutNoeudHydro(SysHydro,NdSRCourant,'2',temps,TabEqui);
                PTNoeud NdTemp=NdSRCourant->suiv;
                while(SontConfondus(NdTemp,NdSRCourant))
                    TabEqui[NdTemp->num]=SysHydro.size()-1; //le numero du NoeudHydro venant d etre ajoute
                                                           //est la taille du vecteur -1
                    SysHydro[SysHydro.size()+1].SetEquivalentSysRac(NdTemp);
                    NdTemp=NdTemp->suiv;
                
            //end if EstNoeudReit

            else //C est un Nd Ramif
                AjoutNoeudHydro(SysHydro,NdSRCourant,'1',temps,TabEqui);
            

        //end if Nd Ramif/Reit

        else
            AjoutNoeudHydro(SysHydro,NdSRCourant,'0',temps,TabEqui);
        
        NdSRCourant=NdSRCourant->suiv;
    


void AjoutNoeudHydro(vector< NoeudHydro<PTNoeud> >& SysHydro,PTNoeud NdSR, char typeNd, int temps,int TabEqui[])

    //Ajout dans le vecteur
    SysHydro.push_back(NoeudHydro<PTNoeud> (NdSR,SysHydro.size(),typeNd,temps));
    //Relation Pere-Fils
    SysHydro.back().SetPere(SysHydro[TabEqui[NdSR->prec->num]]);
    SysHydro[TabEqui[NdSR->prec->num]].SetFils(SysHydro.back()); 

    //Equivalences
    TabEqui[NdSR->num]=SysHydro.size()-1;




int main()

    int tempsTotal=28;

    /*-----------------------------------------------------*/
    /*SOME CODE THAT DOES WORK AND NEEDED FOR THE NEXT PART*/
    /*-----------------------------------------------------*/


    cout<<"------------------------------------------"<<endl;
    cout<<"-------------------HYDRO------------------"<<endl;
    cout<<"------------------------------------------"<<endl;

    // //HYDRO
    NoeudHydro<PTNoeud> *Nd;
    Nd=new NoeudHydro<PTNoeud> (SRG.GetSR()->premAxe->suivant->premNoeud,0,'0',tempsTotal);

    vector< NoeudHydro<PTNoeud> > SysHydro;
    int TabEqui[27]; 
    TabEqui[0]=-9;
    TabEqui[4]=0;
    SysHydro.push_back(*Nd);

    ParcoursAxe(SRG.GetSR()->premAxe->suivant,SysHydro,TabEqui,tempsTotal);

    for(int i=0;i<SysHydro.size();i++)
        cout<<SysHydro[i]<<endl;
        cout<<endl;
    
    delete Nd;
    return 0;  
 

以及出现问题的班级:

#ifndef NOEUDHYDRO
#define NOEUDHYDRO

#include <iostream>
#include <vector>

#include "fonctions_Info_Element.h"

using namespace std;

template <typename T>
class NoeudHydro

    private :
        int numero; //numero dans l hydro
        int typeRacine;
        int ordreRamif;
        char typeNd; //(noeud simple =0, ramif = 1, reit = 2)
        float coordonnes[3]; //coordonnees
        double diametre;
        int age;
        float distanceApex;

        double conductAxiale; //conductance axiale entre le pere et lui meme
        double conductRadiale;

        NoeudHydro<T>* Pere;
        vector<NoeudHydro*> Fils; //NoeudHydro suivant. Le premier est le suivant sur la meme racine. 
                                  //Les autres sont les NoeudHydro suivant sur les axes fils (s il y a)
        vector<T> EquivalentSysRac; //Liste des Noeud/Seg du SysRac equivalents

    public : 
        //CONSTRUCTEURS/DESTRUCTEUR
        NoeudHydro(); //par defaut
        NoeudHydro(T& Element,int num, char typeNd,  int temps);
        ~NoeudHydro();

        //ACCESSEURS
        int GetNumero() const;
        NoeudHydro<T>* GetPere() const;
        NoeudHydro<T>* GetFils(int i) const; 
        T GetEquivalentSysRac(int i) const;
        int GetNbEquivalentSysRac() const;

        void SetNumero(int i);
        void SetPere(NoeudHydro<T>& Pere);
        void SetFils(NoeudHydro<T>& Fils);

;

template <typename T>
NoeudHydro<T>::NoeudHydro(T& Element,int num, char typeNd, int temps)
    this->Pere=NULL;

    this->numero=num;
    this->typeRacine=TypeRacineElement(Element);
    this->ordreRamif=OrdreRamifElement(Element);
    this->typeNd=typeNd;

    for(int i=0;i<3;i++)
        this->coordonnes[i]=CoordonneesElement(Element,i);
    

    this->diametre=DiametreElement(Element);
    this->age=AgeElement(Element,temps);
    this->EquivalentSysRac.push_back(Element);
    cout<<"NoeudHydro "<<this->numero<<" cree"<<endl;


template <typename T>
NoeudHydro<T>::~NoeudHydro()    cout<<"NoeudHydro "<<this->numero<<" detruit"<<endl;




//ACCESSEURS

template <typename T>
int NoeudHydro<T>::GetNumero() const
    return this->numero;


template <typename T>
NoeudHydro<T>* NoeudHydro<T>::GetPere() const
    return this->Pere;


template <typename T>
NoeudHydro<T>* NoeudHydro<T>::GetFils(int i) const
    return this->Fils[i];


template <typename T>
void NoeudHydro<T>::SetNumero(int i)
    this->numero=i;


template <typename T>
void NoeudHydro<T>::SetPere(NoeudHydro& NdPere)
    this->Pere=&NdPere;


template <typename T>
void NoeudHydro<T>::SetFils(NoeudHydro& NdFils)
    this->Fils.push_back(&NdFils);



//Operateur
template <typename T>
ostream& operator<<(ostream& stream, NoeudHydro<T> Nd)
    if(Nd.GetPere()!=NULL)cout<<"Noeud Pere : "<<Nd.GetPere()->GetNumero()<<endl; 
    for(int i=0;i<Nd.GetNbFils();i++)
        cout<<"Fils numero "<<i<<" : noeud "<<Nd.GetFils(i)->GetNumero()<<endl;
     
    return stream;

#endif

错误似乎来自GetNumero(),但如果我这样做没有错误

NoeudHydro<PTNoeud> Nd(parameter needed);
Nd.GetNumero();

只有当我做Nd.GetPere()-&gt;GetNumero();

不明白为什么,我也注意到代码确实有效。 (如果我不使用 valgrind,我会继续编写代码)。 有人有话要说吗?

注意:PTNoeud 与 noeud* 相同

【问题讨论】:

不相关:“NB : PTNoeud is the same as noeud*”那何必呢? 定义PTNoeud的部分我没有写,我使用它以便我的同事可以理解 这里的关键信息是“地址 0x5b88450 是大小为 240 的块内的 0 个字节已释放” - 这意味着您所指的数据位于内存中 已经空闲' d。 Valgrind 进一步告诉你该块最初分配的位置。 【参考方案1】:

如果不能轻松地追踪错误到行号,就很难准确地说出错误是什么。

不明白为什么,我也注意到代码确实有效。 (如果我不使用 valgrind,我会继续编写代码)。有人有话要说吗?

这就是未定义行为 (UB) 的本质。如果 UB 意味着应用程序总是以一个很好且易于调试的 coredump 崩溃,那就太好了。实际上,UB 意味着几乎任何事情都可能发生。它可以运行多年而没有副作用,然后突然崩溃。

回到问题上来。 Valgrind 告诉您您正在访问已删除的内容。它也告诉你这一切都发生在emplace_back。这可能意味着正在调整vector 的大小,并且在调整大小的过程中出现了问题。

我的猜测是您的问题出在您的父/子原始指针上。当 vector 调整大小时,这可能会使这些指针无效。如果这是正确的,那么您将需要修改赋值运算符/复制构造函数/移动构造函数,或者只考虑使用智能指针。

【讨论】:

【参考方案2】:

非常感谢。我使用了智能指针,它可以工作。我只想问我做得是否正确(你让我发现了智能指针):

void NoeudHydro<T>::SetPere(NoeudHydro& NdPere)
    shared_ptr<NoeudHydro<T> >temp (new NoeudHydro<T>(NdPere));
    this->Pere=temp;


template <typename T>
void NoeudHydro<T>::SetFils(NoeudHydro& NdFils)
    shared_ptr<NoeudHydro<T> >temp (new NoeudHydro<T>(NdFils));
    Fils.push_back(temp);

shared_ptr&lt;NoeudHydro&lt;T&gt; &gt; Pere;vector&lt;shared_ptr&lt;NoeudHydro&lt;T&gt;&gt;&gt; Fils;。 而且我也不明白为什么当调整向量大小时,指针会失效?

【讨论】:

正如我所说,没有调试很难确定。当vector 调整大小时,会分配更大的新内存块,将旧内存复制到新内存的开头并释放旧内存。这意味着在调整大小之后,任何指向旧内存的东西都是无效的。您的共享指针看起来不错,但您应该可以只使用 emplace_back 而不是创建一个临时的并使用 push_back。

以上是关于大小为 4 valgrind 的读取无效的主要内容,如果未能解决你的问题,请参考以下文章

C - valgrind - 大小为 1 的无效读取

大小为 8 的无效读取 - Valgrind + C

wchar_t valgrind 问题 - 大小为 8 的读取无效

valgrind 使用 std::string 报告无效读取

内存检测工具Valgrind

free():fclose 上的下一个大小(正常)无效。但不是在 Valgrind 运行时[重复]