본문 바로가기
지식/자료구조

[자료구조] 배열(Arrays)

by 천무지 2024. 4. 7.
반응형

 

배열은 한 타입의 자료형을 연속적으로 할당하는 것을 말합니다.

 

만약 한 반이 30명인 학생들의 이름을 사용하고 싶다면 30개의 변수를 각각 할당하기 보단 배열을 이용하여 선언하는 것이 더욱 쉽고 효율적일 것입니다.

 

배열의 선언은 다음과 같이 할 수 있습니다.

int list[5] = {1,2,3,4,5};

 

int형 배열 list는 5개를 저장할 수 있는 배열로 선언되었습니다. 또한 각각의 요소에는 앞에서부터 1,2,3,4,5를 저장하도록 하였습니다.

 

여기서 각각의 값과 주소에 접근 하는 방법은 다음과 같습니다.

Variables Memory Address
list[0] base address = a
list[1] a + sizeof(int)
list[2] a + 2 * sizeof(int)
list[3] a + 3 * sizeof(int)
list[4] a + 4 * sizeof(int)

 

int형 배열이며 5개의 요소를 선언했기 때문에 메모리 상에서 int형의 크기만큼 연속적인 5개의 공간을 할당받습니다.

따라서 list 배열은  총 4 * 5 로 20byte를 할당받았습니다. 각각의 요소는 int형으로 4byte를 가지게됩니다.

 

 

 

배열은 포인터로도 해석될 수 있습니다.

int list2[5];
int *list1;

 

라고 선언하였다고 가정합니다.

 

그렇다면 list2는 list1과 같은 pointer integer 타입이라고 할 수 있습니다.

둘의 차이가 있다면 list2는 5개의 int형 만큼 공간을 할당받았다는 것과, list1을 배열처럼 사용하기 위해서는 메모리를 동적으로 할당받아야한다는 것입니다. 이를 동적할당이라고 하는데 뒤에서 다루겠습니다.

 

각설하여 list2를 포인터로 사용할 수 있다고 하였는데 다음 작성한 문장들이 어떠한 의미를 가지는지 알아보겠습니다.

list2 a pointer to list2[0] (list2[0]를 가리키는 포인터의 값                                                 =  list2[0]  주소)
list2 + i a pointer to list2[i] (list2[i]를 가리키는 포인터의 값                                                   = list2[i]  주소)
(list2 + i) is equal to &list2[i] (list2[i]의 주소)
*(list2 + i) is equal to list[2] (list2[i]에 저장된 값)

 

배열을 사용한 코드 예시를 한번 살펴보겠습니다.

#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

float sum(float[], int);
float input[MAX_SIZE], answer;
int i;

int main() {
    for(i=0; i<MAX_SIZE; i++)
        input[i] = i;
    answer = sum(input, MAX_SIZE);
    printf("The sum is: %f\n", answer);
    return 0;
}

float sum(float list[], int n) {
    int i;
    float tempsum = 0;
    for(i=0; i<n; i++)
        tempsum += list[i];
    return tempsum;
}

 

배열을 인수로 전달하는 점을 주의깊게 생각해보시길 바랍니다.

sum이라는 함수가 호출됩니다. 이때 input이 인수로 들어가있습니다. 즉,input[0]을 가리키는 포인터가 복사되어 인수로 전달됩니다. 복사된 값이라도 주소를 가리키는 포인터를 전달해주었기 sum 함수 내부에서 반복문을 통해 배열의 값에 접근할 수 있게 되었습니다.

 

두번째 예시를 보겠습니다.

#include <stdio.h>
#include <stdlib.h>

void print1(int*, int);

void main() {
    int one[] = {0, 1, 2, 3, 4};
    print1(one, 5);
}

void print1(int *ptr, int rows) {
    /* print out a one-dimensional array using a pointer */
    int i;
    printf("address contents\n");
    for(i=0; i<rows; i++)
        printf("%8p%5d\n", ptr+i, *(ptr+i));
}

 

첫번째 예시와 다른 점이 있다면 첫번째 예시에서는 함수를 선언할 때 float sum(float list[], int n) 처럼 배열을 인수로 했었다면 두번째 예시에서는  void print1(int *ptr, int rows) 처럼 포인터를 배열의 인수로 전달했다는 것입니다.

하지만 앞서 말씀을 드린것 처럼 배열은 포인터와 같은 타입으로 사용할 수 있다고 했었습니다. 따라서 첫번째 예시와 두번째 예시는 함수를 선언할 사용한 인수를 봤을 때 달라진 것은 거의 없습니다.

 

두번째 예시의 결과를 알아보겠습니다.

두번째 예시의 결과

현재 배열의 각 요소의 주소와 배열에 저장된 값이 출력된 것을 알 수 있습니다.

배열은 연속적인 공간을 할당받기에 현재 요소와 다음 요소의 주소의 차이는 자료형의 크기와 같습니다.

또한 주소는 16진수로 표현합니다 16진수의 순서는 다음과 같습니다.

0 1 2 3 4 5 6 7 8 9 A B C D E F

주소의 오른쪽에서 2자리만 본다면 아래로 내려올때 마다 배열의 자료형이 int형 이기 때문에 4씩 증가하는 것을 볼 수 있습니다. 

반응형