본문 바로가기

std::array 소개 본문

💘 C++/행렬, 문자열, 포인터, 참조

std::array 소개

Hyonii 2022. 7. 23. 13:00

std::array

이전 포스트들에서 고정 배열(fixed array)과 동적 배열(dynamic array)을 배웠다.

두 가지 배열 모두 C++에 내장되어 있지만,

포인터로 형 변환되었을 시 배열 길이 정보가 손실되고,

동적 배열은 지저분한 할당 해제 문제가 있다.

 

이러한 문제를 해결하기 위해 C++ 표준 라이브러리는 배열 관리를 쉽게 해주는 std::array와 std::vector가 있다.


An introduction to std::array in C++11

C++ 11에서 소개된 std::array는 함수에 전달할 때 포인터로 형 변환되지 않는 고정 길이 배열이다.

std::array는 <array>헤더의 std namespace 내부에 정의되어 있다.

 

array를 선언할 때

 

이런식으로 선언했었다.

std::array 변수 선언은 쉽다.

#include <array>

std::array<int,5>myArray; // declare an integer array with length 5

 

둘이 똑같이 작동한다.

둘 다 const 붙일 수 있고, using namespace std 쓰면 std도 생략할 수 있다.

 

위에 주석처리한 이유는 겹치는 이름 있으면 오류가 생겨서 그렇다.

array 같은 일반적인 단어는 변수명으로 쓰면 충돌이 생길 수 있다.

 

std::array는 참 편한데

단점이 고정 배열 선언처럼 array의 길이는 컴파일 타임에 설정해야 한다.

 

여기에 숫자가 꼭 들어가야 한다.

그냥 내장된 고정 array는

 

이렇게 해도 알아서 컴파일러가 다섯개라고 해줬는데..

std::array는 안먹힌다.

그 외에 array로 할 수 있는 거는 다 된다고 보면 된다.

 

std::array는 초기화 리스트(initializer list) 또는 유니폼 초기화(uniform initialization)를 사용해서 초기화할 수 있다.

std::array<int, 5> myArray = {9, 7, 5, 3, 1}; //initialization list
std::array<int, 5> myArray2 {9, 7, 5, 3, 1};  //uniform initialization

또한 초기화 리스트를 사용해서 배열에 값을 할당할 수 있다.

std::array<int, 5> myArray;
myArray = {0, 1, 2, 3, 4};    // okay
myArray = {9, 8, 7};          // okay, elements 3 and 4 are set to 0!
myArray = {0, 1, 2, 3, 4, 5}; // now allowed, too many elements in initializer list!

이런 식으로 대입할 수 있는데 element 개수가 적어준 것보다 많으면 오류 난다.

 

하지만 적을 때는 상관없다.

나머지는 0으로 채워준다.


cout도 당연히 같은 방식으로 작동한다.

일반 배열처럼 첨자 연산자[ ] 를 사용해서 배열의 요소 값에 접근할 수 있다.

 

이렇게 찍을 수 있고

std::array는 찍을 수 있는 방법이 한 가지 더 있다.

 

. 을 찍고 at을 쓰고 (0) 하면 위와 똑같이 작동한다.

대괄호[ ] 아니고 괄호 ( )인 거 주의하자!

at은 멤버 함수 이름이다. 그래서 뒤에 괄호가 와야 한다.

 

일반 배열 array에서 사용하던 대괄호[ ] 방식하고 .at( )의 차이점은 무엇일까?

위에 대괄호는 10이

 

여기가 5니까

 

이 자리에 0,1,2,3,4 까지만 들어갈 수 있는데 

이게 넘는지 안 넘는지 유효 범위 검사도 안 하고 바로 무조건 access 해버린다.

잘못된 index가 들어가면 나쁜 일이 발생한다.

 

그런데 .at을 사용하면 미리 한번 체크를 해보고 문제가 생기면 예외처리를 발동시킨다.

일단 여기서는 중간에 유효 범위 검사를 하니까 아무래도 속도가 조금 더 느리다.

퍼포먼스가 아주 중요한 프로그램을 만들 때는 위에 거를 쓰는 게 낫다.

 


Size

그리고 몇 가지 편리한 기능들이 있다.

size() 함수를 사용해서 배열의 길이를 알 수 있다.

 

element가 몇 개인지 체크할 수 있다. 편하다
잘 나온다.

함수 파라미터로 보낼 때에도 원래 성질이 그대로 유지가 된다.

 

함수 파라미터로 넣을 때 파라미터 길이가 조금 길다는 단점이 있다.

std::array는 함수에 전달될 때 포인터로 형 변환되지 않기 때문에

size() 함수는 함수 내에서 호출하더라도 정상적으로 작동한다.

 

여기서 주의해야 할 점이 두 가지 있다.

 

첫 번째,

표준 라이브러리에서는 "size"라는 용어는 배열 길이를 의미하므로

sizeof() 연산자의 결과와 혼동하면 안 된다.

(sizeof() 결과: 배열 요소 자료형의 크기 * 배열 길이)

 

이렇게 집어넣으면 아무 문제 없기 대문에 아주 좋다고 생각할 수 있다.

두 번째,

일반 변수도 파라미터로 집어넣으면 한번 복사가 된다고 말씀드린 적 있다. array도 복사가 된다.

그래서 array가 많이 클 경우에는 복사하는데 시간이 걸린다.

 

그럴 경우에는 포인터로 보내는 방법도 있고 레퍼런스를 쓰는 방법도 있는데

위 예제에서는 성능상의 이유로 레퍼런스를 쓰면 참 편하다.

std::array가 함수로 전달될 때 컴파일러가 배열의 복사본을 만드는 것을 방지하기 위해서다.

std::array는 항상 참조로 전달하자.

 

그리고 my_arr가 함수 안에서 변하지 않기를 원한다면 const로 수정을 막아버릴 수 있다.

 

그래서 array나 vector 같은 표준 라이브러리 사용할 때는

이렇게 함수 파라미터로 넣을 때

레퍼런스를 쓸 것인지, 포인터를 쓸 것인지, const를 붙일 건지 아니면 그냥 넣을 것인지 잘 생각해보고 넣어야 한다.

 


std::array의 길이는 항상 알려져 있기 때문에 for-each 루프로 사용할 수 있다.

 

잘 나오고 있다.

이 때도 마찬가지로 레퍼런스를 달아줄 수 있다.

 

이렇게 하면 중간에 복사되는 과정이 생략되어서 빨라진다.

 


sorting

sort(정렬)가 알고리즘에 들어있다.

 

알고리즘 include해줘야 한다.

<algorithm> 헤더에 있는 std::sort를 사용해서 std::array를 정렬할 수 있다.

 

.begin , .end는 처음부터 끝까지 sorting 하려고 써준 거다.

물론 원하시다면 sorting을 중간까지만 할 수도 있다.

 

이전 포스팅에서 selection sort 한번 직접 구현해보시라고 말씀드린 적 있다.

 

배열과 선택 정렬 Selection Sort

배열과 선택 정렬 Selection Sort 맛있는 음식이 여러가지 있을 때 어느 것을 먼저 먹을지 순서를 정하기도 하고, 사람이 여러명 있을 때 키 순서대로 세워보기도 한다. 이렇게 순서를 맞춰주는

hyoniidaaa.tistory.com

실전에서는 직접 구현한 것을 사용할 일이 많지는 않지만, 한 번씩 해보면 공부에 도움이 많이 된다.

 

실제로는 이런식으로 아주 편리하게 사용할 수 있는 라이브러리들이 많다.

 

역순 정렬은 어떻게 하면 될까?

 

앞에 r만 붙여주면 된다.
역순으로도 잘 정렬된다.


std::array에 대해서 알아보았다.

std::array는 내장된 고정 array와 용법은 거의 비슷하다.

그러나 내장된 고정 배열보다 메모리를 더 사용하지 않으며,

함수로 전달 시 포인터로 형 변환되지 않는 장점이 있다.

단점은 선언할 때 배열 길이를 명시적으로 지정해야 한다는 것이 있다.

std::array는 여러 가지 면에서 편리하게 사용할 수 있다.

Comments