Ron Weasley 2022. 8. 12. 21:07

배열

배열이라는 개념을 접하기 전에 지금까지 우리는 메모리에 저장 공간을 만들기 위해 변수라는 것을 사용했습니다. 예를 들어 사과 10개를 담기 위해 AppleBox라는 변수명으로 변수를 하나 만들어 줬습니다. 하지만 또 다른 예로 5과목의 점수를 처리하고 싶을 때는 kor, eng, math, sci, social 등 하나씩 다 변수를 지정해줬습니다.

 

이렇게 하나씩 변수를 선언하면 점수도 일일이 하나씩 넣어줘야 하는 번거로움이 있기 때문에 우리는 배열이라는 개념을 알고 있어야 합니다.

 

쉽게, 배열은 같은 형태의 많은 데이터를 반복하여 처리하기 위해서 메모리에 "연속적"으로 저장해놓고 쪼개서 사용하는 방법이라고 생각을 하시면 됩니다.

 

잘 이해가 안가시겠지만 예제로 보겠습니다.

%p 서식 지정자에 대해서는 포인터에 대해서 배울 때 언급을 하겠지만, 메모리 주소를 출력하는 서식 지정자입니다.

메모리 구조에 대해서 살짝 언급을 드리자면, main에서 선언을 한 지역변수 kor, eng, math는 스택이라는 메모리 공간에 들어가게됩니다. 지금 제가 출력한 0x16d6fb3d8 이라는 메모리 주소도 스택 주소의 일부분이고, 4바이트씩 내려가는 것을 볼 수 있는데 여기서 의문을 가지면 보통..데이터가 쌓이면 증가해야 되는것이 아닌가요? 할 수 있지만 스택은 메모리 구조에서 가장 높은 주소를 가지고 있기 때문에 위에서 아래로 내려가는 특징이 있습니다. 그에 비해 힙은 밑에서 위로 올라가는 특징이 있어 0x0 0x4 0x8 ~~~로 되는것입니다. 이 부분에 대해서는 나중에 배우도록 하겠습니다.

 

단순히, main 함수에서 선언한 지역 변수 kor, eng, math가 가지는 메모리 주소값이 출력되었구나! 라고만 이해하시면 됩니다.

 

잡담이 좀 길었네요..이처럼 kor, eng, math를 하나씩 선언해주는 것이 복잡하기 때문에 한번에 선언을 하기 위해서 배열을 배우는 것입니다. 바로 배열을 선언하는 방법을 보겠습니다.

 

배열의 선언

배열을 선언하는 방법은 같은 자료형을 가지는 변수들을 하나의 이름으로 한꺼번에 확보하기 위해서 사용합니다.

int형 요소 5개인 배열을 선언하는 방법은 다음과 같습니다.

int arr[5];

자료형 : int
배열명 : arr
배열의 요소 갯수 : 5

인용된 부분을 보면 아시겠지만 배열은 자료형, 배열명, 배열의 요소 갯수로 이루어져 있습니다.

백번의 말보다는 한번의 소스코드를 작성하는 것이 좋으니 5명의 나이를 저장할 배열을 선언하는 코드를 작성해보겠습니다.

먼저, 배열 선언문인 int old[5]를 보시면 int형 변수 5개를 하나씩 선언한 것과 전체 저장 공간의 크기는 같습니다. 하지만 메모리에 할당되는 방식에 차이가 있습니다. 보통 변수를 선언하면 각 변수는 독립적인 저장 공간을 갖고 각각의 이름을 사용합니다.

 

int a, int b, int c 요런식으로 말이죠!

반면 배열은 저장 공간이 연속으로 할당되며 배열명이 전체 공간의 이름이 됩니다.

기차를 떠오르시면 될 것 같네요.

이제 이 부분이 배열로 선언된 부분이며, int형 변수는 크기가 4바이트이므로 5개를 연속으로 할당하면 총 20바이트가 됩니다. 배열의 나누어진 조각을 배열 요소라고 칭하는데, 배열 요소는 다음과 같습니다.

old는 배열명, [1]은 첨자 입니다.

 

배열의 사용

배열을 선언할 때와 배열 요소를 사용할 때 대괄호[] 안의 숫자는 의미가 다릅니다. 선언을 할 때는 배열 요소의 전체 개수를 표시하며, 사용할 때는 각 요소가 배열에서 몇 번째 인덱스에 있는지를 의미합니다. 이 값들이 첨자(배열에서의 위치, 인덱스)가 되며, 배열의 첨자는 0부터 시작하므로 최대 "배열 요소 개수 - 1" 까지 사용이 가능합니다. 즉, 배열 요소의 개수가 5개면 첨자는 0 ~ 4 까지 입니다.

 

배열 초기화

배열도 변수와 마찬가지로 최초 할당된 저장 공간에는 쓰레기 값이 저장되어 있습니다. 그렇기 때문에 배열도 원하는 값을 가지려면 선언과 동시에 초기화를 해야 합니다. 초기화 하는 방법은 다음과 같습니다.

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

초깃값은 첫 번째 요소부터 차례로 초기화 됩니다. 그리고 char형 배열을 초기화 하는 방법을 보겠습니다.

char chr[10] = {'a', 'p', 'p', 'l', 'e'};

배열과 반복문

배열은 연속된 저장 공간을 할당하고 초기화할 수 있어 같은 유형의 변수가 많이 필요할 때 사용합니다. 연속된 배열 요소를 일일이 변수처럼 하나씩 떼어서 사용한다면, 이는 배열을 제대로 활용하고 있지 못한 것입니다. 그래서 반복문을 사용하는 것을 보겠습니다.

학교 성적을 한번에 입력받아서 출력하는 프로그램입니다.

이처럼 데이터를 일일이 하나씩 선언하지 않아도 배열을 반복하면서 인덱스(첨자)값만 바꿔주면 됩니다.

 

<확인문제>

1. 설명에 대한 배열을 선언해보세요

1. 정수 5개를 저장할 배열
답 : int array[5];

2. 실수 10개를 저장할 배열
답 : double array[10];

3. 배열 요소 개수가 3개인 int형 배열
답 : int array[3];

4. 첨자의 최대값이 4인 char형 배열
답 : char array[5];

2. 그림과 일치하도록 배열을 선언하여라.

그림은 따로 첨부를 하지 않았지만, [0] ~ [2] 인덱스는 1,2,3 으로 초기화를 시키고 [3] ~ [5] 인덱스는 0값이 들어있습니다.

답 : int array[6] = {1,2,3};

 

3. 다음과 같이 초기화된 A 배열의 값을 복사하여 B 배열을 채운 후 출력하는 프로그램을 작성해보세요.

A 배열의 값이 복사된 B 배열의 상태
1 2 3 1 2 3 1 2 3 1

문자를 저장하는 배열

우리는 문자를 저장하기 위해서 char형을 선언했습니다. 하지만 char형 배열로 문자열을 저장하기 위해서는 어떻게 해야할 지 알아보겠습니다. 일단은 문자열을 저장할려면 문자열의 길이보다 1이상 커야 된다고 말씀을 드렸습니다. 왜냐면 NULL 문자가 들어가야 하기 때문입니다. 간단한 코드를 보겠습니다.

str배열을 선언하면서 Applejam으로 초기화를 했습니다. 그리고 나서 입력한 문자열로 바뀌었습니다.

결국엔 문자 상수로 초기화 하는 방법을 위해서 배웠었는데 굳이 그럴 필요가 없다는 것입니다.

 

널 문자란?

이제는 널 문자에 대해서 알아야합니다. 초기화한 문자들은 배열의 처음부터 차례로 저장되어 문자열을 만들게 됩니다. 이때 남는 배열 요소에는 0이 채워집니다. 이렇게 char형 배열에 저장된 0을 특별히 "널 문자" 라고 칭하게 되는 것입니다. ( \0 )

 

문자열 대입

char형 배열이 문자열을 저장하는 변수의 역할을 하므로 초기화된 이후에도 얼마든지 새로운 문자열을 저장할 수 있습니다.

단, 문자열의 길이가 다를 수 있는 예외적인 상황이 발생하기 때문에 일반 변수처럼 대입 연산자(=)를 사용하는 것은 불가능 합니다. 이때 사용하는 함수로써 strcpy가 있습니다.

 

stycpy 함수는 char형 배열에 새로운 문자열을 복사(저장)하는 함수로 생각을 하면 되는데 저장할 문자열의 길이를 파악하여 딱 그 길이 만큼만 char형 배열에 복사합니다. 물론 여기서도 널 문자가 자동으로 붙혀집니다. 예제 코드를 보겠습니다.

일단, strcpy함수를 사용하기 위해서는 <string.h> 헤더 파일을 포함시켜야 합니다.

이 헤더 파일은 문자열을 다루는 함수들의 원형을 모아 놓은 것으로 strcpy 함수에 접근하기 위해 사용합니다.

strcpy 함수 원형은 다음과 같습니다.

strcpy(저장될 배열명, 저장할 문자열)

 

문자열 전용 입출력 함수 : gets, puts

우리가 보통 키보드로 문자열을 입력할 때 scanf 함수를 사용헀었습니다. 하지만 scanf는 char형 배열에 문자열을 입력할 수 있으나 중간에 빈칸이 있는 경우 빈칸 전까지만 입력합니다. 따라서 빈칸을 포함한 새로운 문자열 입력 방식이 필요합니다.

 

gets 함수는 빈칸을 포함하여 한 줄 전체를 문자열로 입력합니다. 또한 이 함수와 짝을 이루는 문자열 출력 함수인 puts도 같이 배워보겠습니다. 예제 코드를 보겠습니다.

gets(char형 배열명)
puts(char형 배열명)

scanf와 조금 다른점이 보이시나요? 바로 공백입니다. gets 함수는 문자열 중간에 빈칸이나 탭 문자를 사용할 수 있습니다. 그리고 엔터키를 누르기 전까지 전체를 하나의 문자열로 배열에 저장하게 됩니다. 하지만 gets함수는 단점이 있습니다. 바로 입력할 배열의 크기를 검사하지 않기 때문에 배열의 크기보다 긴 문자열을 입력하면 메모리 영역을 침범하여 버퍼오버플로를 발생시킬 수 있습니다. 물론 gets 뿐만 아니라 다른 함수들도 가능하기 때문에 무조건 메모리에 맡게 사용하는걸 권장드립니다.

 

puts 함수는 문자열 상수나 char형 배열의 배열명을 주면 문자열을 화면에 출력합니다.

 

<확인문제>

1. 다음 코드는 2개의 문자열을 입력받아 위치를 바꾼 후에 출력합니다. 빈칸을 채워 프로그램을 완성 시키세요.

 

<도전 실전 예제>

키보드로부터 문장을 입력받은 후에 대문자를 찾아 소문자로 바꾸는 프로그램을 작성하세요. 바뀐 문장과 바뀐 문자의 수도 함께 출력됩니다.

실행결과
문장 입력 : DON'T Worry, Be Happy~
바뀐 문장 : don't worry, be happy~
바뀐 문자 수 : 7

그리고 소문자를 대문자로 변형 시키는 것도 보겠습니다.