Declaring array of int
Is there any difference between these two declarations?
int x[10];
vs.
int* x = new int[10];
I suppose the开发者_运维百科 former declaration (like the latter one) is a pointer declaration and both variables could be treated the same. Does it mean they are intrinsically the same?
#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;
According to the standard, we should actually distinguish between three different types of array declarations:
int x[10];
void method() {
int y[10];
int *z = new int[10];
delete z;
}
The first declaration, int x[10]
, uses static storage duration, defined by cppreference as: "The storage for the object is allocated when the program begins and deallocated when the program ends. Only one instance of the object exists. All objects declared at namespace scope (including global namespace) have this storage duration, plus those declared with static or extern."
The second one, int y[10]
, uses automatic storage duration, defined by cppreference as: "The object is allocated at the beginning of the enclosing code block and deallocated at the end. All local objects have this storage duration, except those declared static, extern or thread_local."
The third one, int *z = new int[10]
, is usually referred to as dynamic memory allocation, and is actually a two-step sequence:
- First, the operator new is called, which allocated memory dynamically using either the default allocation methods of the standard library or a user defined implementation (since new can be overridden during run-time). The allocated memory will be sufficient to fit the N elements allocated, plus any additional memory required to keep metadata for the given allocation (so that it can be later successfully freed).
- Second, if the first step is successful, we then proceed to initialize or construct each object in the array.
As already mentioned by other comments, these types of declaration have their subtle differences, but the most common ones are:
On most modern operating systems:
- automatic storage is usually allocated on the stack, which is (generally speaking) a thread-specific pre-allocated memory space using a LIFO mechanism
- static storage uses pre-allocated memory space reserved inside the executable (more specifically .BSS and .DATA segments, depending if the variable is zero initialized or not)
- dynamic memory is allocated using heap memory, and is subject to the mercy of the system's RAM management system and other mechanisms such as paging.
Dynamically allocated memory should be explicitly
delete
-ed by the programmer, whereas static and automatic storage variables are taken care of by the "environment"Static and automatic storage variables are limited to a specific scope, whereas dynamically allocated memory has no bounds, meaning, a variable declared in one module, can be passed to any other module that operates in the same address space
When allocating an array using
new[]
, the size can be 0(As already pointed out by @R Sahu) The types of
&x
and&z
are different:&x
isint (*)[10]
&z
isint **
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.
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;
- array syntax (e.g.
x[i]
, wherei
is an integral value between0
and9
inclusive) can be used to set or retrieve values of the respective arrays in the above syntax; - pointer arithmetic can be used to obtain an address of an element of the array (e.g.
x + i
is equivalent to&x[i]
fori
between0
and10
inclusive. [yes, it is possible to obtain "one past the end" address]; - Pointer dereferencing and array access are equivalent. i.e.
*(x+i)
andx[i]
are equivalent, fori
between0
and9
[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.
- In the first case
sizeof(x) == sizeof(int)*10
.sizeof(int)
gives an implementation-defined balue, butsizeof(x)/sizeof(*x)
will always give the number of elements in the array (i.e. astd::size_t
with value10
). - In the second,
sizeof(x) == sizeof(int *)
- which is an implementation-defined value. The value ofsizeof(x)/sizeof(*x)
is practically exceedingly unlikely to yield a value of10
. 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), thenx
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 ofnew x[10]
) is never deallocated. This means the lifetime ofx
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.
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;
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. :)
If you want to size an array dynamically, e.g.:
void doSomething(int size)
{
int x[size]; // does not compile
int *z = new int[size];
//Do something interesting ...
doMore(z, size);
}
then x will not compile in C++, so you have to use z. The good news is that you can now use z, in most cases, as though it were statically allocated, e.g.:
void doMore(int anArray[], int size)
{
// ...
}
takes z as an argument, treating the pointer as an array.
精彩评论