2

As far as I understand

int* p = new int();

Is something like creating an int with a constructor. If so why does the following code work like an array?

int* p = new int();
    
*p = 5;
    
p[1] = 15;
    
for (int i = 0; i < 2; i++)
    cout << p[i] << endl;
5
15
anastaciu
  • 23,467
  • 7
  • 28
  • 53
Overdrowsed
  • 107
  • 7
  • 10
    As the old saying goes, undefined behaviour is undefined. Any appearance it gives of "working" is just an evil disguise. – molbdnilo May 10 '22 at 14:22
  • 7
    You are, basically, creating an array of a single element. – Some programmer dude May 10 '22 at 14:23
  • 3
    That is an undefined behavior. – Asesh May 10 '22 at 14:23
  • 6
    Also, arrays tend to *decay* to pointers to their first element. If you have an array `a` and pass it to a function expecting a pointer, then the compiler will treat it as `&a[0]`. Furthermore, for any pointer (or array) the expressions `a[i]` is *exactly* the same as `*(a + i)`. So there's a lot of commonality between pointers and arrays because of the array-to-pointer decay. – Some programmer dude May 10 '22 at 14:25
  • 4
    Note that `*p = 5;` is equivalent to `p[0] = 5;`. This code is closely related to `int p = 5; int* q = &p; p[1] = 15;`, which is also undefined and may also appear to work. – molbdnilo May 10 '22 at 14:26
  • 1
    To continue, in C++ pointers to "arrays" are really pointers to a single element (like what happens in the array-to-pointer decay). And when all you have is a pointer then that's really all you have, there's no information about how many "elements" there are. C++ just doesn't have any kind of bounds-checking, for array or pointers. – Some programmer dude May 10 '22 at 14:27
  • 2
    `p[1] = 15;` -- This is a buffer overrun. You have now stumbled into why buffer overruns are security issues. Your program allocated for 1 `int`, but you stepped into space not allocated by you and changed the value at that location. That is exactly how buffer overrun exploits are accomplished. – PaulMcKenzie May 10 '22 at 14:40
  • 1
    This is undefined behavior. Why it appears to work in your case is usually a `c++` implementation allocates memory from an OS in pages that are several kB in size even though you asked for 4 or 8 bytes. It does this because requesting memory from the OS is usually a costly operation in time. – drescherjm May 10 '22 at 15:18
  • 2
    Everytime someone invokes Undefined Behaviour, a kitten dies. – Raildex May 10 '22 at 15:23

2 Answers2

6

Why does new int() work like an array in C++?

p[1] is equivalent to *(p + 1), it simply dereferences the pointer to access the value stored in the memory location where it points to, the notation is similar to array notation, it's allowed and is preferred to pointer notation because it's more readable.


As far as I understand

 int* p = new int();

Is something like creating an int with a constructor.

Yes, but that's not all, you are also allocating memory for exactly one int and assigning the memory address to the pointer p.

Note that it could be int* p = new int[2], it's the exact same pointer, but in this case the memory block returned by new is good for 2 int instead of just the one, incidentally this would make the rest of your code valid, except for the fact that you do not delete the memory allocated by new, which would cause a memory leak.

Now consider the following code:

int arr[10];
int* p = arr;

In this case you have the exact same pointer, but it will be pointing to the first element of an array with automatic storage duration.

The pointer does not know how much memory it points to because it's pointing to the first element in a given memory block, for the program it's not apparent how big that block is. When indexing the pointer, it's the programmer responsability to not overrun that memory.

One important thing to note is that, in some cases, where other languages might stop you from putting your foot in it, C++ does not, it trusts the programmer to produce correct code, that's why it's usually harder to be a C++ programmer.


As already pointed out your program incurs in undefined behavior while accessing p[1]. Note the first phrase of the linked resource, it simply states:

[Undefined behavior] renders the entire program meaningless if certain rules of the language are violated

That is the case here, the memory you are accessing is located out of the bounds defined by your manual memory allocation. The fact that the output is what you expect is a matter of (bad, I would say) luck, it may output the correct result today and crash tomorrow, who knows.

You can see by the above examples that this situation would be hard to diagnose, it's the exact same type for the 3 sample cases.

In any case there are ways to diagnose memory problems like this, examples are valgrind and gcc address sanitizer, among others.


On a side note, avoid using raw pointers, use smart pointers or one of the C++ containers if possible.

I would also encourage you to acquire some knowledge on the related topic of OOP RAII principles.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • Yes, with smart pointers and C++ `std:array` instead of C arrays this would not have happened, there is no decay. (It is still possible to have out-of-bounds errors with C++ types - on purpose for performance reasons - which lead to UB.) – Sebastian May 10 '22 at 15:25
2

why does the following code work like an array?

It doesn't. The program has undefined behavior meaning it is still in error even if it doesn't say so explicitly and "seems to be working". This is due to the use of the expressions p[1] and p[i] in your program.

Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely(or make conclusions based) on the output of a program that has undefined behavior. The program may just crash.

So the output that you're seeing(maybe seeing) is a result of undefined behavior. And as i said don't rely on the output of a program that has UB. The program may just crash.

So the first step to make the program correct would be to remove UB. Then and only then you can start reasoning about the output of the program.


1For a more technically accurate definition of undefined behavior see this where it is mentioned that: there are no restrictions on the behavior of the program.

Jason
  • 36,170
  • 5
  • 26
  • 60