-2

I'm very confused with regard to the following instructions:

#include <iostream>
#define MAX_IT 100
using namespace std;

class Integer{

private :
    int a;
public:
    Integer(int valoare){a=valoare;}
    int getA(){return a;}
    void setA(int valoare){a=valoare;}
};

int main(){
    Integer* a=new Integer(0);

    //cout<<a[0].getA();
    for(int i=1;i<=MAX_IT;i++)
    {
        a[i]=*(new Integer(i));
    }

    for(int i=0;i<=MAX_IT;i++)
        cout<<a[i].getA()<<endl;

    return 13;
}

It works for small values of MAX_IT, but when I try to set MAX_IT to 1000 it doesn't work anymore. Initially, I thought "new" operator was supposed to do the job, but after some reading documentation I understood it is not supposed to work at all like this (out of bound array).

So my question is: why is it working for small values of MAX_IT and not for bigger ones?

EDIT:

I am experimenting with this code for a larger program, where I am not allowed to use STL. You have not understood my concern: if I have Integer *var=new Integer[10]; for(int k=1;K<10;k++) *(var+k)=k; //this is perfectly fine, but if I try var[10]=new Integer; //this should not be working and should generate a memory problem //My concern is that it is working if I do it only 100 times or so...The question if why is it working everytime for small number of iterations?

Radu
  • 83
  • 2
  • 9

3 Answers3

17

Because by allocating space for one Integer then using it as an array of multiple Integers, your code invokes undefined behavior, meaning that it can do anything, including crashing, working seemingly fine, or pulling demons out of your nose.


And anyways it's leaking memory. If you don't need dynamic memory allocation, then don't use it.

a[i]=*(new Integer(i));

And kaboom, you lost the pointer to the Integer, no chance to delete it later. Leaks.


If you don't need raw arrays, don't use them. Prefer std::vector. Or switch to C if C++ is too hard.

std::vector<Integer> vec;
vec.push_back(Integer(1337));
  • WTH, +8. I do **not** want a "nice answer" badge for such a question...! –  Jun 17 '13 at 21:37
  • 1
    @milleniumbug LOL. It's just that I'm surprised to have such an applause for a simple answer, whereas [other answers of mine](http://stackoverflow.com/questions/15114140/writing-binary-number-system-in-c-code/15114188#15114188) are much more useful and much less appreciated (even double-downvoted). Sometimes I just don't get the logic of the SO community... :( –  Jun 17 '13 at 21:40
  • 1
    Hah, guess who gave you a nice answer *and* an enlightened. Nice reverse psychology tactics. – chris Jun 17 '13 at 21:42
  • This code was just for testing, I need this algorithm in a larger program, where I am not allowed to use STL. – Radu Jun 17 '13 at 21:42
  • 2
    @Radu Not *allowed?* Homework or stupid boss or embedded platform? –  Jun 17 '13 at 21:43
  • 1
    @Radu If I was allowed to use C++ and not allowed to use STL, I would rewrite needed portions of STL myself ;) Because I can. – milleniumbug Jun 17 '13 at 21:47
  • @milleniumbug You appear to be much more patient than me, I'd just silently fall back to C :D –  Jun 17 '13 at 21:48
  • 1
    @milleniumbug Well, thinking a bit more about it, if I wasn't allowed to use the STL, I'd just use the C++ standard library instead. –  Jun 17 '13 at 21:57
  • most of us can :) however, vector from STL uses an internal array. Everytime its capacity is reached, it allocates space for a new, double-sized array and copies all the elements there. If my code would work for larger sets of data, it would be much more efficient, since it poses no need to copy ALL the elements. – Radu Jun 17 '13 at 22:01
  • 1
    Pedant: This answer incorrectly describes when undefined behavior occurs. It's only UB if you write beyond the actual size of the object the allocator gave you. As long as you stay within the bounds of the object returned, there is no undefined behavior. This size is guaranteed to be at least the size you requested, but can be larger. How much larger is of course extremely non-portable. – Ben Voigt Jun 17 '13 at 22:04
  • @Radu In C++11 it can move them. In C++03 `std::vector` can't, but `???::vector` could swap them. – milleniumbug Jun 17 '13 at 22:06
  • @BenVoigt Thanks for the suggestion, but I don't see where it is incorrect. How `Integer *int = new Integer(); int[1] = foo;` **is not** UB? –  Jun 17 '13 at 22:06
  • 1
    @Radu: Sounds like you actually have no idea if using the standard library would be a good idea, and are just guessing that it would be slower. Have you done any profiling? – GManNickG Jun 17 '13 at 22:07
  • @H2CO3 Yes, I always forget about STL/C++STDLIB name conflict. While I wrongly call "subset of C++STDLIB that contains algorithms, containers, adapters and iterators" STL, at least I'm consistent about it :) – milleniumbug Jun 17 '13 at 22:10
  • @H2CO3: Hmm, ok in this instance it is undefined behavior because the object has a non-trivial constructor and you skipped calling it. However there's a general question lurking in there. The C++ Standard's rules for object lifetime say that if you had enough storage for an array of POD objects returned from the allocator, then an array actually exists and you can use it. – Ben Voigt Jun 17 '13 at 22:10
  • @BenVoigt Even if you asked for less of them and you don't know how many of them you actually have? –  Jun 17 '13 at 22:11
  • @H2CO3: Therein lies the problem. You don't know how many you actually have. I mentioned that in my answer, which attempts to answer the question instead of simply correcting the code (the OP clearly knows the correct version since it's contained toward the bottom of the question). – Ben Voigt Jun 17 '13 at 22:13
  • Section 3.8 in the Standard isn't very long or technical -- it's worth reading at least the first 4 paragraphs. – Ben Voigt Jun 17 '13 at 22:16
  • 1
    @BenVoigt If you allocate one element, and then access the second, it is undefined behavior. Period. If the `operator new` function actually allocates more memory than necessary, and you access it, there is a good change that the program will seem to work. But that's just one possible effect of undefined behavior. – James Kanze Jun 17 '13 at 23:36
  • @James: The Standard says that if you acquire sufficient memory for two elements, and the type is trivially constructable, then you have two elements. Finding out whether you acquired more memory than you asked for is of course a problem, but I remember seeing a proposal to let you find that out. – Ben Voigt Jun 18 '13 at 03:13
  • 1
    @BenVoigt The standard also says that if you call the `operator new` function with `n`, you allocate `n` bytes. In other words, any access beyond those `n` bytes is undefined behavior. There may be proposals to change this, but they are, for the moment at least, just proposals. – James Kanze Jun 18 '13 at 08:59
2

The reason that things tend to work nicely when you overflow your buffer by just a little bit is... memory fragmentation! Who would have guessed?

To avoid memory fragmentation, allocators won't return you a block of just sizeof (Integer). They'll give you a somewhat larger block, to ensure that if the block is later freed before the adjacent blocks, it's at least big enough to be useful.

Exactly how big this is can vary by architecture, OS, compiler version, or even how much memory is physically present in the machine. You should consider it to be completely unpredictable. Also, some libraries designed to help catch this sort of bug force any small object to be placed at the end of the block instead of the beginning, so the extra bytes could be negative array indices instead of positive.

Therefore, don't ever rely on having spare area given to you for free after (or before) an object.


Guru note: Occasionally someone comes up with a valid use for the extra memory, and asks for a way to discover how large it is. One good example is that the capacity (not size!) of a std::vector could be adjusted to match the actual allocated space instead of the requested space, and therefore reduce (on average) the number of reallocations needed. Such requests usually come paired with other guru allocator APIs, such as the ability to expand an allocation in-place if there happen to be free blocks adjacent.


Note that in your particular case you do still have undefined behavior, because you're calling operator= on a non-POD object which hasn't first been constructed. If you gave class Integer a trivial default constructor that would change.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

you actually need

Integer* a=new Integer[MAX_IT];

//cout<<a[0].getA();
for(int i=1;i<MAX_IT;i++) << note < not <=
{
    a[i]=i;
}

better would be to use std::vector though

pm100
  • 48,078
  • 23
  • 82
  • 145
  • I am experimenting with this code for a larger program, where I am not allowed to use STL. You have not understood my concern: if I have Integer *var=new Integer[10]; for(int k=1;K<10;k++) *(var+k)=k; //this is perfectly fine, but if I try var[10]=new Integer; //this should not be working and should generate a memory problem //My concern is that it is working if I do it only 100 times or so. – Radu Jun 17 '13 at 21:43
  • Your code was wrong; anything might happen. In yr case it worked some times but not others. Fix yr code and all will be well – pm100 Jun 17 '13 at 22:30