开发者

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:

  1. 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.
  2. Dynamically allocated memory should be explicitly delete-ed by the programmer, whereas static and automatic storage variables are taken care of by the "environment"

  3. 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

  4. When allocating an array using new[], the size can be 0

  5. (As already pointed out by @R Sahu) The types of &x and &z are different:

    • &x is int (*)[10]
    • &z is int **


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], 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;
  • pointer arithmetic can be used to obtain an address of an element of the array (e.g. 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];
  • Pointer dereferencing and array access are equivalent. i.e. *(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.

  1. In the first case 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).
  2. In the second, 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.

  1. 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
    
  2. 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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜