Thread: Valgrind error between two items in initialiser list

  1. #1
    C++ rookie Galdor's Avatar
    Join Date
    Apr 2016
    Location
    Belgium
    Posts
    32

    Valgrind error between two items in initialiser list

    I am trying to solve valgrind errors in a code, but I am having some trouble… Perhaps someone can give me some hints ?

    I have a "Mother" class holding a whole lot of attributes "A, B, C, D, ..." that are all references to objects of the same type (i.e. "SomeClass &")

    Code:
    class Mother
    {
        //
    public:
        //
        Mother(foo, bar);
        //
    private:
        //
        SomeClass & A;
        SomeClass & B;
        SomeClass & C;
        SomeClass & D;
        etc.
    };
    Each attribute is constructed within the initialisation list of the Mother() constructor :

    Code:
    Mother::Mother(foo, bar)
        : A (*new SomeClass(foo, bar, "alpha"))
        , B (*new SomeClass(foo, bar, "beta" ))
        , C (*new SomeClass(foo, bar, "gamma"))
        , D (*new SomeClass(foo, bar, "zeta" ))
        , etc.
    The valgrind error occurs after around 50 such references to objects have been initialised and 4 still need to be initialised. The error is of the form :

    Code:
    ==8347== Invalid write of size 8
    ==8347==    at 0x42776B: Mother::Mother([...]) (mother.cpp:[...])
    ==8347==    by [...]
    ==8347==  Address 0x9081fe8 is 0 bytes after a block of size 440 alloc'd
    ==8347==    at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==8347==    by [...]
    I've tried to run gdb along with valgrind, but gdb gets lost in the code : it thinks it is at some place that is inconsistent with what "std::cout << [...]" instructions print on the screen.

    As an alternative, I've added "std::cout << [...]" instructions in the code to locate where the valgrind message occurs. Unfortunately, it occurs after the "std::cout [...]" instruction located at the last line of the SomeClass() constructor and before the first "std::cout [...]" instruction located within the first initialiser list item of the next SomeClass object.

    Code:
    static dummy([...])
    {
        std::cout << "first" << std::endl;
        return [...]
    }
    
    SomeClass::SomeClass(foo, bar)
        : A ( dummy( *new SomeClass(foo, bar) ) )
        , B (*new SomeClass(foo, bar))
        , C (*new SomeClass(foo, bar))
        , D (*new SomeClass(foo, bar))
        , etc.
    {
        [...]
        std::cout << "last" << std::endl;
    }
    Swapping the order of two attributes does not change the error.
    I.e. if the valgrind error occurs after constructing C and before constructing D then the following code

    Code:
    Mother::Mother(foo, bar)
        : A (*new SomeClass(foo, bar))
        , B (*new SomeClass(foo, bar))
        , D (*new SomeClass(foo, bar))
        , C (*new SomeClass(foo, bar))
        , etc.
    Will show an error after constructing D and before constructing C.

    From your experience, would you have any idea/hint regarding what could cause the Valgrind error ? What I should try to do to find the root cause ? Is there is a better way to write my code ?
    Last edited by Galdor; 04-29-2016 at 09:49 AM. Reason: Minor changes to sentence to make it clearer

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,661
    Which version of GCC?
    Which version of Valgrind?

    Do you see the problem when you initialise via assignment within the constructor itself, and not via initialiser lists?

    It would also help if you could post some actual code we could test with. Code full of ... snipping doesn't help us guess at all the possible things you could have done wrong.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    C++ rookie Galdor's Avatar
    Join Date
    Apr 2016
    Location
    Belgium
    Posts
    32
    Quote Originally Posted by Salem View Post
    Which version of GCC?
    Which version of Valgrind?
    gcc 4.8.4
    valgrind 3.10.1

    Do you see the problem when you initialise via assignment within the constructor itself, and not via initialiser lists?
    The attributes are references to objects so they must be initialised within the initialiser list…

    And changing the attributes from a reference to object into a pointer to object or into an object changes the problem : valgrind doesn't show the error any more at that same point (there is the same kind of error happening later on…) and the program no longer crashes…

    I suspect the issue is caused by pointers pointing where they should not or something…

    Quote Originally Posted by Salem View Post
    It would also help if you could post some actual code we could test with. Code full of ... snipping doesn't help us guess at all the possible things you could have done wrong.
    Well to be honest, that's an awful lot of code so I don't even know what I should copy & paste that would be relevant… What would you like to see ?

  4. #4
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    Quote Originally Posted by Galdor View Post
    Well to be honest, that's an awful lot of code so I don't even know what I should copy & paste that would be relevant… What would you like to see ?
    A simplified, yet complete, example that illustrates the problem.

    Short, Self Contained, Correct Example

  5. #5
    C++ rookie Galdor's Avatar
    Join Date
    Apr 2016
    Location
    Belgium
    Posts
    32
    Quote Originally Posted by Matticus View Post
    A simplified, yet complete, example that illustrates the problem.
    Short, Self Contained, Correct Example
    Yes, I understand that this would make things simpler, but unfortunately it feels like the problem is modified any time I make the slightest change…
    • changing the attributes from a reference to object into a pointer to object or into an object changes the problem
    • and since the problem does not occur at the initialisation of the first fifty "SomeClass" objects, I suspect that if I simplify the problem by reducing the number of attributes in the "Mother" class then the error will no longer show up either…
    • not to mention that the problem might be caused by the part of the code that construct the Mother object (there are actually a class constructor that instantiates another object whose constructor instantiates the Mother object…)


    Therefore all the simpler examples I can think of won't illustrate the problem… Now, if you have a suggestion, please tell !

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Galdor
    Is there is a better way to write my code ?
    Frankly, this looks bad to me:
    Code:
    class Mother
    {
        //
    public:
        //
        Mother(foo, bar);
        //
    private:
        //
        SomeClass & A;
        SomeClass & B;
        SomeClass & C;
        SomeClass & D;
        etc.
    };
    
    Mother::Mother(foo, bar)
        : A (*new SomeClass(foo, bar, "alpha"))
        , B (*new SomeClass(foo, bar, "beta" ))
        , C (*new SomeClass(foo, bar, "gamma"))
        , D (*new SomeClass(foo, bar, "zeta" ))
        , etc.
    What if an exception is propagated from say, new SomeClass(foo, bar, "gamma")? It seems to me that you will then have a memory leak as you have no way of invoking delete on what has already been created. I presume that you have some good reason why Mother does not store SomeClass objects instead of references, yet I wonder if it will be better to take a smart pointer approach:
    Code:
    class Mother
    {
        //
    public:
        //
        Mother(foo, bar);
        //
    private:
        //
        std::unique_ptr<SomeClass> A;
        std::unique_ptr<SomeClass> B;
        std::unique_ptr<SomeClass> C;
        std::unique_ptr<SomeClass> D;
        etc.
    };
    
    Mother::Mother(foo, bar)
    {
        A.reset(new SomeClass(foo, bar, "alpha"));
        B.reset(new SomeClass(foo, bar, "beta"));
        C.reset(new SomeClass(foo, bar, "gamma"));
        D.reset(new SomeClass(foo, bar, "zeta"));
    }
    Last edited by laserlight; 04-29-2016 at 01:53 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,661
    > but unfortunately it feels like the problem is modified any time I make the slightest change…
    Well to be honest, if you've got that much fragile code, then chances are the problem is really somewhere else and this is just where the problem eventually manifests itself.

    A simplified example would at least prove to yourself that there is nothing actually wrong with the current method (from a memory management point of view at least).

    Are threads involved by any chance?

    > and since the problem does not occur at the initialisation of the first fifty "SomeClass" objects,
    Don't tell me you have 50+ named variables, instead of a vector.

    As an alternative, you could try running in the debugger when linked against Electric Fence - eLinux.org

    Valgrind also gives you the option of dropping into gdb when a problem is detected.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    C++ rookie Galdor's Avatar
    Join Date
    Apr 2016
    Location
    Belgium
    Posts
    32
    Quote Originally Posted by laserlight View Post
    I presume that you have some good reason why Mother does not store SomeClass objects instead of references
    Not necessarily : I am not a programming expert (I have a mechanical engineering background), so even though I am trying my best to make sensible choices, it is very possible that I'm making poor design decisions…

    So the best solution for you would be to have objects as attributes ?
    Unless the objects take too much memory in the stack in which case you would use smart pointers instead, as you described ?

    Quote Originally Posted by laserlight View Post
    What if an exception is propagated from say, new SomeClass(foo, bar, "gamma")? It seems to me that you will then have a memory leak as you have no way of invoking delete on what has already been created.
    That's true ! Up until now I have not really cared about that because I was not managing exceptions, but using assertions instead. I know this is bad in a production environment, but in my case I am working on a in-house simulation software to which only myself and a few colleagues have access. And if something goes wrong, we prefer to have the software crash anyway…

    However, I would really prefer to do things the "right" way, so if you tell me that objects and smart pointers are the way to go, then I will happily follow your advice.

  9. #9
    C++ rookie Galdor's Avatar
    Join Date
    Apr 2016
    Location
    Belgium
    Posts
    32

    Post

    Quote Originally Posted by Salem View Post
    Well to be honest, if you've got that much fragile code, then chances are the problem is really somewhere else and this is just where the problem eventually manifests itself.
    I think you are right, but I feel a bit powerless to find the root cause of the issue… I tried several approaches to fix the problem, but I am running out of options… Did you face similar issues in the past ? How did you manage to fix them ? Do you have tricks and/or strategies to do so ?

    Quote Originally Posted by Salem View Post
    Are threads involved by any chance?
    Thank God, no… Otherwise that would be even more of a nightmare… I am having enough of a hard time

    Quote Originally Posted by Salem View Post
    Don't tell me you have 50+ named variables, instead of a vector.
    I'm afraid I do have a lot of variables… But much less than 50 (I didn't count properly in my first post…). The "SomeClass" objects are wrappers around arrays that represent a physical quantity (temperature, mass density, velocity, etc.) or that are used for debug as a way to save a state of computation. In the "Mother" class there are 15 arrays for physical quantities, followed by 12 arrays for debug purposes, and the code crashed between the 9th and 10th debug object. Each of these arrays represent something different, so a vector won't do. And a map would make the synthax more difficult to read IMHO… So yes I have a lot of arrays to initialise… Would you use container objects anyway ?

    Quote Originally Posted by Salem View Post
    As an alternative, you could try running in the debugger when linked against Electric Fence - eLinux.org
    What's the difference between electric fence and valgrind ?

    Quote Originally Posted by Salem View Post
    Valgrind also gives you the option of dropping into gdb when a problem is detected.
    Yes, that was what I was doing : running valgrind with -vgdb=full option (that's what you meant, right ?)
    Last edited by Galdor; 04-29-2016 at 02:45 PM.

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Galdor
    So the best solution for you would be to have objects as attributes ?
    It is the simplest solution, and if you don't need a more complex solution, then the simplest solution is best.

    Quote Originally Posted by Galdor
    Unless the objects take too much memory in the stack in which case you would use smart pointers instead, as you described ?
    Yes. Other reasons include the need for inheritance-based polymorphism, i.e., you store a (smart) pointer to a base class object, which could in fact point to a derived class object. Yet another reason could be that the member is actually optional, in which case you would store a null pointer. If you need to have some kind of container, then generally a separate container object, e.g., a std::vector, would be better.

    Quote Originally Posted by Galdor
    That's true ! Up until now I have not really cared about that because I was not managing exceptions, but using assertions instead. I know this is bad in a production environment, but in my case I am working on a in-house simulation software to which only myself and a few colleagues have access. And if something goes wrong, we prefer to have the software crash anyway…
    Even if you are not throwing your own exceptions, the use of new means that std::bad_alloc could be thrown.

    Quote Originally Posted by Galdor
    I'm afraid I do have a lot of variables… But much less than 50 (I didn't count properly in my first post…). The "SomeClass" objects are wrappers around arrays that represent a physical quantity (temperature, mass density, velocity, etc.) or that are used for debug as a way to save a state of computation. In the "Mother" class there are 15 arrays for physical quantities, followed by 12 arrays for debug purposes, and the code crashed between the 9th and 10th debug object. Each of these arrays represent something different, so a vector won't do. And a map would make the synthax more difficult to read IMHO… So yes I have a lot of arrays to initialise… Would you use container objects anyway ?
    Generally, a class should model one thing and model it well. This means that typically, a well designed class only has a few member variables. With 15 member variables, probably the class is representing too much at once, so you can move some groups of these into another class that models that group of member variables.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    C++ rookie Galdor's Avatar
    Join Date
    Apr 2016
    Location
    Belgium
    Posts
    32
    All right, I've found the root of the problem today… Here is an explanation of what happened and a few more questions I have.

    What was wrong

    Short answer :

    preprocessor macro havoc that made some parts of the code believe a class was smaller than what it actually was. This lead to some "class boundary overflows"…

    Long answer :

    Consider a header class defining a "MyClass" class

    Code:
    //myClass.h
    
    #ifndef MY_CLASS__H
    #define MY_CLASS__H
    
    class MyClass
    {
    public:
        //
        MyClass(void);
        //
    private:
        //
        double myArr[200]; //Both main() and MyClass() know about myArr
        //
        #ifdef MY_MACRO //Only myClass.cpp defines this macro, not main.cpp
        double anotherArr[200]; //main() ignores the existence of anotherArr while MyClass() knows about it…
        #endif
    };
    
    #endif
    and two source files "main.cpp" and "myClass.cpp". The first source file (named "main.cpp") contains the definition of the main() function and does not define the MY_MACRO macro

    Code:
    //main.cpp
    
    #include "myClass.h"
    
    int main(void) {
        MyClass * MyObj = new MyClass();
        return 0;
    }
    while the second source file (named "myClass.cpp") defines the Constructor of the MyClass class as well as the MY_MACRO macro

    Code:
    //myClass.cpp
    
    #define MY_MACRO
    
    #include "myClass.h"
    
    MyClass::
    MyClass()
    {
        anotherArr[0]   = 1.0; //Line #9  : triggers valgrind error
        anotherArr[100] = 1.0; //Line #10 : triggers valgrind error
    }
    Running valgrind leads to out-of-bound/overflow errors

    Code:
    ==3515== Invalid write of size 8
    ==3515==    at 0x400786: MyClass::MyClass() (myClass.cpp:9)
    ==3515==    by 0x400745: main (main.cpp:5)
    ==3515==  Address 0x5a02680 is 0 bytes after a block of size 1,600 alloc'd
    ==3515==    at 0x4C29180: operator new(unsigned long) (vg_replace_malloc.c:324)
    ==3515==    by 0x40073A: main (main.cpp:5)
    ==3515== 
    ==3515== Invalid write of size 8
    ==3515==    at 0x40079B: MyClass::MyClass() (myClass.cpp:10)
    ==3515==    by 0x400745: main (main.cpp:5)
    ==3515==  Address 0x5a029a0 is 736 bytes inside an unallocated block of size 4,192,544 in arena "client"
    From what I understand, main() believes that MyClass objects are smaller than they really are. So calling "new MyClass()" allocates less memory than what is actually needed. On the other hand, the MyClass() constructor wants to use a larger chunk of memory than what was allocated. Kaboum : valgrind errors and invalid memory…

    Surprisingly enough, running this code did not trigger a seg fault on my computer…

    That being said, I hope my original question was not too fuzy/stupid… (I was really desperate at the time I posted it…)

    Few more questions

    Now, before I mark this topic as solved here is a few questions I still have :

    What should I change in my coding habits for such mistakes not to happen ever again ?

    My personal conclusion is : macros are evil, only use them when they are absolutely necessary (in my case they could have been avoided). Judging from my limited experience, it seems to me that in C++ they are only useful when passing flags during compilation (for instance using gcc's -D option)

    Do you agree with that ? Is there something else you would recommend ?

    Also, @laserlight in your previous post you wrote some basic code showing how you organise your source code when using unique_ptrs. Could you do the same in the case I have a class holding objects (instead of pointers to objects or references to objects) please ? I can get such a code to work, but I would like to make sure the way I would do it is clean/smart. I find that I tend to have very long initialisation lists, and I don't think it's ok.

  12. #12
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    You are violating the One Definition Rule (ODR), which is probably why you're seeing weird things. You're in the undefined behaviour land. All source files (translation units) MUST see the exact same class definitions, but due to your macros, they don't.
    In essence: don't use macros unless you absolutely have to. If you do, avoid them in headers. If you have to use them in headers, don't use them to change definitions depending on macro conditions.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #13
    C++ rookie Galdor's Avatar
    Join Date
    Apr 2016
    Location
    Belgium
    Posts
    32
    You are violating the One Definition Rule (ODR)
    @Elysia thanks for the tip, I did not know it got a name !

  14. #14
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by Galdor View Post

    Few more questions

    Now, before I mark this topic as solved here is a few questions I still have :

    What should I change in my coding habits for such mistakes not to happen ever again ?

    My personal conclusion is : macros are evil, only use them when they are absolutely necessary (in my case they could have been avoided). Judging from my limited experience, it seems to me that in C++ they are only useful when passing flags during compilation (for instance using gcc's -D option)

    Do you agree with that ? Is there something else you would recommend ?
    Yep that's pretty much right. Macros are a tool of last resort in c++. As you say, they can be used for compile time flags. There are other uses too, but It's always preferable to use a function of constant variable when possible, because those are aware of the C++ type system.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  15. #15
    C++ rookie Galdor's Avatar
    Join Date
    Apr 2016
    Location
    Belgium
    Posts
    32
    Quote Originally Posted by King Mir View Post
    Yep that's pretty much right. Macros are a tool of last resort in c++. As you say, they can be used for compile time flags. There are other uses too, but It's always preferable to use a function of constant variable when possible, because those are aware of the C++ type system.
    All right !

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. need assistance with a valgrind error within clang
    By saldar05 in forum C Programming
    Replies: 2
    Last Post: 03-04-2013, 01:59 AM
  2. Don't understand why valgrind is giving an error here.
    By dayalsoap in forum C Programming
    Replies: 15
    Last Post: 03-02-2013, 11:59 AM
  3. Valgrind error help
    By edishuman in forum C Programming
    Replies: 1
    Last Post: 11-12-2011, 03:37 PM
  4. valgrind error - still reachable
    By myle in forum C Programming
    Replies: 1
    Last Post: 04-19-2009, 08:57 PM
  5. Constructor Initialiser List
    By 0rion in forum C++ Programming
    Replies: 5
    Last Post: 02-19-2006, 01:36 PM

Tags for this Thread