developer tip

int 배열 선언

optionbox 2020. 11. 28. 09:08
반응형

int 배열 선언


이 두 선언간에 차이점이 있습니까?

int x[10];

int* x = new int[10];

전자 선언 (후자의 선언과 마찬가지로)이 포인터 선언이고 두 변수가 동일하게 처리 될 수 있다고 가정합니다. 본질적으로 동일하다는 의미입니까?


#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]; 

-스택에 크기 10 정수의 배열을 만듭니다.
-이 메모리는 스택이 풀릴 때 사라지기 때문에 명시 적으로 삭제할 필요가 없습니다.
-그 범위는 기능으로 제한됩니다.doSomething()

int y[10];

-BSS / Data 세그먼트에 크기 10 정수의 배열을 만듭니다.
-이 메모리를 명시 적으로 삭제할 필요는 없습니다.
-선언 global되었으므로 전 세계적으로 접근 가능합니다.

int *z = new int[10];

-힙에 크기 10 정수의 동적 배열을 할당하고이 메모리의 주소를에 반환합니다 z.
-이 동적 메모리를 사용한 후에는 명시 적으로 삭제해야합니다. 사용 :

delete[] z;

비슷한 유일한 것은

int x[10];

int* x = new int[10];

a int*가 예상되는 일부 컨텍스트에서 사용할 수 있다는 것 입니다.

int* b = x;   // Either form of x will work

void foo(int* p) {}

foo(x);      // Either form will work

그러나 a int*가 예상되는 모든 컨텍스트에서 사용할 수는 없습니다 . 구체적으로 특별히,

delete [] x;  // UB for the first case, necessary for the second case.

핵심 차이점 중 일부는 다른 답변에서 설명되었습니다. 다른 핵심 차이점은 다음과 같습니다.

차이 1

sizeof(x) == sizeof(int)*10   // First case

sizeof(x) == sizeof(int*)     // Second case.

차이 2

의 입력 &xIS int (*)[10]첫 번째 경우에

의 유형 &xint**두 번째 경우에

차이 3

주어진 기능

void foo(int (&arr)[10]) { }

x번째가 아닌 첫 번째를 사용하여 호출 할 수 있습니다 x.

foo(x);     // OK for first case, not OK for second case.

첫 번째 int는 size 의 배열입니다 10. 스택에 생성되었다고 말하는 것이 잘못되었습니다. 표준이 그것을 보장하지 않기 때문입니다. 구현 정의. 저장 기간 전역 변수인지 지역 변수 인지에 따라 정적 이거나 자동 일 수 있습니다 .x

두 번째에서는 유형의 포인터를 만듭니다 int*. 반드시 힙에서 생성되는 것은 아니지만 표준은 그렇게 말하지 않습니다. 할당 된 메모리는 10 * sizeof(int)바이트에 걸쳐 있습니다 . 이를 위해 다음과 같이 작성하여 메모리 할당을 취소해야합니다.

delete [] x; 

이 경우 포인터의 메모리 x는 동적으로 할당되고 동적으로 할당 해제되므로 이러한 개체는 동적 저장 기간 이 있다고합니다 .


표준에 따르면 실제로 세 가지 유형의 배열 선언을 구분해야합니다.

int x[10];

void method() {
     int y[10];
     int *z = new int[10];
     delete z;
}

첫 번째 선언 인 cppreference에 의해 정의 된 정적 저장 기간을 int x[10]사용합니다 . "프로그램이 시작될 때 개체에 대한 저장소가 할당되고 프로그램이 종료 될 때 할당이 취소됩니다. 개체의 인스턴스가 하나만 존재합니다. 모든 개체가 네임 스페이스 범위에서 선언되었습니다 (포함). 전역 네임 스페이스)에는이 저장 기간과 함께 static 또는 extern으로 선언 된 기간이 있습니다. "

두 번째 int y[10]cppreference에 의해 정의 된 자동 저장 기간을 사용합니다 . "객체는 둘러싸는 코드 블록의 시작 부분에 할당되고 끝에서 할당 해제됩니다. static, extern 또는 thread_local로 선언 된 객체를 제외한 모든 로컬 객체는이 저장 기간을 갖습니다. . "

세 번째 int *z = new int[10]는 일반적으로 동적 메모리 할당 이라고하며 실제로는 2 단계 시퀀스입니다.

  • 먼저 new 연산자가 호출되어 표준 라이브러리의 기본 할당 방법 또는 사용자 정의 구현을 사용하여 동적으로 메모리를 할당합니다 (new는 런타임 중에 재정의 될 수 있기 때문). 할당 된 메모리는 할당 된 N 요소와 지정된 할당에 대한 메타 데이터를 유지하는 데 필요한 추가 메모리 (나중에 성공적으로 해제 될 수 있음)에 적합하기에 충분합니다.
  • 둘째, 첫 번째 단계가 성공하면 배열의 각 개체를 초기화하거나 구성합니다.

다른 의견에서 이미 언급했듯이 이러한 유형의 선언에는 미묘한 차이가 있지만 가장 일반적인 선언은 다음과 같습니다.

  1. 대부분의 최신 운영 체제에서 :

    • 자동 스토리지는 일반적으로 스택에 할당되며, 이는 일반적으로 LIFO 메커니즘을 사용하여 스레드 별 사전 할당 된 메모리 공간입니다.
    • 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 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.

참고URL : https://stackoverflow.com/questions/6159968/declaring-array-of-int

반응형