Is there any difference between these two declarations?
int x[10];
vs.
int* x = new int[10];
I suppose th
They are the same as far as both x's point to the first memory address in the array of 10 integers, however very different in that
int x[10]
declares the memory in static random access memory, and the keyword 'new' creates them dynamically with the heap, and is about the same as using malloc in c to dynamically create an array.
Not only that, but (I believe, haven't tested the theory) there is a chance that:
int* x = new int[10];
could fail, and depending on the compiler, could return an error or a null pointer. If the c++ compiler adheres to ANSI/ISO standards, then it supports a "no-throw" form of new which returns a null if allocation fails, rather than throwing an exception.
The other difference being that the 'new' operator can be overloaded.
What I'm not sure of, however, is if either one (in c++) creates a null terminated array. I know that in c, in the compiler I use at least, you must make sure to always append a \0 to any strings or arrays if you expect to be able to iterate over them without overextending the bounds.
Just my $.02 worth. :)
#include<iostream>
int y[10];
void doSomething()
{
int x[10];
int *z = new int[10];
//Do something interesting
delete []z;
}
int main()
{
doSomething();
}
int x[10];
- Creates an array of size 10 integers on stack.
- You do not have to explicitly delete this memory because it goes away as stack unwinds.
- Its scope is limited to the function doSomething()
int y[10];
- Creates an array of size 10 integers on BSS/Data segment.
- You do not have to explicitly delete this memory.
- Since it is declared global
it is accessible globally.
int *z = new int[10];
- Allocates a dynamic array of size 10 integers on heap and returns the address of this memory to z
.
- You have to explicitly delete this dynamic memory after using it. using:
delete[] z;
First case: x
is created on stack/data segment based on whether it's non-static local variable or static/global variable. And address of x
is not modifiable.
Second case: 'x' is a pointer pointing to an array created generally on heap (free store). You can change x
pointing to something else also. Moreover, you need to take care of deallocating it by using delete[] x;
First one is an array of int
of size 10
. Saying that its created on stack is wrong. Because the Standard doesn't guarantee that. Its implementation-defined. Its storage duration could be static or automatic depending on whether x
is global variable or local variable.
In the second one, you create a pointer of type int*
. Not necessarily created on heap, the Standard doesn't say that. The allocated memory spans over 10 * sizeof(int)
bytes. For this you've to deallocate the memory yourself, by writing:
delete [] x;
In this case, the memory of the pointer x
is dynamically allocated, and dynamically deallocated, so such objects is said to have dynamic storage duration.
The declarations are completely different.
The first case,
int x[10];
declares x
as an array of 10
integer, whereas the second case,
int* x = new int[10];
declares x
as a pointer to int
- a variable with value equal to an address of an int
, and initialises that pointer to the result of a new expression (new int [10]
) that dynamically allocates an array of ten integers.
Not withstanding the differences, the two can be used in similar ways;
x[i]
, where i
is an integral value between 0
and 9
inclusive) can be used to set or retrieve values of the respective arrays in the above syntax;x + i
is equivalent to &x[i]
for i
between 0
and 10
inclusive. [yes, it is possible to obtain "one past the end" address];*(x+i)
and x[i]
are equivalent, for i
between 0
and 9
[dereferencing a "one past the end" pointer gives undefined behaviour].However, there are also some key differences, for example;
Results of the sizeof
operator. sizeof(x)
gives different values in the two cases.
sizeof(x) == sizeof(int)*10
. sizeof(int)
gives an implementation-defined balue, but sizeof(x)/sizeof(*x)
will always give the number of elements in the array (i.e. a std::size_t
with value 10
).sizeof(x) == sizeof(int *)
- which is an implementation-defined value. The value of sizeof(x)/sizeof(*x)
is practically exceedingly unlikely to yield a value of 10
. Which means this technique cannot be used to obtain the number of elements.Lifetime.
In the first case, the lifetime of x
depends on the scope in which the declaration occurs. If the declaration occurs at file scope (i.e. in a compilation unit, outside any function block), then x
has static storage duration (so exists for as long as the program is running). If the declaration occurs in a block, x
- and all its elements - ceases to exist when the block ends. For example
{
int x[10];
} // x and all its elements cease to exist here
In the second case, it is only the pointer x
that has a lifetime that depends on scope. The dynamically allocated memory (the result of new x[10]
) is never deallocated. This means the lifetime of x
and lifetime of the (dynamically allocated) array it references are decoupled, which brings us to a third difference .....
Result of assignment An array cannot be reassigned, a pointer can (unless appropriately const
qualified).
Consider a context of
// x as previously defined in one or the other form
int y[10];
int z;
x = y;
x = &z;
In the first case, both assignments will result in a compiler diagnostic - the assignments are invalid. In the second case, the assignments are valid and cause x
to point at the address of (the first element of) y
and to the address of z
respectively. Unless the value of x
is stored in another pointer before reassignment, the memory allocated by the new expression (new int [10]
) is leaked - it is no longer accessible by the program, but is not released either.
The only thing similar between
int x[10];
and
int* x = new int[10];
is that either can be used in some contexts where a int*
is expected:
int* b = x; // Either form of x will work
void foo(int* p) {}
foo(x); // Either form will work
However, they cannot be used in all contexts where a int*
is expected. Specifically,
delete [] x; // UB for the first case, necessary for the second case.
Some of the core differences have been explained in the other answers. The other core differences are:
Difference 1
sizeof(x) == sizeof(int)*10 // First case
sizeof(x) == sizeof(int*) // Second case.
Difference 2
Type of &x
is int (*)[10]
in the first case
Type of &x
is int**
in the second case
Difference 3
Given function
void foo(int (&arr)[10]) { }
you can call it using the first x
not the second x
.
foo(x); // OK for first case, not OK for second case.