views:

186

answers:

5

I'm studying for a final exam and I stumbled upon a curious question that was part of the exam our teacher gave last year to some poor souls. The question goes something like this:

Is the following program correct, or not? If it is, write down what the program outputs. If it's not, write down why.

The program:

#include<iostream.h>
class cls
{     int x;
      public: cls() { x=23; }
      int get_x(){ return x; } };
int main()
{     cls *p1, *p2;
      p1=new cls;
      p2=(cls*)malloc(sizeof(cls));
      int x=p1->get_x()+p2->get_x();
      cout<<x;
      return 0;
}

My first instinct was to answer with "the program is not correct, as new should be used instead of malloc". However, after compiling the program and seeing it output 23 I realize that that answer might not be correct.

The problem is that I was expecting p2->get_x() to return some arbitrary number (whatever happened to be in that spot of the memory when malloc was called). However, it returned 0. I'm not sure whether this is a coincidence or if class members are initialized with 0 when it is malloc-ed.

  • Is this behavior (p2->x being 0 after malloc) the default? Should I have expected this?
  • What would your answer to my teacher's question be? (besides forgetting to #include <stdlib.h> for malloc :P)
+3  A: 

new calls the constructor, malloc will not. So your object will be in an unknown state.

gbrandt
I know `malloc` doesn't call the constructor. My question was about the class member `x` -- does it get initialized with 0 or does it get some random value that was in memory at that spot?
Felix
@Felix: If the constructor isn't called, the value of member data is undefined behavior.
Jefromi
@Felix Initializing x happens in the constructor, so it's undefined. Even using `new` wouldn't zero out x unless x was actually set to 0 in the constructor; member fields don't get default initializations like in Java
Michael Mrozek
If you're building in debug mode I believe the memory will probably be 0'd. If you're in release mode you will probably see some random number in that space.
tloach
@tloach - that depends on the compiler. Microsoft C++ will fill uninitialized memory with 0xcdcdcdcd (or something like that. Freed memory also gets filled with some similar number) in debug mode. In release mode it doesn't modify the memory at all during malloc/new.
jmucchiello
@tloach, in fact, some operating systems will zero out all the memory allocated for a process (in release). That will only obscure the error more since a simple test program (where malloc always provides new memory) can seem to initialize the memory, while in a more complex environment the values would seem random.
David Rodríguez - dribeas
As tloach mentioned, in order to more easily spot issues in debug mode, some compilers will 0-out the memory that is malloc so that if you stumble on a 0 you know your forgot to allocate it. Unfortunately using 0 might be just what you intended in which case.. you'll just assume it works :/
Matthieu M.
@tloach: Like jmucchiello said, the point here is... undefined behavior is undefined.
Jefromi
A: 

The actual behaviour is unknown, because new acts pretty the same like malloc + constructor call.

In your code, the second part is missing, hence, it could work in one case, it could not, but you can't say exactly.

Kotti
+10  A: 
  • Is this behavior (p2->x being 0 after malloc) the default? Should I have expected this?

No, p2->x can be anything after the call to malloc. It just happens to be 0 in your test environment.

  • What would your answer to my teacher's question be? (besides forgetting to #include for malloc :P)

What everyone has told you, new combines the call to get memory from the freestore with a call to the object's constructor. Malloc only does half of that.

Fixing it: While the sample program is wrong. It isn't always wrong to use "malloc" with classes. It is perfectly valid in a shared memory situation you just have to add an in-place call to new:

p2=(cls*)malloc(sizeof(cls));
new(p2) cls;
jmucchiello
If you are using placement new you must also explicitly call the destructor (you can not use delete as you did not use new). Also you should enphasis that this is not common (mainley used when building your own container obejcts when you pre-allocate storage for multiple items (not just one))
Martin York
I would argue for using `reinterpret_cast`. It emphasizes that you're doing something very dangerous.
Matthieu M.
A: 

Why can't 0 be an arbitrary number too? Are you running in Debug mode? What compiler?

VC++ pre-fills newly allocated memory with a string of 0xCC byte values (in debug mode of-course) so you would not have obtained a zero for an answer if you were using it.

SDX2000
I'm using g++, no debug.
Felix
A: 

Malloc makes no guarantuee to zero out the memory it allocated and the result of the programm is undefined.

Otherwise there are many other things that keep this program from being correct C++. cout is in namespace std, malloc needs to included through#include <cstdlib> and iostream.h isn't standard compliant either.

pmr