왜 배열의 차원이 그 유형의 일부입니까?


14

C ++ Primer 책을 읽는 동안 "어레이의 요소 수는 배열 유형의 일부입니다."라는 문장을 보았습니다. 그래서 다음 코드를 사용하여 찾고 싶었습니다.

#include<iostream>

int main()
{
    char Array1[]{'H', 'e', 'l', 'p'};
    char Array2[]{'P', 'l', 'e', 'a', 's', 'e'};

    std::cout<<typeid(Array1).name()<<std::endl;        //prints  A4_c
    std::cout<<typeid(Array2).name()<<std::endl;        //prints  A6_c

    return 0;
}

그리고 흥미롭게도 두 배열에서 typeid의 결과가 어떻게 든 다른 것으로 나타났습니다.

  • 무대 뒤에서 무슨 일이 일어나고 있습니까?
  • 배열에 크기가 포함 된 유형이 필요한 이유는 무엇입니까? 크기가 바뀌지 않아야합니까?
  • 이것이 배열 비교에 어떤 영향을 미칩니 까?

개념을 깊이 이해할 수 있기를 원합니다.


3
그것은 엄격 아니다 필요한 유형의 크기 정보를 포함하지만, 유용
byxor

배열에 대한 자습서는 (1)을 설명합니다. 배열을 비교하는 기본 방법이 없기 때문에 (3)의 의미가 무엇인지 잘 모르겠습니다.
HolyBlackCat

답변:


20

무대 뒤에서 무슨 일이 일어나고 있습니까?

동적으로 할당되지 않은 것은 정의에 따라 동종 요소 의 고정 크기 컨테이너입니다. N유형 의 요소 배열은 T연속 된 N유형 의 객체 시퀀스로 메모리에 배치됩니다 T.


배열에 크기가 포함 된 유형이 필요한 이유는 무엇입니까?

배열 유형에 크기가 포함되는 것이 "필수"라고는 생각하지 않습니다. 실제로 포인터를 사용하여 연속적인 T객체 시퀀스를 참조 할 수 있습니다 . 이러한 포인터는 배열에 대한 크기 정보를 잃게됩니다.

그러나 유용한 것입니다. 형식 안전성을 향상시키고 여러 가지 방법으로 사용할 수있는 컴파일 타임에 유용한 정보를 인코딩합니다. 예를 들어, 참조- 배열을 사용하여 다양한 크기의 배열 에 과부하

void foo(int(&array)[4]) { /* ... */ }
void foo(int(&array)[8]) { /* ... */ }

또는 배열의 크기를 상수 표현식으로 계산

template <typename T, std::size_t N>
constexpr auto sizeOf(const T(&array)[N]) { return N; }

이것이 배열 비교에 어떤 영향을 미칩니 까?

실제로는 그렇지 않습니다.

두 숫자 (예 : int객체)를 비교하는 것과 같은 방식으로 C 스타일 배열을 비교할 수 없습니다 . 어휘 비교를 작성하고 크기가 다른 콜렉션의 의미를 결정해야합니다. std::vector<T>를 제공 하고 동일한 논리를 배열에 적용 할 수 있습니다.


보너스 : C ++ 11 이상은 std::array컨테이너와 같은 인터페이스를 가진 C 스타일 배열을 감싸는 래퍼입니다. C 스타일 배열은 다른 컨테이너 (예 :)와보다 일관성 이 있으며 즉시 사전 식 비교를std::vector<T> 지원하므로 C 스타일 배열보다 선호되어야합니다 .


2
"어떤 종류의 사전 비교를 작성하고 다른 크기의 컬렉션에 대한 의미를 결정해야합니다." 당신은 사용할 수 있습니다 std::equal(경유 std::begin하고 std::end있는 배열에 정의 된). 이 경우 다른 크기의 배열이 동일하지 않습니다.
Stu

3
범위가 배열 인 범위의 for 루프는 컴파일 타임에 배열의 크기를 읽어야한다는 점에 주목할 가치가 있습니다. 그러나 크기를 기준으로 오버로드하는 것보다 훨씬 많이 나타납니다.
Milo Brandt

8

객체를 생성 할 때 객체에 할당되는 공간의 양은 전적으로 유형에 따라 다릅니다. 내가 말하고있는 할당은 newor의 할당이 아니라 할당 된 malloc공간이므로 생성자를 실행하고 객체를 초기화 할 수 있습니다.

(예를 들어)로 정의 된 구조체가있는 경우

struct A { char a, b; }; //sizeof(A) == 2, ie an A needs 2 bytes of space

그런 다음 객체를 만들 때 :

A a{'a', 'b'};

객체를 구성하는 프로세스를 프로세스로 생각할 수 있습니다.

  • 스택에 2 바이트의 공간을 할당하십시오 (이 예제에서는 중요하지 않은 곳).
  • 객체 생성자 실행 (이 경우 복사 'a''b'객체)

필요한 2 바이트의 공간은 전적으로 객체의 유형에 따라 결정되며 함수의 인수는 중요하지 않습니다. 따라서 배열의 프로세스는 동일하지만 필요한 공간의 양은 배열의 요소 수에 따라 다릅니다.

char a[] = {'a'}; //need space for 1 element
char b[] = {'a', 'b', 'c', 'd', 'e'}; //need space for 5 elements

의 종류에 따라서 ab사실을 반영해야 a한 문자에 대한 충분한 공간을 필요로하고 b5 개 문자에 충분한 공간을 필요로합니다. 즉, 이러한 배열의 크기는 갑자기 변경 될 수 없으며 5 요소 배열이 작성되면 항상 5 요소 배열입니다. 크기가 다를 수있는 "배열"과 유사한 객체를 가지려면 동적 메모리 할당이 필요합니다.


0

런타임 라이브러리의 내부 이유입니다. 예를 들어 다음과 같은 내용을 고려하면

unsigned int i;
unsigned int *iPtr;
unsigned int *iPtrArr[2];
unsigned int **iPtrHandle;

그런 다음 문제가 무엇인지 분명해진다 : 예를 들어, 주소 지정 unsigned int *했습니다 우려 자체로 sizeof operator또는 주소 unsigned int.

여기에 나오는 나머지 부분에 대한 자세한 설명이 있지만 선언 된 유형의 일반 언어 텍스트를 인쇄하는 프로그램에 관한 Kernighan 및 Ritchie 의 C Programming Language, 2nd Edition 에서 다룬 내용을 요약 한 것 입니다. 끈.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.