1. 정의

1) 같은 자료형의 변수 여러 개를 연속으로 늘어놓은 자료구조

2. 특징

1) 배열의 크기는 선언 시 고정: 메모리 상에서 연속된 주소에 위치

#include <iostream>

using namespace std;

int main()
{
    int arr[5] = {};
}

 

2) 배열 인덱스의 시작은 0

: 배열의 첫 번째 인자 = 0

: 배열의 마지막 번째 인자 = 선언된 인자의 개수 - 1

 

3) 배열의 원소에 대해 초기화필요

* 초기화를 하는 이유: 초기화를 하지 않으면 처음에 쓰레기 값이 들어감

   ex) 5개의 원소를 가지는 배열에서 초기화를 하지 않은 경우

#include <iostream>

using namespace std;

int main()
{
    int arr[5];

    return 0;
}

 

(1) 각 원소를 0으로 초기화 하는 방법

#include <iostream>

using namespace std;

int main()
{
    int arr[5] = {};
}

 

(2) 특정 인덱스를 특정 값으로, 나머지 원소는 0으로 초기화

     ex) 첫 번째 원소를 9, 두 번째 원소를 8, 나머지 원소를 0으로 초기화

#include <iostream>

using namespace std;

int main()
{
    int arr[5] = {9, 8, };

    return 0;
}

 

(3) 배열의 각 원소를 각각 다른 특정 값으로 초기화

     ex) 첫 번째 원소부터 2, 15, -10, 99, -300 으로 초기화

#include <iostream>

using namespace std;

int main()
{
    int arr[5] = {};
    arr[0] = 2;
    arr[1] = 15;
    arr[2] = -10;
    arr[3] = 99;
    arr[4] = -300;

    return 0;
}

 

(4) 배열의 원소를 한 번에 초기화

#include <iostream>

using namespace std;

int main()
{
    int arr[5] = {1, 2, 3, 4, 5};

    return 0;
}

※주의! 선언된 원소의 개수보다 초과할 경우 크래시 발생

    ex) 5개까지 넣을 수 있는 배열에 5번째 원소를 넣었을 경우

#include <iostream>

using namespace std;

int main()
{
    int arr[5] = {1, 2, 3, 4, 5, 6};

    return 0;
}

 

4) 배열의 크기 = (자료형의 크기 * 원소의 개수)

    ex) 배열의 크기가 5로 동일하지만 자료형은 다른 배열의 크기

#include <iostream>

using namespace std;

int main()
{
    int arr[5] = {1, 2, 3, 4, 5};
    double arr1[5] = { 1.2, 2.48, -0.123, 1.548, -123.456 };
    float arr2[5] = { 1.3432, -123.3423, 23452.123, 0.0012, 0.99999 };
    char arr3[5] = { '가', 'A', 'C', 'あ', '段' };
    string arr4[5] = {"안녕", "Hello", "こんにちは", "你好", "Hola"};
    
    cout << "int형 사이즈   : " << sizeof(int) << "      / int형 배열의 사이즈   : " << sizeof(arr) << endl;
    cout << "double형 사이즈: " << sizeof(double) << "     / double형 배열의 사이즈 : " << sizeof(arr1) << endl;
    cout << "float형 사이즈 : " << sizeof(float) << "    / float형 배열의 사이즈   : " << sizeof(arr2) << endl;
    cout << "char형 사이즈  : " << sizeof(char) << "   / char형 배열의 사이즈     : " << sizeof(arr3) << endl;
    cout << "string형 사이즈: " << sizeof(string) << " / string형 배열의 사이즈    : " << sizeof(arr4) << endl;

    return 0;
}


3. 배열 원소 출력

1) for문을 이용해 배열의 값 출력 가능

#include <iostream>

using namespace std;

int main()
{
    int arr[5] = {1, 2, 3, 4, 5};

    for (int i = 0; i < 5; i++)
    {
        cout << "배열의 " << i << "번째 값: " << arr[i] << endl;
    }

    return 0;
}


4. 배열과 포인터

1) 배열 이름 = 첫 번째 원소에 대한 주소 값

: 배열의 이름을 출력하면 첫 번째 원소에 대한 주소 값과 동일함

  (= 배열의 이름을 출력하면 배열의 시작 주소를 알 수 있음)

#include <iostream>

using namespace std;

int main()
{
    int arr[5] = {1, 2, 3, 4, 5};

    cout << "배열의 이름:                       " << arr << endl;
    cout << "배열의 첫 번째 원소에 대한 주소값: " << &arr[0] << endl;
    cout << "배열의 두 번째 원소에 대한 주소값: " << &arr[1] << endl;

    return 0;
}

 

2) 배열 원소의 주소 값 = 배열 원소의 포인터에 대한 주소 값

#include <iostream>

using namespace std;

int main()
{
    int arr[5] = {1, 2, 3, 4, 5};
    int* PtrArr = arr;

    int NextPtr = 0;

    for (int i = 0; i < 5; i++)
    {
        cout << "배열의 " << i << "번째에 대한 원소 주소  : " << &arr[i] << endl;
        cout << "배열의 " << i << "번째에 대한 포인터 주소: " << PtrArr << endl;

        NextPtr += *PtrArr;
        PtrArr++;
        cout << "---------------------------------------------------" << endl;
    }

    return 0;
}

 

3) 배열 각 원소의 크기와 포인터의 크기는 다름

: 배열 각각의 원소에 대해 원소의 주소와 포인터 주소는 같지만

  포인터의 크기는 시스템에 따라 8byte 혹은 4byte로 정해져 있고, 배열 원소의 크기는 자료형에 따라 달라짐

   ex) int형 배열일 때와 double형 배열 각각에 대한 첫 번째 원소의 값과 주소값, 사이즈 비교

#include <iostream>

using namespace std;

int main()
{
    int arr[5] = {1, 2, 3, 4, 5};
    int* iPtrArr = arr;

    cout << "배열의 첫 번째 원소 값: " << arr[0] << "           / 배열의 첫 번째 원소에 대한 주소값: " << &arr[0] << endl;
    cout << "배열의 첫 번째 원소의 포인터 값: " << *iPtrArr << " / 배열의 첫 번째 원소의 포인터의 주소 값: " << iPtrArr << endl;
    cout << "-----------------------------------------------------------------------------------------------" << endl;
    cout << "배열의 첫 번째 원소에 대한 사이즈 : " << sizeof(arr[0]) << endl;
    cout << "배열의 첫 번째 원소의 포인터에 대한 사이즈: " << sizeof(iPtrArr) << endl << endl;

    double arr1[5] = { 1.2, 2.48, -0.123, 1.548, -123.456 };
    double* dPtrArr = arr1;

    cout << "배열의 첫 번째 원소 값: " << arr1[0] << "           / 배열의 첫 번째 원소에 대한 주소값: " << &arr1[0] << endl;
    cout << "배열의 첫 번째 원소의 포인터 값: " << *dPtrArr << " / 배열의 첫 번째 원소의 포인터의 주소 값: " << dPtrArr << endl;
    cout << "------------------------------------------------------------------------------------------------" << endl;
    cout << "배열의 첫 번째 원소에 대한 사이즈 : " << sizeof(arr1[0]) << endl;
    cout << "배열의 첫 번째 원소의 포인터에 대한 사이즈: " << sizeof(dPtrArr) << endl << endl;

    return 0;
}


5. 2차원 배열

1) 정의

- 배열의 배열: 2행 2열로 구성된 행렬 형태로 구성

 

2) 선언과 초기화

자료형 배열명[행의 크기][열의 크기] 
= {
    {원소1-1, 원소1-2, ...}, // 행1
    {원소2-1, 원소2-1, ...}, // 행2
    ...
  };

 

3) 특징

(1) 배열의 크기 고정적

     : 배열 선언 시 행과 열을 지정하기 때문에 프로그램 실행 동안 변경 불가능

     : 컴파일 시간에 메모리가 미리 배열의 크기만큼 할당됨

 

(2) 이차원 배열이기 때문에 두 개의 인덱스로 접근 가능

     ex) 인덱스를 사용해 특정 값에 임의 접근 가능

#include <iostream>

using namespace std;

int main()
{
    int arr[3][2] = {
        {1, 2},
        {3, 4},
        {5, 6}
    };

    cout << "2행 2열에 있는 값: " << arr[1][1] << endl;
}

더보기

* 임의 접근 가능

- 임의의 위치에 있는 요소를 직접 접근 가능

- 시간 복잡도: O(1)

- 연속적인 데이터 구조에서 사용 가능

: 연속적이지 않은 데이터 구조(ex. linked list) 에서는 첫 번째 원소부터 순차적으로 탐색해야하기에 임의 접근 불가

  → 시간 복잡도: O(n)

 

(3) 이차원 배열도 배열이기 때문에 메모리 상에 연속적으로 저장: 행 우선 방식(Row-major order)으로 메모리에 저장

     ex) 3행 2열의 2차원 배열

#include <iostream>

using namespace std;

int main()
{
    int arr[3][2] = {
        {1,2},
        {3,4},
        {5,6}
    };

    return 0;
}

 

(4) 메모리 상에서는 1차원 배열로 취급되기 때문에 배열의 이름은 1행 1열의 시작 주소를 가리킴

     ex) 3행 2열 배열에서 배열의 이름과 1행 1열, 1행 2열의 주소 값 비교

#include <iostream>

using namespace std;

int main()
{
    int arr[3][2] =
    {
        {1, 2},
        {3, 2},
        {5, 3}
    };

    cout << "배열의 이름      : " << arr << endl;
    cout << "1행 1열의 주소 값: " << &arr[0][0] << endl;
    cout << "1행 2열의 주소 값: " << &arr[0][1] << endl;
}

 

4) 이차원 배열과 포인터

(1) 포인터를 이용해 이차원 배열의 원소에 접근

    ex) 3행 4열의 이차원 배열에서 포인터를 이용해 각 원소에 접근하기

#include <iostream>

using namespace std;

int main()
{
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    cout << "포인터를 이용해 1행 1열 값에 접근하기: " << *(*arr) << endl;
    cout << "포인터를 이용해 1행 2열 값에 접근하기: " << *(*(arr) + 1) << endl;
    cout << "포인터를 이용해 1행 3열 값에 접근하기: " << *(*(arr) + 2) << endl;
    cout << "포인터를 이용해 1행 4열 값에 접근하기: " << *(*(arr) + 3) << endl;
    cout << "포인터를 이용해 2행 1열 값에 접근하기: " << *(*(arr + 1)) << endl;
    cout << "포인터를 이용해 2행 2열 값에 접근하기: " << *(*(arr + 1) + 1) << endl;
    cout << "포인터를 이용해 2행 3열 값에 접근하기: " << *(*(arr + 1) + 2) << endl;
    cout << "포인터를 이용해 2행 4열 값에 접근하기: " << *(*(arr + 1) + 3) << endl;
    cout << "포인터를 이용해 3행 1열 값에 접근하기: " << *(*(arr + 2)) << endl;
    cout << "포인터를 이용해 3행 2열 값에 접근하기: " << *(*(arr + 2) + 1) << endl;
    cout << "포인터를 이용해 3행 3열 값에 접근하기: " << *(*(arr + 2) + 2) << endl;
    cout << "포인터를 이용해 3행 4열 값에 접근하기: " << *(*(arr + 2) + 3) << endl;
    
    return 0;
}

- *arr이 첫 번째 원소의 값이므로 0 이라 생각하면 됨

→ *arr = arr[0][0]의 값 = 1행 1열의 값

→ *(arr) + 1 = arr[0][0+1]의 값 = 1행 2열의 값

→ *(arr + 1) + 1 = arr[0 + 1][0+1]의 값 = 2행 2열의 값

 

(2) 이중포인터를 사용해 동적으로 배열의 크기를 부여: 배열의 크기를 런타임에 결정

  ex1) 3행 4열의 이차원 배열에 대해 동적으로 부여

#include <iostream>

using namespace std;

int main()
{
    // 행의 개수 설정: 3행
    int** arr = new int* [3];

    for (int i = 0; i < 3; i++)
    {
        // 행의 개수만큼 돌면서 열의 개수 설정: 4열
        arr[i] = new int[4];
    }
    
    return 0;
}

 

  ex2) 이중포인터로 동적 생성한 3행 4열의 이차원 배열에 대해 주소값 출력

#include <iostream>

using namespace std;

int main()
{
    // 행의 개수 설정: 3행
    int** arr = new int* [3];

    for (int i = 0; i < 3; i++)
    {
        // 행의 개수만큼 돌면서 열의 개수 설정: 4열
        arr[i] = new int[4];
    }

    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            cout << i + 1 <<"행의 "<<j + 1 <<"열에 대한 값: " << &arr[i][j] << endl;
        }
        cout << endl;
    }
    
    return 0;
}


6. 문자열 배열

1) 정의

(1) 문자열로 이루어진 배열

 

2) 특징

(1) 문자열은 끝에 null문자(0값)이 들어있음: null 문자가 배열의 끝을 알려줌

#include <iostream>

using namespace std;

int main()
{
    char charArr[] = { "HELLO" };
    return 0;
}

 

(2) 배열의 길이: 실제 작성한 길이 + 1(null문자)

#include <iostream>

using namespace std;

int main()
{
    char charArr[] = { "HELLO" };

    cout << "문자열 배열의 길이: " << sizeof(charArr) << endl;
    cout << "문자열의 길이     : " << strlen(charArr) << endl;
    
    return 0;
}

cf) 한 글자 한 글자 따로 작성한 char형 배열은 배열의 길이가 정상적으로 나옴

#include <iostream>

using namespace std;

int main()
{
    char charArr[] = { 'H', 'E', 'L', 'L', 'O'};

    cout << "배열의 길이 값  : " << sizeof(charArr) << endl;
}

 

3) 문자열 배열과 포인터

(1) 포인터를 통한 문자열 변경

#include <iostream>

using namespace std;

int main()
{
    char charArr[] = { 'H', 'E', 'L', 'L', 'O'};

    cout << "원래의 값: " << charArr << endl;

    char* ptrString = charArr;

    *ptrString = 'K';
    *(ptrString + 1) = 'm';
    
    return 0;
}

- *ptrString: 첫 번째 포인터의 값 변경 = H → K로 변경

- *ptrString + 1: 첫 번째 포인터의 다음 값 변경 = E → m 으로 변경


7. 배열과 메모리 해제

1) 정적 배열(미리 사이즈를 정해둔 배열)의 경우

- 컴파일 타임에 크기가 결정되고 스택 메모리에 할당되기 때문에 프로그램이 끝날 때 자동으로 해제됨

  → 메모리 해제를 해줄 필요가 없음

 

2) 동적 배열(사이즈를 미리 정하지 않은 배열)의 경우

- new 연산자를 통해 힙 메모리에 할당되기 때문에 delete[]를 사용해 메모리 해제 필요

* 힙메모리: heap space 참조

: 메모리를 해제하지 않으면 메모리 누수 발생

 

3) 메모리 해제 방법

(1) 1차원 배열의 경우: delete[] 배열명;

#include <iostream>

using namespace std;

int main()
{
    int* arr = new int[3];

    for (int i = 0; i < 3; i++)
    {
        arr[i] = i + 1;

        cout << i << "번째 원소: " << arr[i] << endl;
    }

    delete[] arr;
    
    return 0;
}

 

(2) 2차원 배열의 경우: for문을 이용해 각 행과 배열 자체의 메모리 해제

#include <iostream>

using namespace std;

int main()
{
    int** arr = new int* [3]; // 3행을 동적으로 생성

    for (int i = 0; i < 3; i++)
    {
        arr[i] = new int[4]; // 4열을 동적으로 생성
    }

    for (int i = 0; i < 3; i++) // 동적으로 생성한
    {
        delete[] arr[i];       // 각 행에 대해 메모리 해제
    }

    delete[] arr;            // 배열 자체의 메모리 해제
    
    return 0;
}

 

728x90

'C++📓 > 이론' 카테고리의 다른 글

클래스: 객체와 클래스  (5) 2024.10.14
구조체 포인터(Struct Pointer)  (0) 2024.10.14
포인터(Pointer)  (0) 2024.10.13
구조체: struct  (0) 2024.09.22
열거형: enum  (0) 2024.09.22

+ Recent posts