C++에서 배열은 컴파일 시점에 크기가 결정되는 정적 자료 구조입니다. vector보다 유연성은 떨어지지만 일반적으로 더 높은 성능을 제공합니다.
1. 배열 선언
배열의 차원은 반드시 상수 표현식(constant expression)이어야 하며, 각 요소의 타입을 명시해야 합니다(auto 사용 불가). 배열은 객체여야 하며 참조(reference)일 수 없습니다.
1 int numbers[10];
2 int *pointerArray[size]; // size개의 정수 포인터 배열
3 string textArray[get_size()]; // get_size()가 상수 표현식을 반환할 경우 유효
2. 배열 초기화
int values1[3] = {0,1,2};
int values2[] = {0,1,2}; // 크기 자동으로 3으로 결정
int values3[5] = {0,1,2}; // 나머지 2개 요소는 0으로 자동 초기화
string names[3] = {"홍", "김"}; // 마지막 요소는 ""로 자동 초기화
3. 문자 배열
문자 배열은 끝에 '\0'이 추가되어 문자열의 종료를 표시합니다.
char text1[] = {'C', '+', '+'}; // 실제 길이는 4, 자동으로 '\0' 추가
char text2[6] = "홍길동"; // 오류: '\0'을 저장할 공간이 부족
4. 배열에 대한 포인터
int *ptrArray[10]; // 10개의 정수 포인터로 구성된 배열
int (*arrayPtr)[10] = &numbers; // 10개의 정수로 구성된 numbers 배열을 가리키는 포인터
int (&arrayRef)[10] = numbers; // 10개의 정수로 구성된 numbers 배열의 참조
int * (&pointerRef) [10] = ptrArray; // 10개의 정수 포인터 배열에 대한 참조
5. 배열 요소 참조
배열 요소는 첨자 연산자나 범위 기반 for 루프로 접근할 수 있습니다. 첨자 범위는 0부터 n-1까지이며(n은 배열 크기), 첨자 타입은 size_t여야 합니다(size_t는 cstddef 헤더에 정의된 부호 없는 정수 타입입니다).
모든 요소를 순회할 때는 범위 기반 for 루프가 더 좋은 선택입니다.
unsigned scores[11] = {};
for( auto score: scores)
cout<< score << " ";
cout<<endl;
6. 포인터와 배열의 관계
배열은 내부적으로 포인터를 사용하여 구현됩니다.
string words[] = {"하나", "둘", "셋"}; // words는 첫 번째 요소를 가리키는 포인터
7. begin, end 함수
포인터는 반복자(iterator)로 사용할 수 있지만, 배열은 클래스가 아니므로 멤버 함수 begin(), end()가 없습니다. 대신 라이브러리에서 begin(arr), end(arr) 함수를 제공합니다.
int *start = begin(arr), *end = end(arr);
while( start != end && *start >=0) // 정수 배열에서 첫 번째 음수 찾기
++start;
end(arr) 포인터 역참조나 증가는 수행할 수 없습니다(end(arr)은 배열 외부의 주소를 가리키기 때문입니다).
포인터는 반복자이므로 다른 반복자 연산도 가능합니다(정수 더하기, 포인터 간 뺄셈, 비교 등). 첨자 연산자는 포인터에도 사용할 수 있으며, 결과 요소가 배열 내에 있어야 합니다.
int data[] = {0,2,4,6,8};
int *p = & data[2]; // p는 요소 4를 가리킴
int value = p[-2]; // value = data[0] = 0
따라서 첨자는 음수일 수 있으며, 결과 요소가 배열 내에 있으면 됩니다.
8. C 스타일 문자열
C 스타일 문자열은 다음과 같이 표현됩니다:
char text[80] = "안녕하세요"; // 자동으로 '\0'으로 끝남, 즉 text[6]='\0'
기본적으로 '\0'이 문자열의 끝을 표시하며, 배열 크기는 문자열 길이+1 이상이어야 합니다.
라이브러리는 이러한 스타일의 문자열을 조작하는 함수를 제공합니다:
strlen(p) 문자열 p의 길이('\0' 제외)
strcmp(p1,p2) 두 문자열 비교: ① 길이 비교, ② 사전순 문자 비교
strcat(p1,p2) p2 내용을 p1 뒤에 추가하고 p1 반환
strcpy(p1, p2) p2 내용을 p1에 복사하고 p1 반환
이 함수들은 문자열 배열의 길이가 충분한지 보장하지 않으므로 프로그래머가 직접 확인해야 합니다. 따라서 오류가 발생하기 쉽습니다. C++는 vector와 string 타입 사용을 권장하지만, 기존 코드와 호환성을 위해 라이브러리는 이러한 오래된 객체를 처리하는 방법을 제공합니다.
const char * str = s.c_str(); // string 객체 s를 C 스타일 문자열로 변환
c_str()을 호출하면 C 스타일 문자열의 포인터를 얻을 수 있지만, 이후 string s가 변경되면 포인터가 유효하지 않을 수 있으므로 변경 전 내용을 복사해야 합니다.
9. 배열로 vector 초기화
int intArray[] = {0, 1, 2, 3, 4, 5};
vector<int> ivec( begin(intArray), end(intArray) ); // ivec가 intArray의 모든 요소로 초기화됨
vector<int> subVec( intArray+1, intArray+4 ); // subVec은 1,2,3,4로 초기화됨
10. 다차원 배열
C++에는 진정한 다차원 배열이 없으며, 다차원 배열은 기본 단위가 배열인 배열입니다.
int matrix[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};
int matrix[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; // 컴파일러가 자동으로 행 분할
int matrix[3][4] = {0,3,6,9}; // 첫 행은 0,3,6,9, 나머지 요소는 0으로 초기화
다차원 배열은 범위 기반 for 루프로 순회할 수 있습니다:
size_t count = 0;
for( auto &row : matrix)
for( auto &col : row){
col = count;
count++;
}
여기서 row와 col은 참조여야 합니다. 그렇지 않으면 matrix의 요소는 4요소 배열을 가리키는 포인터가 되고, row는 이러한 포인터가 되어 다음 단계의 요소가 없으므로 내부 루프에서 오류가 발생합니다.
따라서 다차원 배열을 범위 기반 for 루프로 순회할 때는 내부 루프를 포함한 모든 레벨의 루프 변수가 참조여야 합니다. 안전을 위해 모든 변수를 참조로 사용하는 것이 좋습니다.