Namespaces
Variants

Array declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

배열 타입의 객체를 선언합니다.

목차

구문

배열 선언은 선언자 가 다음과 같은 형태를 갖는 모든 단순 선언입니다

noptr-declarator [ expr  (선택 사항) ] attr  (선택 사항)
noptr-declarator - 모든 유효한 declarator 이지만, 만약 * , & , 또는 && 로 시작하는 경우 괄호로 둘러싸여야 함 (그렇지 않으면 전체 선언자가 pointer declarator 또는 reference declarator 로 처리됨).
expr - integral constant expression (C++14 이전) converted constant expression of type std::size_t (C++14 이후) , 0보다 큰 값으로 평가됨
attr - (C++11 이후) attributes 목록

T a [ N ] ; 형태의 선언은 a T 타입의 객체 N 개가 연속적으로 할당된 배열 객체 로 선언합니다. 배열의 요소는 0 부터 …, N - 1 까지 번호가 매겨지며, 첨자 연산자 [] 를 사용하여 a [ 0 ] , …, a [ N - 1 ] 와 같이 접근할 수 있습니다.

배열은 모든 기본 타입 ( void 제외), 포인터 , 멤버 포인터 , 클래스 , 열거형 , 또는 알려진 경계를 가진 다른 배열(이 경우 배열을 다차원 배열이라고 함)로부터 생성될 수 있습니다. 다시 말해, 알려지지 않은 경계를 가진 배열 타입을 제외한 객체 타입만이 배열 타입의 요소 타입이 될 수 있습니다. 불완전한 요소 타입을 가진 배열 타입 또한 불완전한 타입입니다.

가능성 있는 제약 조건 (C++20부터) auto 지정자는 배열에 대한 포인터나 참조 선언에서 배열 요소 타입으로 사용될 수 있으며, 이는 초기화식 또는 함수 인수 (C++14부터) 에서 요소 타입을 추론합니다. 예를 들어 auto ( * p ) [ 42 ] = & a ; a int [ 42 ] 타입의 lvalue인 경우 유효합니다.

(C++11부터)

참조 배열이나 함수 배열은 존재하지 않습니다.

배열 타입에 cv-qualifiers 를 적용하면(typedef나 템플릿 타입 조작을 통해) 한정자가 요소 타입에 적용되지만, 요소가 cv-qualified 타입인 모든 배열 타입은 동일한 cv-qualification을 가진 것으로 간주됩니다.

// a와 b는 동일한 const-qualified 타입 "5개의 const char 배열"을 가짐
typedef const char CC;
CC a[5] = {};
typedef char CA[5];
const CA b = {};

new[]-표현식 과 함께 사용될 때, 배열의 크기는 0일 수 있습니다; 이러한 배열은 요소를 가지지 않습니다:

int* p = new int[0]; // p[0] 또는 *p에 접근하는 것은 정의되지 않은 동작입니다
delete[] p; // 여전히 정리 작업이 필요합니다

할당

배열 타입의 객체는 전체적으로 수정될 수 없습니다: 비록 그것들이 lvalues (예: 배열의 주소를 취할 수 있음)일지라도, 대입 연산자의 좌변에 나타날 수 없습니다:

int a[3] = {1, 2, 3}, b[3] = {4, 5, 6};
int (*p)[3] = &a; // 정상: a의 주소를 취할 수 있음
a = b;            // 오류: a는 배열임
struct { int c[3]; } s1, s2 = {3, 4, 5};
s1 = s2; // 정상: 암시적으로 정의된 복사 할당 연산자가
         // 배열 타입의 데이터 멤버를 할당할 수 있음

배열-포인터 변환

배열 타입의 좌측값과 우측값에서 포인터 타입의 우측값으로 암시적 변환 이 존재합니다: 이는 배열의 첫 번째 요소를 가리키는 포인터를 생성합니다. 이 변환은 배열이 예상되지 않지만 포인터가 예상되는 컨텍스트에서 배열이 나타날 때마다 사용됩니다:

#include <iostream>
#include <iterator>
#include <numeric>
void g(int (&a)[3])
{
    std::cout << a[0] << '\n';
}
void f(int* p)
{
    std::cout << *p << '\n';
}
int main()
{
    int a[3] = {1, 2, 3};
    int* p = a;
    std::cout << sizeof a << '\n'  // 배열의 크기를 출력
              << sizeof p << '\n'; // 포인터의 크기를 출력
    // 배열은 허용되지만 포인터는 허용되지 않는 경우, 배열만 사용 가능
    g(a); // 정상: 함수가 참조로 배열을 받음
//  g(p); // 오류
    for (int n : a)            // 정상: 범위 기반 for 루프에서 배열 사용 가능
        std::cout << n << ' '; // 배열의 요소들을 출력
//  for (int n : p)            // 오류
//      std::cout << n << ' ';
    std::iota(std::begin(a), std::end(a), 7); // 정상: begin과 end는 배열을 받음
//  std::iota(std::begin(p), std::end(p), 7); // 오류
    // 포인터는 허용되지만 배열은 허용되지 않는 경우, 둘 다 사용 가능:
    f(a); // 정상: 함수가 포인터를 받음
    f(p); // 정상: 함수가 포인터를 받음
    std::cout << *a << '\n' // 첫 번째 요소 출력
              << *p << '\n' // 동일
              << *(a + 1) << ' ' << a[1] << '\n'  // 두 번째 요소 출력
              << *(p + 1) << ' ' << p[1] << '\n'; // 동일
}

다차원 배열

배열의 원소 타입이 다른 배열일 때, 그 배열을 다차원 배열이라고 합니다:

// 2개의 배열로 이루어진 배열, 각 배열은 3개의 int를 가짐
int a[2][3] = {{1, 2, 3},  // 2 × 3 행렬로 볼 수 있음
               {4, 5, 6}}; // 행 우선 배치 방식

배열-포인터 변환(decay)이 적용될 때, 다차원 배열은 첫 번째 요소(예: 첫 번째 행이나 첫 번째 평면)에 대한 포인터로 변환된다는 점에 유의하십시오: 배열-포인터 변환은 한 번만 적용됩니다.

int a[2];            // 2개의 int를 가지는 배열
int* p1 = a;         // a는 a의 첫 번째 요소를 가리키는 포인터로 decay됨
int b[2][3];         // 3개의 int를 가지는 2개의 배열의 배열
// int** p2 = b;     // 오류: b는 int**로 decay되지 않음
int (*p2)[3] = b;    // b는 b의 첫 번째 3-요소 행을 가리키는 포인터로 decay됨
int c[2][3][4];      // 4개의 int를 가지는 3개의 배열의 2개의 배열의 배열
// int*** p3 = c;    // 오류: c는 int***로 decay되지 않음
int (*p3)[3][4] = c; // c는 c의 첫 번째 3 × 4-요소 평면을 가리키는 포인터로 decay됨

경계를 알 수 없는 배열

만약 expr 이 배열 선언에서 생략되면, 선언된 타입은 "T의 알려지지 않은 경계의 배열"이며, 이는 일종의 불완전 타입 입니다. 단, 집합체 초기화 와 함께 사용되는 선언에서는 예외입니다:

extern int x[];      // x의 타입은 "크기를 알 수 없는 int 배열"입니다
int a[] = {1, 2, 3}; // a의 타입은 "3개의 int 배열"입니다

배열 요소가 알려지지 않은 경계의 배열일 수 없기 때문에, 다차원 배열은 첫 번째 차원을 제외한 다른 차원에서 알려지지 않은 경계를 가질 수 없습니다:

extern int a[][2]; // 정상: 알 수 없는 경계를 가진 2개 int 배열의 배열
extern int b[2][]; // 오류: 배열이 불완전한 요소 타입을 가짐

동일한 범위 내에서 해당 개체의 선행 선언이 있고 그 범위에서 경계가 지정된 경우, 생략된 배열 경계는 이전 선언에서와 동일하게 간주되며, 이는 클래스의 정적 데이터 멤버 정의에도 유사하게 적용됩니다:

extern int x[10];
struct S
{
    static int y[10];
};
int x[];               // OK: 경계는 10
int S::y[];            // OK: 경계는 10
void f()
{
    extern int x[];
    int i = sizeof(x); // 오류: 불완전한 객체 타입
}

알려지지 않은 경계를 가진 배열에 대한 참조자와 포인터는 형성될 수 있으나, 초기화하거나 할당할 수 없습니다 (C++20 이전) 초기화하거나 할당할 수 있습니다 (C++20 이후) 알려진 경계를 가진 배열 및 배열 포인터로부터. C 프로그래밍 언어에서는 알려지지 않은 경계를 가진 배열에 대한 포인터가 알려진 경계를 가진 배열에 대한 포인터와 호환되므로, 양방향으로 변환 및 할당이 가능합니다.

extern int a1[];
int (&r1)[] = a1;  // 정상
int (*p1)[] = &a1; // 정상
int (*q)[2] = &a1; // 오류 (C에서는 정상)
int a2[] = {1, 2, 3};
int (&r2)[] = a2;  // 정상 (C++20부터)
int (*p2)[] = &a2; // 정상 (C++20부터)

알 수 없는 경계를 가진 배열에 대한 포인터는 포인터 산술 에 참여할 수 없으며 첨자 연산자 의 왼쪽에 사용될 수 없지만, 역참조는 가능합니다.

배열 rvalues

배열은 함수에서 값으로 반환될 수 없으며 대부분의 캐스트 표현식의 대상이 될 수 없지만, 배열 prvalue 는 타입 별칭을 사용하여 brace-initialized functional cast 를 통해 배열 임시 객체를 구성함으로써 형성될 수 있습니다.

클래스 prvalue와 마찬가지로, 배열 prvalue는 평가 시 temporary materialization 을 통해 xvalue로 변환됩니다.

(since C++17)

배열 xvalue 는 클래스 rvalue의 배열 멤버에 직접 접근하거나 std::move 를 사용하거나 rvalue 참조를 반환하는 다른 캐스트나 함수 호출을 통해 직접 형성될 수 있습니다.

#include <iostream>
#include <type_traits>
#include <utility>
void f(int (&&x)[2][3])
{
    std::cout << sizeof x << '\n';
}
struct X
{
    int i[2][3];
} x;
template<typename T>
using identity = T;
int main()
{
    std::cout << sizeof X().i << '\n';           // 배열의 크기
    f(X().i);                                    // 정상: xvalue에 바인딩
//  f(x.i);                                      // 오류: lvalue에 바인딩할 수 없음
    int a[2][3];
    f(std::move(a));                             // 정상: xvalue에 바인딩
    using arr_t = int[2][3];
    f(arr_t{});                                  // 정상: prvalue에 바인딩
    f(identity<int[][3]>{{1, 2, 3}, {4, 5, 6}}); // 정상: prvalue에 바인딩
}

출력:

24
24
24
24
24

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 393 C++98 알려지지 않은 경계를 가진 배열에 대한 포인터나 참조를
함수 매개변수로 사용할 수 없었음
허용됨
CWG 619 C++98 생략된 경우 배열의 경계를 이전 선언으로부터
추론할 수 없었음
추론 허용됨
CWG 2099 C++98 배열 정적 데이터 멤버의 경계는 초기화자가 제공되더라도
생략할 수 없었음
생략 허용됨
CWG 2397 C++11 auto 를 요소 타입으로 사용할 수 없었음 허용됨

참고 항목

C documentation for Array declaration