C++

[C++11] std::array vs. C-array

카루-R 2020. 3. 21. 00:52
반응형

예...늦은 감이 없지 않아 있습니다만,
C++11에서 새로 추가된 컨테이너, std::array에 대해서 포스팅 해보려고 합니다.
기존 C-스타일 배열과 비교를 해 보겠습니다.

1. 배열생성

1부터 10까지 들어있는 배열을 생성하겠습니다.

#include <iostream>
#include <array>
#include <algorithm>
using namespace std;

...와 같은 헤더 혹은 uns는 생략하겠습니다.
이 모든 헤더파일이 import std.core; 하나면 끝

// [코드 1: 배열 생성]
// C++11이상에서 동작합니다.
int main() {
    int carray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    std::array<int, 10> stdarray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
}

하지만 우린 여기서 더 줄일 수 있죠.

// C에서는 대입연산자 넣어줘야 합니다!!
int carray[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// C++14까지는 <int, 10> 넣어줘야 합니다!!
array stdarray{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

저번에 deduction guide에 관해 글을 하나 썼습니다만...
array을 array로만 써도 되는 기적을 만들어줬죠.
배열 생성 자체는 std::array가 더 쉬워보입니다. []도 필요없고, 타입도 적을 필요가 없으니까요.
(단, 모든 원소의 자료형이 일치해야 합니다. 그렇지 않으면 타입 추론시 오류가 납니다.)

 

2. 부분 초기화

배열의 3, 7번째 요소만 9로 초기화 해 봅시다.

// [코드 2: 부분 초기화]

int main() {
    int carray[10]{0, 0, 9, 0, 0, 0, 9};
    array<int, 10> stdarr{0, 0, 9, 0, 0, 0, 9};
}

 초기화라고 언급하였으니 배열을 만든 후 값을 대입하는 경우는 생각하지 않았습니다.
C-Array의 경우 배열 크기만 써주어도 편하게 작성할 수 있는 반면,
std::array는 하나하나 원소를 다 써야 합니다. 뒤에 0만 있다면 그부분은 생략해도 되지만,
array가 아니라 array<int, 10>이라고 명시해야 합니다.

 

참고로 C++이 아니라 C언어에서는 아래와 같은 초기화도 지원합니다.

int carray[10] = { [2] = 9, [6] = 9 };

 

3. 배열의 순회

전체 배열의 요소를 순회해볼까요?

// [코드 3: 배열의 순회]
// C++17 이상에서 동작합니다. (<int, 10>을 명시할 경우 C++11부터)
int main() {
    int carray[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    array stdarr{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // carray를 stdarr로 바꿔도 잘 작동한다.
    for (int n : carray) 
        cout << n << endl;
    
    // stdarr을 carray로 바꾸면 성공적으로 오류가 난다.
    copy(stdarr.cbegin(), stdarr.cend(), ostream_iterator<int>(cout, "\n"));
}

마지막의 copy를 굳이 carray에 쓰고 싶다면

copy(begin(X), end(X), ostream_iterator<int>(cout, "\n"));

X자리에 carray를 넣으시면 됩니다. 물론 기존 STL, array도 begin()을 사용할 수 있습니다.
vector v; 라면 v.begin() == begin(v)와 같은데 이는 일관성을 위한 것입니다.
int arr[3]; arr.begin()은 안되지만 begin(arr)은 가능합니다.

 

4. 잘못된 접근

인덱스를 벗어나 봅시다.

// [코드 4: 잘못된 접근]
// C++17 이상에서 동작합니다. (<int, 10>을 명시할 경우 C++11부터)
int main() try {
    int carray[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    array<int, 10> stdarr{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
 
    [[maybe_unused]] auto n = carray[11];
    n = stdarr[11];
    n = stdarr.at(11);
}
catch (const out_of_range &exception) {
    cout << "Exception caugted: " << exception.what() << endl;
}

main()을 자세히 보세요. 뭔가 이상하지 않습니까?
int main() try { 라니?
이건 function-try-block이라고 하여 try-catch문 자체가 함수 몸체를 이룹니다.
각설하고, 프로그램의 흐름을 보면 n = stdarr.at(11)에서 예외가 발생합니다.
자기 자신의 크기를 알기 때문에 11번째 요소는 없다는 것을 알고 오류를 뱉은 것이지요.
역시 모던 C++입니다.


 C++11의 std::array는 C언어의 배열보다 안전합니다. 또한 사용하기도 편리하고, STL답게 기능도 많습니다.
혹시라도 지금 C언어의 배열을 쓰신다면, 다시 한번 생각해 보시기 바랍니다.
M$가 비주얼스튜디오에 괜히 scanf_s를 넣은 것이 아닙니다. 버퍼 오버플로우, 굉장히 심각한 문제인 것 아시죠? 그걸 막아주는게 STL의 래퍼 클래스입니다.
천천히 적응해 보세요. 정말 도저히 std::array가 나하고는 안맞다,
그때 바꾸셔도 충분 합니다. 우선 array가 있다는건 알아야 하나까요.

반응형