[KJ]1주차 개발일지 - 배열

jhj603·2025년 7월 14일
0

배열

배열은 가장 기본적이고 중요한 자료 구조 중 하나입니다.

배열 : 여러 개의 데이터를 하나의 변수 이름으로 묶어서 관리하는 자료구조

여러 개의 데이터를 하나의 변수 이름으로 묶어서 관리한다는 것이 어떤 의미인지 코드로 작성해보겠습니다.

# 학생 3명의 성적을 입력받아 출력하는 코드
score1 = int(input('1번 학생 성적 입력 : '))
score2 = int(input('2번 학생 성적 입력 : '))
score3 = int(input('3번 학생 성적 입력 : '))

print(f'1번 학생 성적 : {score1}')
print(f'2번 학생 성적 : {score2}')
print(f'3번 학생 성적 : {score3}')

# Output
1번 학생 성적 입력 : 30
2번 학생 성적 입력 : 50
3번 학생 성적 입력 : 70

1번 학생 성적 : 30
2번 학생 성적 : 50
3번 학생 성적 : 70

score1, score2, score3 모두 각 변수가 가리키는 데이터가 다를 수 있지만 학생의 성적을 나타내는 변수라는 공통점이 있습니다.

이러한 경우, 공통점이 있는 변수들을 하나의 변수로 묶고 관리와 사용을 편리하게 해주는 배열 자료구조를 사용하면 코드의 복잡성을 줄일 수 있을 것이라 생각합니다.

# 학생 3명의 성적을 배열에 입력받아 출력하는 코드
score = []

score.append(int(input('1번 학생 성적 입력 : ')))
score.append(int(input('2번 학생 성적 입력 : ')))
score.append(int(input('3번 학생 성적 입력 : ')))

print(f'1번 학생 성적 : {score[0]}')
print(f'2번 학생 성적 : {score[1]}')
print(f'3번 학생 성적 : {score[2]}')

# Output
1번 학생 성적 입력 : 30
2번 학생 성적 입력 : 50
3번 학생 성적 입력 : 70

1번 학생 성적 : 30
2번 학생 성적 : 50
3번 학생 성적 : 70

위의 코드와 동일하게 동작하지만 하나의 변수 안에 기존 3개의 변수를 전부 저장하고 사용할 수 있었습니다.

게다가 반복문과 배열을 같이 사용하면 여기서 더욱 간결한 코드를 작성할 수 있습니다.

# 학생 3명의 성적을 반복문으로 배열에 입력받아 출력하는 코드
score = []

for i in range(1, 4):
	score.append(int(input(f'{i}번 학생 성적 입력 : ')))

for i in range(3):
	print(f'{i + 1}번 학생 성적 : {score[i]}')

# Output
1번 학생 성적 입력 : 30
2번 학생 성적 입력 : 50
3번 학생 성적 입력 : 70

1번 학생 성적 : 30
2번 학생 성적 : 50
3번 학생 성적 : 70

배열의 개념과 특징

위에서 배열은 여러 개의 데이터하나의 변수 이름으로 묶어서 관리하는데 사용된다고 했습니다.

여기서 배열의 첫 번째 핵심 개념이 등장합니다.

1. 동일한 자료형

배열은 같은 종류(타입, 자료형)의 데이터만 저장할 수 있습니다.

예를 들면, 정수형(int, long 등) 배열은 정수형 데이터들만, 문자열(string) 배열은 문자열 데이터들만 저장할 수 있습니다.

어? python에서는 정수, 실수, 문자열, 심지어는 같은 list 등 구분없이 하나의 list에 저장할 수 있는데?

맞습니다. python에서는 배열 자료구조를 list와 tuple로 지원하는데, 이들은 저장 데이터들의 자료형에 관계없이 데이터를 저장할 수 있다는 특징이 있습니다.

그러나, 이는 python의 특징을 십 분 활용해서 가능한 것이지, 저장되는 데이터들의 자료형은 모두 같기 때문에 결국은 동일한 자료형의 데이터만 저장할 수 있다는 배열의 개념을 충족합니다.

- python의 변수는 값을 갖지 않는다.


여러 다른 프로그래밍 언어는 변수마다 고유의 저장 공간을 할당받아 데이터 값을 복사해 와 사용하는데 python은 다릅니다.

python은 값을 복사하는 것이 아닌 해당 데이터가 저장된 공간을 참조하는 식으로 데이터를 사용합니다.

python은 모든 데이터, 함수, 모듈, 패키지 등을 모두 하나의 객체로 취급하고 변수는 이 객체들의 주소를 참조하기 때문에 값을 갖지 않는다고 말하는 것입니다.

저는 이러한 동작이 가능한 이유로 python이 내부적으로 C언어로 구현되어 있다는 점으로 추측하고 있습니다.

C언어에는 주소를 저장할 수 있는 포인터 기능이 존재합니다.

포인터가 무엇인지 설명하기에는 안그래도 긴 포스팅이 더욱 길어질 것 같으니 간단하게 설명하고 넘어가겠습니다.

Pointer(포인터) : 공간을 가리키는 주소 값을 저장하는 변수

포인터를 통해 해당 변수가 가리키는 공간에 접근할 수 있고 그 공간에 저장된 데이터를 읽어올 수 있습니다.

이런 동작의 장점은 주소 값만 알고 있다면 어디서든 해당 데이터를 변경할 수 있고 python의 경우에는 저장된 주소 값만 변경해 원본 데이터의 변동 없이 가리키는 데이터를 변경할 수 있다는 점입니다.

python에서 N = 17을 수행하면 왼쪽 위 사진과 같이 17이란 객체를 N으로 참조하고 이 후 N += 1을 수행한다면 오른쪽 위 사진과 같이 객체 17을 가리키던 화살표가 객체 18을 가리켜 N에 18이란 데이터가 저장된 것과 같은 효과를 냅니다.

이제 다시 돌아가서, python에서 배열에 저장되는 데이터들은 자료형이 다른 것처럼 보여도 다른 데이터를 저장하고 있는 객체를 가리키는 주소 값들을 저장한다고 생각합니다.

이 주소 값들은 모두 자료형이 같기 때문에 보이는 것은 다양한 자료형의 데이터들이 모여있는 것처럼 보이지만 실제로 저장된 데이터들은 모두 동일한 자료형의 데이터들인 것이라 추측하고 있습니다.

2. 연속된 메모리 공간

배열에 저장되는 데이터들은 메모리 상에 연속적으로 할당됩니다.


위에서 나왔던 사진을 다시 가져왔습니다.

단순 변수로 선언한 경우, 위 사진은 나름대로 비슷한 위치에 각 변수들이 만들어져 있지만 실제로는 완전히 다른 위치에 각 변수들이 생성됩니다.

이렇게 되면 다음 변수에 접근하기 위해 다음 변수가 어디에 있는지에 대한 값을 알지 못하면 접근할 수 없게 됩니다.

배열은 하나의 변수를 통해 여러 데이터를 저장하기 위해 연속된 메모리 공간을 할당받습니다.

연속된 메모리 공간을 할당받으면 다음 데이터의 위치 값을 모르더라도 다음 위치에 대한 추론이 가능해집니다.

하지만, 이 특징 때문에 배열을 만들 때 항상 고정된 크기 값을 전달해줘야 한다는 단점이 있습니다.

어? python list는 크기를 전달 안해줘도 배열이 만들어지는데?

배열이 고정된 크기 값을 가져야 한다는 단점을 해소하기 위해 동적 배열 또는 가변 길이 배열이라는 자료 구조가 등장했습니다.

말 그대로 크기가 유동적인 배열을 뜻하는데 python의 list는 동적 배열로 만들어져 있기 때문에 크기를 전달하지 않아도 배열을 만들 수 있습니다.

동적 배열이 배열이 갖고 있는 단점을 해소해 무조건 좋은 것이라고 생각하실 수 있겠지만 이를 해소한 방법으로 인해 반드시 그런 것은 아닙니다.

바로 원하는 크기만큼의 배열 할당 후, 기존 배열의 데이터를 모두 복사해 옮기고 기존 배열의 할당을 반환하는 식으로 해소했습니다.

따라서 삽입과 삭제가 반복적으로 일어나면 메모리 할당과 해제가 계속 일어나 프로그램의 속도가 느려질 수 있습니다.

3. Index(인덱스)를 통한 임의 접근

배열의 각 데이터는 Index(인덱스)라고 불리는 고유 번호를 가지고 있고 이를 통해 저장된 데이터에 바로 접근할 수 있다.

저는 배열을 사용하는 가장 큰 이유로 Index를 꼽습니다.

위에서 연속된 메모리 공간에 데이터들을 저장한다고 적어놨는데, 그렇기 때문에 가능한 특징입니다.

연속된 메모리 공간에 데이터를 저장하면 다음 데이터의 주소가 어디일 지 추론이 가능해집니다.

만약 8byte 크기의 자료형 데이터 3개를 가진 리스트가 있다면 위 그림과 같이 배열에 저장될 것입니다.

여기서 배열의 첫 번째 공간의 크기는 8byte이기 때문에 다음 데이터는 처음 데이터가 저장된 주소에 8byte만큼을 더한 공간에 저장됩니다.

이처럼 연속된 메모리 공간에 저장된다는 특징 덕분에 우리는 배열의 시작 주소와 저장된 데이터의 자료형 크기, 몇 번째 데이터인지의 3가지 값만 알면 해당 데이터에 바로 접근이 가능합니다.

그리고 이 값들은 각각 배열의 이름, 자료형 크기, Index(인덱스)라고 불립니다.

원하는 데이터의 위치 = 배열의 시작 공간의 위치 + (자료형 크기 * 몇 번째 데이터인가?)
원하는 데이터의 위치 = 배열의 이름 + (자료형 크기 * index)

임의 접근으로 인해 배열은 삽입과 삭제는 느리지만 탐색에 있어서는 굉장히 빠른 모습을 보여줍니다.

배열의 장점

1. Index를 통한 임의 접근으로 인한 빠른 접근 속도
Index를 통해 배열의 어느 위치에 있는 데이터든 O(1)이라는 매우 빠른 속도로 데이터에 접근할 수 있습니다. 연속된 메모리 공간에 저장되어 있기 때문에 시작 공간으로부터 (Index * 데이터 크기)만큼을 더하면 위치가 특정되기 때문입니다.
2. 캐시 효율성
연속된 메모리 공간에 저장되기 때문에 캐시 메모리를 효율적으로 사용할 수 있고, 전체적인 프로그램 성능 향상에 기여합니다.
3. 간단한 구조
다른 자료 구조에 비해 매우 단순해 배우고 사용하기 쉽습니다.

배열에 장점만 있다면 좋겠지만 불행히도 배열에도 단점이 있습니다.

배열의 단점

1. 크기 고정
배열을 처음 선언할 때, 그 크기를 미리 정해야 합니다. 한 번 크기가 정해진 배열은 중간에 크기를 늘리거나 줄일 수 없습니다. 만일 데이터의 양이 가변적일 경우 배열은 비효율적일 수 있습니다.
2. 비효율적인 삽입, 삭제
크기 고정이라는 단점으로 인해 비롯된 단점이라고 생각되는 이 단점은 배열의 중간에 새로운 데이터를 삽입하거나 삭제할 경우에 발생합니다. 그 이후의 데이터들을 한 칸 앞이나 뒤로 이동시켜야 하는데 이 과정이 O(n)의 시간복잡도를 가지기 때문에 프로그램이 느려질 수 있습니다.

마무리

배열의 크기 고정이라는 단점을 해소하기 위해 리스트(python의 list 아님!)나 동적 배열 등의 자료 구조가 등장한 것으로 알고 있습니다.

하지만 리스트는 삽입과 삭제가 빠른 반면, Index를 통한 임의 접근이라는 장점을 포기했고, 동적 배열 또한 재할당 과정을 거치기 때문에 시간 소요라는 단점을 가지고 있습니다.

그럼에도 배열 자료 구조가 가져다 주는 장점들이 막강하다고 생각하기 때문에 저는 배열을 적극적으로 사용하고 있습니다.

대신 이러한 단점들을 충분히 고려해서 동적으로 데이터 삽입, 삭제가 최대한 이뤄지지 않도록 사용하는 것이 좋다고 생각합니다.

profile
자라나라 실력 실력

0개의 댓글