C++ - 배열 참조
배열 참조는 배열보다 안전한 속도 면에서 유리하다 !!!
int *a[1]; // (1)
int (*a)[1]; // (2)
int &a[1]; // (3)
int (&a)[1]; // (4)
이 4개의 차이점은 ?
(1)은 모두 알다시피 int의 포인터의 배열이다.
(2) int 배열의 포인터이다.
(3)은 참조의 배열로 볼 수 있다. 사실 컴파일 오류이다. 참조의 배열은 규격에서 금지.
(4) 이것이 본 글의 주제인 int의 배열 참조이다.
배열 참조는 그 말 대로이다.
배열과 배열에 대한 참조는 뭐가 다른가?
배열은 함수의 인수로 지정 되었을 때 실제로는 포인터를 지정한 것과 같은 것이다. 이때, 요소 수 정보는 사라진다. 한편 참조형은 포인터가 아니라서, 요수 수에 의미가 있다.
요소 수가 다르면 형태가 다르기 때문이다.
void foo(int a[100]);
void bar(int (&b)[100]);
여기에서 주목하는 것은 foo에서 요소 수 100이라는 정보는 무의미하다는 것이다.
foo는 실제로는 아래와 같은 뜻이다.
void foo(int *a);
그래서 요소 수를 생략해도 컴파일이 된다
void foo(int a[]);
요소 수에 의미가 없다는 것은 아래의 코드도 합법이다.
int c[1];
foo( c );
이것이 위험하다는 것은 알고 있을 것이다.
만약 foo를 다음과 같은 구현하면 어떤 일이 일어날지 아무도 모른다.
void foo(int a[100])
{
a[10] = 0; // 범위 외 접근은 미정의
}
다른 인수로서 요소 수를ㄹ 넘기는 함수도 많다.
그러나 이는 C 시대의 이야기이다.
C++에는 더 좋은 대안이 제공되고 있다.
int c[1];
bar( c );
이번에는 bar에 c를 넘겨 보면 이것은 컴파일 오류이다.
b는 int(&)[100]
타입이지 포인터가 아니다.
그래서 int[1]
의 암묵적 변환을 시도하지만 불가능하다.
int e[100];
int* d = e;
bar( d );
이것도 못한다. int*
에서 int(&)[100]
에 대한 암묵적 변환을 시도하지만 못한다.
int e[100];
bar( e );
컴파일가능한 건 이것 뿐이다.
앞에 이야기 한대로 배열의 참조는 요소 수에 의미가 있다. 요소 수가 다르면 형이 다른 것이다.
즉 실행 시 범위 체크는 불필요하다. 정적으로 해결할 수 있다.
다른 차이로서는 포인터가 아니기 때문 포인터 연산이 불가능하다.
void bar(int (&b)[100])
{
b++; // error
}
다음과 같게 하면 배열 타입만 받아 들이고, 요소 수를 정적으로 얻을 수 있다.
template< int i >
void baz(int (&a)[i]);
int c[1];
int e[100];
int* d = c;
baz( c ); // ok
baz( e ); // ok
baz( d ); // error
SFINAE와 조합하면 요소 수 100이하의 배열만 받아 들이는 등의 유연한 처리를 컴파일 시에 할 수 있다.
실행 시 요소 수 조사를 할 필요가 없으므로 속도적으로도 유리한 것이다.
무엇보다 조사를 잊어버리는 실수를 미연에 막을 수 있는 점에서 안전하다.
또한 타입 쓰기가 알기 어렵다면 typedef 하면 된다.
#include <iostream>
template<typename T, size_t size>
using array = T[size];
void foo( array<int,100>& a ){
a[0] = 1234;
}
template<typename T, size_t size>
constexpr size_t length( array<T,size>& )
{
return size;
}
int main(){
int a[100];
foo( a );
constexpr size_t length = length(a);
std::cout << length << std::endl;
std::cout << a[0] << std::endl;
}
본 기억이 있을 것이다.
std::array
는 이 연장선상에 있다.
T(&)[n]
을 이해하지 못하는 사람들을 위해서 일부러 준비해 준 멋진 타입이다.
모르는 사람은 std::array
를 사용하면 된다.
현실에서는 가변 배열을 사용하는 경우가 많을 것이다. 그러나 고정 길이 배열을 취급하는 경우는 적극적으로 참조를 써야 한다.
이 글은 2020-08-03에 작성되었습니다.