[Rookiss C++] 배열

황교선·2023년 3월 24일
0

cpp

목록 보기
13/19

저번에는 여러가지 자료형을 하나로 묶어서 하나의 자료형으로 만든 구조체를 배웠다. 그럼 하나의 자료형만으로 연속적인 형태로 쭉 이어진 자료형은 없을까? 구조체에서도 언급은 했지만 배열로 이를 할 수 있다.

배열 자체는 그렇게 어렵지 않지만, 배열을 무궁무진하게 활용하기 때문에 여러가지 연습문제를 통해 많이 익히는 것이 중요하다.

배열

동일한 자료형의 변수가 연이어 메모리에 저장될 수 있도록하는 집합체

자료형 배열이름[원소갯수];
int array[5]; // 5개의 정수형 원소를 갖는 array라는 이름의 배열
char array2[10]; // 10개의 문자형 원소를 갖는 array라는 이름의 배열
float array3[50]; // 50개의 실수형 원소를 갖는 array라는 이름의 배열
  • 배열의 길이를 선언할 때에는 반드시 상수 사용 (컴파일러에 따라 다르다고 듣긴했음)
  • 배열 요소의 인덱스는 언제나 0부터 시작
  • C++ 컴파일러는 배열의 길이를 신경 쓰지 않음

초기화

int numbers[5] = {}; // 모두 0으로 초기화
int numbers1[10] = { 1, 2, 3, 4, 5 }; // 나머지 값들은 0으로 초기화
int numbers2[] = { 1, 2, 3, 41, 11, 456, 756 }; // 데이터 갯수만큼의 크기에 해당하는 배열로 만듬

인덱스

배열의 각 원소에 접근하기 위해서는 인덱스를 사용하면 된다. 인덱스는 0부터 시작하여 배열의 크기의 - 1까지 사용할 수 있다.

int number[5] = { }; // 인덱스는 0 ~ 4, 현재 배열 원소들 값 : 0, 0, 0, 0, 0
number[0] = 1; // 0는 배열의 첫 번째 원소임. 현재 배열 원소들 값 : 1, 0, 0, 0, 0
number[2] = 10; // 2는 세 번째 원소임. 현재 배열 원소들 값 : 1, 0, 10, 0, 0
number[4] = 2; // 4는 배열의 5-1이므로 마지막인 다섯 번째 원소. 현재 배열 원소들 값 : 1, 0, 10, 0, 2

인덱스를 사용하지 않고 포인터를 이용하는 방법도 있다. 배열의 이름은 포인터

원소 순회

배열을 선언하고 사용할 때, 각각의 원소를 일일이 접근해서 사용해야한다면 일반 여러 개의 변수를 하나씩 선언해서 사용하는거랑 사실상 큰 차이가 없을 것이다. 하지만 for 문처럼 반복문을 사용하여 모든 원소, 특정 원소 등에 접근할 수 있게 만들 수 있다. 배열의 선언에서는 상수 밖에 사용할 수 없었지만, 선언이 되고 난 후 배열을 사용할 때에는 변수로 인덱스를 참조할 수 있다

int main()
{
    int numbers1[10] = { 1,2,3,4,5 }; // 1, 2, 3, 4, 5, 0, 0, 0, 0, 0

    for (int i = 0; i < 5; i++)
    {
        numbers1[i] = 0; // 한 번씩 순회하며 배열의 앞부분을 0, 0, 0, 0, 0으로 바꿈
    }
    // 배열의 모든 원소의 값 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}

현재 코드는 numbers1 이라는 크기가 10인 배열에 원소0부터 원소4까지의 원소 값을 차례대로 0으로 바꾸는 것이다. 단순하게 반복문을 사용하고 있지만, 조건문을 통하여 특정 원소만 값을 바꾸게도 가능하다.

다차원 배열

배열은 한 자료형의 여러 값을 한 번에 관리할 수 있지만, 그런 배열을 여러 개 관리하고 싶을 땐 어떻게 해야할까? 그럴 땐 다차원 배열을 이용하면 된다. 다차원 배열 중 주로 이차원 배열까지만 사용하며, 이차원 배열은 여러 개의 배열을 배열로 관리할 수 있게 된다.

자료형 배열이름[행갯수][열갯수];
int table[2][5] = { { 1, 1, 5, 2, 2 }, { 4, 2, 3, 4, 1 } }; // 2행 5열짜리 배열
// 입체적으료 표현하면 아래와 같음
// 1, 1, 5, 2, 2 
// 4, 2, 3, 4, 1
// 왼쪽 위부터 0행 0열이고 오른쪽 아래로 1행 4열 (== 1행 1열 ~ 2행 5열)

주석으로 입체적으로 표현하면이라고 했지만, 사실 다차원 배열도 메모리 공간 상에 올라갈 때는 순차적으로 일렬로 올라가므로 1차원 배열이랑 차이가 없다. 메모리 상에서는 차이가 없지만, 그래도 우리가 코드로 편하게 하기 위해서 차원을 사용하는 것이고, 1차원과 2차원을 큰 차이 없이 다루는 코드를 아래에 썼다.

int apartment[2][5] = { { 1, 1, 5, 2, 2 }, { 4, 2, 3, 4, 1 }, };
for (int floor = 0; floor < 2; floor++)
{
    for (int room = 0; room < 5; room++)
    {
        int num = apartment[floor][room];
        cout << num << " ";
    }
    cout << endl;
}

// 1차원으로도 똑같이 만들 수 있고, 어셈블리 상에서 차이가 없음
int apartment1D[10] = { 1, 1, 5, 2, 2, 4, 2, 3, 4, 1 };
for (int floor = 0; floor < 2; floor++)
{
    for (int room = 0; room < 5; room++)
    {
        int num = apartment1D[(floor * 5) + room];
        cout << num << " ";
    }
    cout << endl;
}

배열과 포인터

배열의 이름이 곧 배열의 시작 주소를 말한다고 했다. 그래서 아래와 같이 사용할 수 있다.

int* p;
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

// '배열의 이름'은 배열의 시작 주소값을 가리키는 자료형*로 변환 가능
p = arr;
// 자료형 1차원 배열과 자료형* 포인터는 완전히 호환됨
cout << p[0] << endl;
cout << arr[0] << endl;
cout << p[5] << endl;
cout << arr[5] << endl;

cout << *p << endl;
cout << *arr << endl;
cout << *(p+3) << endl;
cout << *(arr+3) << endl;

// 2차원 배열 vs 다중 포인터
int arr2[2][2] = { { 1, 2 }, { 3, 4 } };
int (*p2)[2] = arr2;
cout << (*p2)[0] << endl;
cout << (*p2)[1] << endl;
cout << (*(p2+1))[0] << endl;
cout << (*(p2+1))[1] << endl;

cout << p2[0][0] << endl;
cout << p2[0][1] << endl;
cout << p2[1][0] << endl;
cout << p2[1][1] << endl;

이처럼 포인터를 사용하여 배열에 접근할 때에는 접근하는 주소가 항상 유효한지 주의해야한다.

다중 포인터를 사용하는 다차원 배열 접근 방법

뭔가 이중 포인터를 사용하여 이차원 배열을 다룰 수 있을 것 같다는 말장난에서의 느낌이 온다. 하지만 이중 포인터를 가지고 이차원 배열의 주소를 가질 수 없다. 왜냐하면 이차원 배열의 주소 자체도 배열의 시작 주소이기 때문에 ‘주소를 담는 주소를 담는 변수’인 이중 포인터에 담을 수 없다. 주소를 담는 변수에 배열의 시작 주소를 담는 것이기에 그냥 포인터를 사용해야한다. 그럼 언제 다중 포인터를 다차원 배열에 접근하여 사용할 수 있는가? 정확히는 다차원 배열은 아니지만, 다차원 배열처럼 사용할 수 있는, 하지만 행마다는 연속적이지 않지만 열 자체는 연속적인 형태의 좀 특이한 형식의 구조를 사용할 수 있다. 말을 좀 복잡하게 했지만 힙 영역에 선언하는 변수들을 이용하는 것이기에 나중에 동적할당 부분에서 설명하도록 하겠다.

profile
성장과 성공, 그 사이 어딘가

0개의 댓글