C++ programming: getting an error or not according to the computer in use.


C++ programming: getting an error or not according to the computer in use.



Hello everyone,



Here a code in c++ programming:


#include <iostream>

using namespace std;

class Buffer {

double* doubles;
int size;

public:

Buffer(int size): size(size), doubles(new double[size])
{
for (int i=0; i<size; ++i) doubles[i] = 0.0;
}

~Buffer() {
delete doubles;
}

void fill(double d) {
Buffer newBuffer(size);
for (int i=0; i<size; ++i) newBuffer.doubles [i] = d;
this->doubles = newBuffer.doubles;
}

void print() const {
for (int i=0; i<size; ++i) cout << doubles[i] << " ";
cout << endl;
}

};

int main() {
Buffer b1(5); b1.print();
Buffer b2(5); b2.fill(12.34); b2.print();
}`



This code is not supposed to run without exception.
Indeed, when my teacher run it (with his computer) he gets the next error:
* Error in a.out: double free or corruption (fasttop):
0x0000000001178c90 *
.
Nevertheless, when I run it (with my computer) I don't get any error, and the code run without any exception.



I post a screen-shot of my terminal after running the code:
Screen-shot of the terminal.



As you can see in the screen-shot, to run the code, I use the clang compiler (my teacher too), with the c++11, 14, and 17 versions.
Thus, my problem doesn't seem to be a c++ version's problem.
Adding the fact that I use Linux over Ubuntu, and my teacher too, it's probably not a operating-system's problem.



I'm very interested to know what is the reason of such a result ?



Thank you in advance.





C++ doesn't normally throw exceptions for programming errors. You can't expect consistent and/or sensible behaviour for a double free.
– chris
Jun 29 at 10:39





The statement this->doubles = newBuffer.doubles in fill() causes both objects (*this and newBuffer) to have the same value of the doubles member. This means, when the two objects are destroyed, the destructor does delete doubles on the same pointer more than once. That is undefined behaviour.
– Peter
Jun 29 at 10:42



this->doubles = newBuffer.doubles


fill()


*this


newBuffer


doubles


delete doubles





Since you are double-freeing the memory, you are invoking undefined behavior. The code, that invokes undefined behavior, can do anything at all, including, but not limited to: seeming to work fine, crashing, or formatting your hard drive.
– Algirdas Preidžius
Jun 29 at 10:43





This code is not supposed to run without exception. Yes, it does, since no exception will be thrown. Rather, a run-time error may occur from the run-time environment.
– Walter
Jun 29 at 10:43





What are you actually asking? Why this code triggers double free? Or why it doesn't always trigger double free?
– Walter
Jun 29 at 10:44




6 Answers
6



The problem are these two lines:


Buffer newBuffer(size);
...
this->doubles = newBuffer.doubles;



The first line creates a new Buffer object. The second line makes this->double point to the same memory that newBuffer.doubles is pointing to. That is, you have two separate pointers both pointing to the very same memory.


Buffer


this->double


newBuffer.doubles



Now when that's done, the newBuffer object goes out of scope and is destructed. And the destructor will free the memory that both this->doubles and newBuffer.doubles is pointing to. That memory is no longer available to your application, using it in any way leads to undefined behavior. This includes both the printing and more specifically the deletion of the memory a second time in the destructor.


newBuffer


this->doubles


newBuffer.doubles



The simplest way to solve your problem is to realize that you don't need the temporary newBuffer object, you can just intitialize this->doubles[i] directly in the loop:


newBuffer


this->doubles[i]


for (int i=0; i<size; ++i) this->doubles [i] = d;



As for why it sometimes seems to work, it's because one of the possible behaviors of UB (Undefined Behavior) is to seemingly work fine. Other times it might cause a crash or other weird behavior in seemingly unrelated parts of your program.





You don't even need that this-> there.
– Qubit
Jun 29 at 10:46


this->





Upped. Your explanation is so much better than mine.
– Bathsheba
Jun 29 at 10:56





Thank you for the reply. How a Undefined Behavior may be detected if (like in my case) the code work fine? And is this part of chance (to have the code run or not) may be a big issue for a programmer ?
– Samuel Bismuth
Jun 29 at 12:22





@SamuelBismuth A good start is to always build with extra warnings enabled, as the compiler might be able to detect these issues. But since the compiler can't always do that, use of static analysis tools (also known as linters) are also useful. For critical code one should also employ code-review where one (or preferably more) people look at your code to see if it's good or makes sense. But worst case scenario is that UB sometimes isn't detected for years, even in bit commercial programs.
– Some programmer dude
Jun 29 at 12:31





I get it. Thanks you for your time, it was very informative.
– Samuel Bismuth
Jun 29 at 12:44



The flaw is in fill. You create a new Buffer object called newBuffer, and then trash the doubles in this (that actually causes a memory leak). newBuffer will delete its buffer when it goes out of scope at the end of fill, and so will the destructor called in main when b2 goes out of scope!


fill


Buffer


newBuffer


doubles


this


newBuffer


fill


main


b2



That's undefined behaviour: delete is called twice on the same pointer. Boom.


delete



The best thing to do by a country mile is to use a std::vector<double> as the class member. Having bare pointers as class members is a recipe for trouble.


std::vector<double>



Your program constitutes what is known as undefined behaviour (for the obvious reasons pointed out in other answers), when the run-time behaviour is unpredictable.



Hence the outcome when running the code on different machines, with different compilers, at different times, by different people, ... may well be quite different, with a crash (double free or corruption) being the most benign (in contrast to running w/o apparent error).



Of course, undefined behaviour should be avoided like the plague. Unfortunately, this is harder said than done, as there are no reliable tools to detect all of it. Many programmers don't even know what constitutes undefined behaviour and what not.





Thanks for the answer. Is code with an unpredictable behavior a big problem or is programmer can easily detect it ?
– Samuel Bismuth
Jun 29 at 12:26





I consider code with undefined behavior, hence unpredictable behavior, to be a big problem which can be very difficult to detect. There are tools, such as valgrind and enabling all compiler warnings, which can help detect these otherwise hard to notice problems.
– Eljay
Jun 29 at 12:57



Your fill function is causing a memory leak and assigning a pointer that is about to be invalidated to your array.



You create a new buffer with a new array, fill that with some values, then you reassign the pointer of your old buffer (losing track of some memory - memory leak). After that, the new buffer goes out of scope so the destructor is called, deleting the memory of both the old and new buffers. And now you call print on memory that is not yours, hence you corrupt the heap. And THEN on top of it all, you try to free it again, even though it's already been freed.



Why not just set the values in the current buffer without creating a temporary, that seems better in every aspect.



Your fill function leaks memory, which in turn trigers UB. You may experience wierd behavior, crash, no symptom or troubles in other programs due to memrory disorder. The exact observation depends on platform and chance.


fill



You can override the assignment operator to do a deep copy. This will solve your problem.






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Comments

Popular posts from this blog

paramiko-expect timeout is happening after executing the command

how to run turtle graphics in Colaboratory

Export result set on Dbeaver to CSV