Programing

배열 붕괴 란 무엇입니까?

lottogame 2020. 3. 1. 15:36
반응형

배열 붕괴 란 무엇입니까?


배열의 붕괴는 무엇입니까? 배열 포인터와 관련이 있습니까?


배열은 포인터로 "부패"한다고합니다. 로 선언 된 C ++ 배열은 int numbers [5]다시 지정할 수 없습니다 numbers = 0x5a5aff23. 즉 말할 수 없습니다 . 더 중요한 것은 붕괴라는 용어는 유형과 치수의 손실을 의미합니다. 차원 정보 (카운트 5)를 잃어 numbers붕괴되고 int*유형은 int [5]더 이상 없습니다 . 부패가 발생하지 않는 경우는 여기를 참조하십시오 .

값으로 배열을 전달하는 경우 실제로하는 것은 포인터를 복사하는 것입니다. 배열의 첫 번째 요소에 대한 포인터가 매개 변수에 복사됩니다 (이 유형은 배열 요소의 유형도 포인터 여야 함). 이것은 배열의 붕괴 특성으로 인해 작동합니다. 일단 소멸 sizeof되면 본질적으로 포인터가되기 때문에 더 이상 완전한 배열의 크기를 제공하지 않습니다. 그렇기 때문에 (다른 이유로) 참조 또는 포인터로 전달하는 것이 선호되는 이유입니다.

배열을 전달하는 세 가지 방법 1 :

void by_value(const T* array)   // const T array[] means the same
void by_pointer(const T (*array)[U])
void by_reference(const T (&array)[U])

마지막 두 개는 적절한 sizeof정보를 제공하지만 첫 번째 것은 배열 인수가 붕괴되어 매개 변수에 할당 된 이후로는 그렇지 않습니다.

1 컴파일 할 때 상수 U를 알아야합니다.


배열은 기본적으로 C / C ++의 포인터와 동일하지만 완전히 다릅니다. 배열을 변환하면 :

const int a[] = { 2, 3, 5, 7, 11 };

포인터로 (캐스팅하지 않고 작동하므로 경우에 따라 예기치 않게 발생할 수 있음) :

const int* p = a;

sizeof연산자가 배열의 요소를 계산 하는 능력을 상실합니다 .

assert( sizeof(p) != sizeof(a) );  // sizes are not equal

이 손실 능력을 "부패"라고합니다.

자세한 내용은 배열 붕괴에 대한기사를 확인하십시오 .


표준의 내용은 다음과 같습니다 (C99 6.3.2.1/3-기타 피연산자-L 값, 배열 및 함수 지정자).

sizeof 연산자 또는 단항 & 연산자의 피연산자이거나 배열을 초기화하는 데 사용되는 문자열 리터럴 인 경우를 제외하고 유형 ''array of type ''이있는 표현식은 ''pointer to type 유형의 표현식으로 변환됩니다. 배열 객체의 초기 요소를 가리키고 lvalue가 아닌 type ''입니다.

즉, 배열 이름이 식에 사용될 때마다 배열의 첫 번째 항목에 대한 포인터로 자동 변환됩니다.

함수 이름은 비슷한 방식으로 작동하지만 함수 포인터는 배열 이름을 포인터로 자동 변환하는 것만 큼 혼동을 일으키지 않도록 훨씬 덜 특수하게 사용됩니다.

C ++ 표준 (4.2 배열에서 포인터로의 변환)은 변환 요구 사항을 (강조 광산)으로 완화합니다.

"NT의 배열"또는 "T의 알려지지 않은 범위의 배열"유형의 lvalue 또는 rvalue는 "pointer to T"유형의 rvalue로 변환 될 있습니다.

따라서 변환은 C에서 거의 항상 수행되는 것처럼 수행 될 필요 가 없습니다 (이로 인해 함수 오버로드 또는 템플릿이 배열 유형에서 일치 할 수 있음).

이것이 C에서 함수 프로토 타입 / 정의에서 배열 매개 변수를 사용하지 않아야하는 이유입니다 (제 의견으로는 일반적인 동의가 있는지 확실하지 않습니다). 그것들은 혼란을 야기하고 어쨌든 허구입니다-포인터 매개 변수를 사용하면 혼란이 완전히 사라지지는 않지만 적어도 매개 변수 선언은 거짓말이 아닙니다.


"부패"는 배열 형식에서 포인터 형식으로 식을 암시 적으로 변환하는 것을 말합니다. 대부분의 컨텍스트에서 컴파일러는 배열 표현식을 볼 때 표현식 유형을 "T의 N- 요소 배열"에서 "포인터에서 T"로 변환하고 표현식의 값을 배열의 첫 번째 요소의 주소로 설정합니다. . 이 규칙의 예외는 배열이 sizeof또는 &연산자 의 피연산자 이거나 배열이 선언에서 초기화 자로 사용되는 문자열 리터럴 인 경우입니다.

다음 코드를 가정하십시오.

char a[80];
strcpy(a, "This is a test");

식은 a"문자의 80 요소 배열"형식이고 식 "테스트는"입니다 (문자의 16 요소 배열) (C에서는 문자열 리터럴은 const 문자의 배열 임). 그러나에 대한 호출에서 strcpy()expression은 피연산자 sizeof또는 모두 &이므로 피연산자 유형은 "포인터에서 문자로"암시 적으로 변환되며 값은 각 요소의 첫 번째 요소 주소로 설정됩니다. 무엇 strcpy()프로토 타입에서 볼 수 있듯이 수신하면, 배열,하지만 포인터되지 않습니다 :

char *strcpy(char *dest, const char *src);

이것은 배열 포인터와 동일하지 않습니다. 예를 들면 다음과 같습니다.

char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;

모두 ptr_to_first_element와 것은 ptr_to_array같은이 값을 ; a의 기본 주소 그러나 유형이 다르며 아래와 같이 다르게 취급됩니다.

a[i] == ptr_to_first_element[i] == (*ptr_to_array)[i] != *ptr_to_array[i] != ptr_to_array[i]

표현이 기억 a[i]으로 해석됩니다 *(a+i)(배열 형식이 포인터 형식으로 변환하는 경우에만 작동한다), 모두 있도록 a[i]하고 ptr_to_first_element[i]작업 같은. 식은 (*ptr_to_array)[i]로 해석됩니다 *(*a+i). 발현 *ptr_to_array[i]ptr_to_array[i]컴파일러 경고 또는 상황에 따라 오류가 발생할 수 있습니다; 평가할 것으로 예상되는 경우 분명히 잘못된 일을 수행합니다 a[i].

sizeof a == sizeof *ptr_to_array == 80

다시 말하지만 배열이 피연산자 인 sizeof경우 포인터 유형으로 변환되지 않습니다.

sizeof *ptr_to_first_element == sizeof (char) == 1
sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size
                                                  is on your platform

ptr_to_first_element char에 대한 간단한 포인터입니다.


C의 배열에는 값이 없습니다.

객체의 값이 예상되지만 객체가 배열 인 경우에는 첫 번째 요소의 주소가 type과 함께 사용됩니다 pointer to (type of array elements).

함수에서 모든 매개 변수는 값으로 전달됩니다 (배열도 예외는 아닙니다). 함수에서 배열을 전달하면 "포인터로 붕괴"(sic); 배열을 다른 것과 비교하면 다시 "포인터로 붕괴"(sic); ...

void foo(int arr[]);

함수 foo는 배열의 값을 기대합니다. 그러나 C에서는 배열에 가치가 없습니다! 따라서 foo배열의 첫 번째 요소의 주소를 대신 가져옵니다.

int arr[5];
int *ip = &(arr[1]);
if (arr == ip) { /* something; */ }

위의 비교에서 arr값이 없으므로 포인터가됩니다. int에 대한 포인터가됩니다. 해당 포인터를 변수와 비교할 수 있습니다 ip.

배열 인덱싱 구문에서 arr은 '포인터로 쇠퇴합니다'

arr[42];
/* same as *(arr + 42); */
/* same as *(&(arr[0]) + 42); */

배열이 포인터로 붕괴되지 않는 유일한 경우는 sizeof 연산자 또는 & 연산자 ( 'address of'연산자)의 피연산자이거나 문자 배열을 초기화하는 데 사용되는 문자열 리터럴 일 때입니다.


배열이 썩고 ;-)을 가리킬 때입니다.

실제로, 어딘가에 배열을 전달하려고하지만 포인터가 대신 전달되는 경우 (도대체 누가 당신을 위해 전체 배열을 전달할 것이기 때문에) 사람들은 가난한 배열이 포인터로 부패했다고 말합니다.


배열 소멸은 배열이 함수에 매개 변수로 전달 될 때 포인터와 동일하게 취급된다는 것을 의미합니다.

void do_something(int *array) {
  // We don't know how big array is here, because it's decayed to a pointer.
  printf("%i\n", sizeof(array));  // always prints 4 on a 32-bit machine
}

int main (int argc, char **argv) {
    int a[10];
    int b[20];
    int *c;
    printf("%zu\n", sizeof(a)); //prints 40 on a 32-bit machine
    printf("%zu\n", sizeof(b)); //prints 80 on a 32-bit machine
    printf("%zu\n", sizeof(c)); //prints 4 on a 32-bit machine
    do_something(a);
    do_something(b);
    do_something(c);
}

위의 두 가지 합병증이나 예외가 있습니다.

먼저 C 및 C ++에서 다차원 배열을 처리 할 때 첫 번째 차원 만 손실됩니다. 배열은 메모리에 연속적으로 배치되므로 컴파일러는 해당 메모리 블록에 대한 오프셋을 계산할 수있는 첫 번째 차원을 제외한 모든 것을 알아야합니다.

void do_something(int array[][10])
{
    // We don't know how big the first dimension is.
}

int main(int argc, char *argv[]) {
    int a[5][10];
    int b[20][10];
    do_something(a);
    do_something(b);
    return 0;
}

둘째, C ++에서는 템플릿을 사용하여 배열의 크기를 추론 할 수 있습니다. Microsoft는 strcpy_s같은 C ++ 버전의 Secure CRT 함수에 이것을 사용하며, 비슷한 트릭을 사용 하여 배열의 요소 수 를 안정적으로 얻을 수 있습니다.


tl; dr : 정의한 배열을 사용할 때 실제로 첫 번째 요소에 대한 포인터를 사용하게됩니다.

그러므로:

  • 글을 쓰면 arr[idx]정말 말하는 것 *(arr + idx)입니다.
  • 함수는 배열을 매개 변수로 사용하지 않으며 배열 매개 변수를 지정하더라도 포인터 만 사용합니다.

이 규칙에 대한 정렬 예외 :

  • 고정 길이 배열을의 함수에 전달할 수 있습니다 struct.
  • sizeof() 포인터의 크기가 아니라 배열이 차지하는 크기를 제공합니다.

참고 URL : https://stackoverflow.com/questions/1461432/what-is-array-decaying



반응형